fix(ai): preserve plugin scenes and sanitize OpenAI chat params#547
fix(ai): preserve plugin scenes and sanitize OpenAI chat params#547Vme500 wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the schema for the scenes field to default to an empty array instead of being optional, and introduces parameter sanitization and normalization in OpenaiService.createRawStream (specifically mapping max_tokens to max_completion_tokens for gpt-5 models). The reviewer identified that the sanitization logic is missing standard parameters like logprobs and top_logprobs, and that reasoning models (such as o1 and o3) should also undergo the same token normalization to prevent API errors. A code suggestion was provided to address these issues and improve TypeScript compliance.
| const allowedKeys = new Set([ | ||
| 'model', 'messages', 'stream', 'temperature', 'top_p', 'n', 'stop', | ||
| 'max_tokens', 'max_completion_tokens', 'presence_penalty', 'frequency_penalty', | ||
| 'logit_bias', 'user', 'tools', 'tool_choice', 'parallel_tool_calls', | ||
| 'response_format', 'seed', 'stream_options', 'reasoning_effort', | ||
| 'metadata', 'store', 'service_tier', 'prediction', 'modalities', 'audio', | ||
| ]) | ||
| const cleanOptions = Object.fromEntries( | ||
| Object.entries(options).filter(([key, value]) => allowedKeys.has(key) && value !== undefined), | ||
| ) | ||
|
|
||
| const model = String(cleanOptions.model || '') | ||
| if (model === 'gpt-5' || model.startsWith('gpt-5-')) { | ||
| if (cleanOptions.max_tokens !== undefined && cleanOptions.max_completion_tokens === undefined) { | ||
| cleanOptions.max_completion_tokens = cleanOptions.max_tokens | ||
| } | ||
| delete cleanOptions.max_tokens | ||
| } | ||
|
|
||
| return this.openAI.chat.completions.create(cleanOptions) |
There was a problem hiding this comment.
There are two main issues with the parameter sanitization and normalization logic here:
- Missing standard OpenAI parameters: The
allowedKeysset is missinglogprobsandtop_logprobs. If a client requests log probabilities, these parameters will be silently stripped, leading to unexpected behavior. - Incomplete reasoning model support: OpenAI's reasoning models (such as
o1,o1-mini,o1-preview, ando3-mini) also do not supportmax_tokensand requiremax_completion_tokens. Restricting this normalization to onlygpt-5will cause 400 Bad Request errors when usingo1oro3models withmax_tokens.
Additionally, casting cleanOptions to any during manipulation and then casting it back to OpenAI.Chat.ChatCompletionCreateParamsStreaming when calling .create() ensures strict TypeScript compliance without compiler errors when deleting properties.
const allowedKeys = new Set([
'model', 'messages', 'stream', 'temperature', 'top_p', 'n', 'stop',
'max_tokens', 'max_completion_tokens', 'presence_penalty', 'frequency_penalty',
'logit_bias', 'logprobs', 'top_logprobs', 'user', 'tools', 'tool_choice',
'parallel_tool_calls', 'response_format', 'seed', 'stream_options',
'reasoning_effort', 'metadata', 'store', 'service_tier', 'prediction',
'modalities', 'audio',
])
const cleanOptions = Object.fromEntries(
Object.entries(options).filter(([key, value]) => allowedKeys.has(key) && value !== undefined),
) as any
const model = String(cleanOptions.model || '')
const isReasoningOrGpt5 = model.startsWith('o1') || model.startsWith('o3') || model === 'gpt-5' || model.startsWith('gpt-5-')
if (isReasoningOrGpt5) {
if (cleanOptions.max_tokens !== undefined && cleanOptions.max_completion_tokens === undefined) {
cleanOptions.max_completion_tokens = cleanOptions.max_tokens
}
delete cleanOptions.max_tokens
}
return this.openAI.chat.completions.create(cleanOptions as OpenAI.Chat.ChatCompletionCreateParamsStreaming)There was a problem hiding this comment.
Thanks for the review. I updated the whitelist to include logprobs and top_logprobs, expanded token normalization to o-series reasoning models using /^o\d/, and added the TypeScript cast around the cleaned options.
Summary
This PR fixes self-hosted plugin chat issues related to model scene metadata and OpenAI chat parameter forwarding.
Changes included:
scenesas an array in AI model schemas.scenesas[]instead ofundefined.max_tokenstomax_completion_tokensfor GPT-5 /gpt-5-*models.Problem
In self-hosted local plugin mode, plugin chat requests may include internal fields such as:
sourcebillingGroupIduserIduserTypeThese fields are application-level metadata and should not be forwarded to OpenAI chat completion APIs. Forwarding them can produce
400 Unknown parametererrors.Additionally, when model
scenesis missing orundefined, plugin-side filtering such as:can fail or result in an empty plugin model list.
GPT-5-compatible models may also reject
max_tokensand expectmax_completion_tokensinstead.Solution
This PR makes the OpenAI provider boundary stricter:
gpt-5-*requests convertmax_tokenstomax_completion_tokens.scenesdefaults to an empty array in the model schemas.Scope
Changed files:
apps/aitoearn-ai/src/config.tsapps/aitoearn-ai/src/core/ai/libs/openai/openai.service.tslibs/aitoearn-ai-shared/src/vos/chat.vo.tsThis PR does not add a new provider and does not modify deployment-specific files.
Safety
This PR does not include:
.envLocal verification
Verified in a self-hosted local plugin deployment:
source/billingGroupIdunknown-parameter errors after sanitization.max_tokenscorrectly.