mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-01 21:20:44 +00:00
feat: add extra_body support for OpenAI-compatible providers
Add extra_body configuration option to model.generationConfig for passing custom parameters to OpenAI-compatible API request bodies. - Add extra_body to ContentGeneratorConfig type - Add extra_body to MODEL_GENERATION_CONFIG_FIELDS and ModelGenerationConfig - Implement extra_body merging in DefaultOpenAICompatibleProvider - Implement extra_body merging in DashScopeOpenAICompatibleProvider - Update documentation with examples and provider compatibility notes - Note: This feature is only for OpenAI-compatible providers (openai, qwen-oauth) Resolves #1647 Resolves #1644 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
561be0eb42
commit
532d97670b
8 changed files with 140 additions and 13 deletions
|
|
@ -93,6 +93,8 @@ export type ContentGeneratorConfig = {
|
|||
schemaCompliance?: 'auto' | 'openapi_30';
|
||||
// Custom HTTP headers to be sent with requests
|
||||
customHeaders?: Record<string, string>;
|
||||
// Extra body parameters to be merged into the request body
|
||||
extra_body?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
// Keep the public ContentGeneratorConfigSources API, but reuse the generic
|
||||
|
|
|
|||
|
|
@ -929,5 +929,71 @@ describe('DashScopeOpenAICompatibleProvider', () => {
|
|||
expect(result.max_tokens).toBe(65536); // Should be limited
|
||||
expect(result.stream).toBe(true); // Streaming should be preserved
|
||||
});
|
||||
|
||||
it('should merge extra_body into the request', () => {
|
||||
const providerWithExtraBody = new DashScopeOpenAICompatibleProvider(
|
||||
{
|
||||
...mockContentGeneratorConfig,
|
||||
extra_body: {
|
||||
custom_param: 'custom_value',
|
||||
nested: { key: 'value' },
|
||||
},
|
||||
},
|
||||
mockCliConfig,
|
||||
);
|
||||
|
||||
const request: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: 'qwen3-coder-plus',
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
};
|
||||
|
||||
const result = providerWithExtraBody.buildRequest(
|
||||
request,
|
||||
'test-prompt-id',
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((result as any).custom_param).toBe('custom_value');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((result as any).nested).toEqual({ key: 'value' });
|
||||
});
|
||||
|
||||
it('should merge extra_body into vision model requests', () => {
|
||||
const providerWithExtraBody = new DashScopeOpenAICompatibleProvider(
|
||||
{
|
||||
...mockContentGeneratorConfig,
|
||||
extra_body: {
|
||||
custom_param: 'custom_value',
|
||||
},
|
||||
},
|
||||
mockCliConfig,
|
||||
);
|
||||
|
||||
const request: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: 'qwen-vl-max',
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
};
|
||||
|
||||
const result = providerWithExtraBody.buildRequest(
|
||||
request,
|
||||
'test-prompt-id',
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((result as any).custom_param).toBe('custom_value');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((result as any).vl_high_resolution_images).toBe(true);
|
||||
});
|
||||
|
||||
it('should not include extra_body when not configured', () => {
|
||||
const request: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: 'qwen3-coder-plus',
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
};
|
||||
|
||||
const result = provider.buildRequest(request, 'test-prompt-id');
|
||||
|
||||
expect(result).not.toHaveProperty('custom_param');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ export class DashScopeOpenAICompatibleProvider
|
|||
request.model,
|
||||
);
|
||||
|
||||
const extraBody = this.contentGeneratorConfig.extra_body;
|
||||
|
||||
if (this.isVisionModel(request.model)) {
|
||||
return {
|
||||
...requestWithTokenLimits,
|
||||
|
|
@ -132,6 +134,7 @@ export class DashScopeOpenAICompatibleProvider
|
|||
...(this.buildMetadata(userPromptId) || {}),
|
||||
/* @ts-expect-error dashscope exclusive */
|
||||
vl_high_resolution_images: true,
|
||||
...(extraBody ? extraBody : {}),
|
||||
} as OpenAI.Chat.ChatCompletionCreateParams;
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +143,7 @@ export class DashScopeOpenAICompatibleProvider
|
|||
messages,
|
||||
...(tools ? { tools } : {}),
|
||||
...(this.buildMetadata(userPromptId) || {}),
|
||||
...(extraBody ? extraBody : {}),
|
||||
} as OpenAI.Chat.ChatCompletionCreateParams;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,5 +261,48 @@ describe('DefaultOpenAICompatibleProvider', () => {
|
|||
// Result should be a different object
|
||||
expect(result).not.toBe(originalRequest);
|
||||
});
|
||||
|
||||
it('should merge extra_body into the request', () => {
|
||||
const providerWithExtraBody = new DefaultOpenAICompatibleProvider(
|
||||
{
|
||||
...mockContentGeneratorConfig,
|
||||
extra_body: {
|
||||
custom_param: 'custom_value',
|
||||
nested: { key: 'value' },
|
||||
},
|
||||
} as ContentGeneratorConfig,
|
||||
mockCliConfig,
|
||||
);
|
||||
|
||||
const originalRequest: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: 'gpt-4',
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
temperature: 0.7,
|
||||
};
|
||||
|
||||
const result = providerWithExtraBody.buildRequest(
|
||||
originalRequest,
|
||||
'prompt-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
...originalRequest,
|
||||
custom_param: 'custom_value',
|
||||
nested: { key: 'value' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include extra_body when not configured', () => {
|
||||
const originalRequest: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: 'gpt-4',
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
temperature: 0.7,
|
||||
};
|
||||
|
||||
const result = provider.buildRequest(originalRequest, 'prompt-id');
|
||||
|
||||
expect(result).toEqual(originalRequest);
|
||||
expect(result).not.toHaveProperty('custom_param');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -64,9 +64,11 @@ export class DefaultOpenAICompatibleProvider
|
|||
request: OpenAI.Chat.ChatCompletionCreateParams,
|
||||
_userPromptId: string,
|
||||
): OpenAI.Chat.ChatCompletionCreateParams {
|
||||
const extraBody = this.contentGeneratorConfig.extra_body;
|
||||
// Default provider doesn't need special enhancements, just pass through all parameters
|
||||
return {
|
||||
...request, // Preserve all original parameters including sampling params
|
||||
...(extraBody ? extraBody : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export const MODEL_GENERATION_CONFIG_FIELDS = [
|
|||
'schemaCompliance',
|
||||
'reasoning',
|
||||
'customHeaders',
|
||||
'extra_body',
|
||||
] as const satisfies ReadonlyArray<keyof ContentGeneratorConfig>;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export type ModelGenerationConfig = Pick<
|
|||
| 'schemaCompliance'
|
||||
| 'reasoning'
|
||||
| 'customHeaders'
|
||||
| 'extra_body'
|
||||
>;
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue