feat: add Responses API compatibility

This commit is contained in:
2026-07-10 23:33:15 +08:00
parent b7351f3b9b
commit 8847d973a8
24 changed files with 5922 additions and 169 deletions
@@ -33,6 +33,8 @@ type FieldDefinition = {
placeholder?: string;
type: FieldType;
scope?: FieldScope;
fixedOptions?: ValueOption[];
allowCustom?: boolean;
};
type ValueOption = { label: string; value: string };
@@ -50,6 +52,19 @@ const textFields: FieldDefinition[] = [
{ key: 'thinkingEffortLevels', label: '推理深度', hint: '声明模型支持的 OpenAI reasoning_effort 取值,平台差异由网关适配', placeholder: 'none, minimal, low, medium, high, xhigh', type: 'list' },
];
const apiProtocolField: FieldDefinition = {
key: 'supportedApiProtocols',
label: '支持的接口规范',
hint: '字段缺失时按 OpenAI Chat 处理;Responses 入口会优先选择原生 Responses 候选',
type: 'list',
allowCustom: false,
fixedOptions: [
{ value: 'openai_chat_completions', label: 'OpenAI Chat' },
{ value: 'openai_responses', label: 'OpenAI Responses' },
{ value: 'anthropic_messages', label: 'Anthropic' },
],
};
const embeddingFields: FieldDefinition[] = [
{ key: 'dimensions', label: '向量维度', placeholder: '1024, 768, 512', type: 'numberList' },
];
@@ -481,7 +496,7 @@ function MultiValueControl(props: {
</label>
))}
</div>
<div className="capabilityCustomValue">
{props.field.allowCustom !== false ? <div className="capabilityCustomValue">
<Input
size="sm"
value={customValue}
@@ -495,7 +510,7 @@ function MultiValueControl(props: {
}}
/>
<Button className="capabilityAddValueButton" type="button" size="sm" variant="outline" onClick={addCustom}></Button>
</div>
</div> : null}
</div>
);
}
@@ -605,6 +620,7 @@ function defaultDirectValueForConfigKey(key: string) {
}
function multiOptionsForField(field: FieldDefinition, config: Record<string, unknown>, value: unknown): ValueOption[] {
if (field.fixedOptions) return field.fixedOptions;
let options: string[] = [];
if (field.key === 'output_resolutions') options = field.scope ? videoResolutionOptions : imageResolutionOptions;
if (field.key === 'aspect_ratio_allowed') options = field.scope ? videoAspectRatioOptions : imageAspectRatioOptions;
@@ -699,7 +715,7 @@ function hasMeaningfulValue(value: unknown): boolean {
}
function capabilityFieldGroups(type: string): Array<{ title: string; icon: ReactNode; fields: FieldDefinition[] }> {
if (isTextType(type)) return [{ title: '文本默认能力', icon: <Brain size={15} />, fields: textFields }];
if (isTextType(type)) return [{ title: '文本默认能力', icon: <Brain size={15} />, fields: type === 'text_generate' ? [apiProtocolField, ...textFields] : textFields }];
if (type === 'text_embedding') return [{ title: '向量默认能力', icon: <Brain size={15} />, fields: embeddingFields }];
if (isImageType(type)) return [{ title: '图像默认能力', icon: <Image size={15} />, fields: imageFieldsFor(type) }];
if (isVideoType(type)) return [{ title: '视频默认能力', icon: <Video size={15} />, fields: videoFieldsFor(type) }];
@@ -736,6 +752,13 @@ function summarizeEnabledCapabilities(types: string[], typeConfigs: Record<strin
function capabilitySummaryHighlights(types: string[], typeConfigs: Record<string, Record<string, unknown>>) {
const highlights: string[] = [];
const protocolLabels: Record<string, string> = {
openai_chat_completions: 'Chat',
openai_responses: 'Responses',
anthropic_messages: 'Anthropic',
};
const protocols = uniqueStrings(types.flatMap((type) => flattenStringValues(typeConfigs[type]?.supportedApiProtocols)));
if (protocols.length) highlights.push(`接口 ${protocols.map((item) => protocolLabels[item] ?? item).join('/')}`);
const textLimits = uniqueStrings(types.flatMap((type) => stringValue(typeConfigs[type]?.max_context_tokens ? typeConfigs[type].max_context_tokens : '').split(',').filter(Boolean)));
if (textLimits.length) highlights.push(`上下文 ${textLimits[0]} Token`);
@@ -126,6 +126,7 @@ const managedRootKeys = new Set<string>([
]);
const managedNestedKeys = new Set<string>([
'supportedApiProtocols',
'supportTool',
'supportThinking',
'supportThinkingModeSwitch',
@@ -275,6 +276,7 @@ export function capabilitiesFromForm(state: CapabilityEditorState): Record<strin
export function defaultCapabilityConfig(type: string): Record<string, unknown> {
if (isTextLike(type)) {
return {
...(type === 'text_generate' ? { supportedApiProtocols: ['openai_chat_completions'] } : {}),
supportTool: type === 'tools_call',
supportThinking: false,
supportThinkingModeSwitch: false,
@@ -398,7 +400,7 @@ function shouldKeepNestedValue(key: string, value: unknown) {
if (key === 'output_resolutions' || key === 'aspect_ratio_allowed' || key === 'aspect_ratio_range' || key === 'input_aspect_ratio_range' || key === 'duration_range') {
return !Array.isArray(value);
}
if (key === 'thinkingEffortLevels' || key === 'dimensions') {
if (key === 'thinkingEffortLevels' || key === 'dimensions' || key === 'supportedApiProtocols') {
return !Array.isArray(value);
}
if (key.endsWith('_count') || key === 'output_max_size' || key.endsWith('_tokens')) {