feat: use managed model capabilities for reasoning and image input

This commit is contained in:
2026-09-12 21:37:18 +08:00
parent e7701d1f2c
commit 26cbb29aa9
29 changed files with 652 additions and 271 deletions

View File

@@ -1,4 +1,5 @@
import type { IncomingMessage, ServerResponse } from 'node:http';
import { parseManagedReasoningChoice } from '../../../shared/managed-model-capabilities';
import type { ConversationInteractionResponse } from '../../coding-runtime/contracts';
import { CodingConversationServiceError } from '../../coding-runtime/conversation-service';
import { normalizeProductModelRef } from '../../coding-projects/project-config';
@@ -182,12 +183,15 @@ export async function handleCodingConversationRoutes(
return true;
}
if (action === 'thinking' && req.method === 'POST') {
const body = await parseJsonBody<{ thinkingLevel?: unknown }>(req);
const body = await parseJsonBody<{ thinkingLevel?: unknown; reasoningChoice?: unknown }>(req);
const reasoningChoice = body.reasoningChoice === undefined ? undefined : parseManagedReasoningChoice(body.reasoningChoice);
if (reasoningChoice === null) invalidRequest('Reasoning choice is invalid');
if (!THINKING_LEVELS.has(String(body.thinkingLevel))) invalidRequest('Thinking level is invalid');
sendJson(res, 200, {
model: await service.setThinking(
conversationId,
body.thinkingLevel as 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'max',
reasoningChoice ?? undefined,
),
});
return true;

View File

@@ -136,7 +136,10 @@ function normalizeWorksSquareAiGatewayBaseUrl(baseUrl: string, credentialMode: s
return normalized;
}
import { normalizeManagedModelCatalog } from '../../../shared/managed-model-capabilities';
type ImportedUserModelConfig = {
modelCapabilitiesV2?: import('../../../shared/managed-model-capabilities').ManagedModelCatalog;
label: string;
baseUrl: string;
apiKey: string;
@@ -176,7 +179,10 @@ export function normalizeImportedUserModelConfig(payload: unknown): ImportedUser
const apiKeyExpiresIn = typeof apiKeyExpiresInRaw === 'number' && Number.isFinite(apiKeyExpiresInRaw)
? Math.max(0, Math.floor(apiKeyExpiresInRaw))
: null;
const models = normalizeImportedModels(record.models);
const v2 = normalizeManagedModelCatalog(record.model_capabilities_v2);
const models = v2 && Array.isArray(record.models)
? [...new Set(record.models.filter((id): id is string => typeof id === 'string' && !!id.trim()).map(id => id.trim()))]
: normalizeImportedModels(record.models);
if (models.length === 0) {
throw new Error('Works Square model config response has no models');
}
@@ -185,6 +191,7 @@ export function normalizeImportedUserModelConfig(payload: unknown): ImportedUser
? record.label.trim()
: NIANCODE_USER_MODEL_ACCOUNT_LABEL;
const modelCapabilities = normalizeImportedModelCapabilities(record.model_capabilities, models);
const modelCapabilitiesV2 = normalizeManagedModelCatalog(record.model_capabilities_v2, models);
return {
label,
@@ -194,6 +201,7 @@ export function normalizeImportedUserModelConfig(payload: unknown): ImportedUser
apiKeyExpiresIn,
models,
...(modelCapabilities ? { modelCapabilities } : {}),
...(modelCapabilitiesV2 ? { modelCapabilitiesV2 } : {}),
};
}
@@ -217,9 +225,11 @@ function importedUserModelMetadata(
const metadata = { ...(existing?.metadata ?? {}) };
delete metadata.worksSquareCredentialExpiresAt;
delete metadata.worksSquareModelCapabilities;
delete metadata.worksSquareModelCapabilitiesV2;
return {
...metadata,
customModels: modelConfig.models,
...(modelConfig.modelCapabilitiesV2 ? { worksSquareModelCapabilitiesV2: modelConfig.modelCapabilitiesV2 } : {}),
...(modelConfig.modelCapabilities
? { worksSquareModelCapabilities: modelConfig.modelCapabilities }
: {}),
@@ -232,12 +242,14 @@ function importedUserModelMetadata(
delete metadata.worksSquareCredentialExpiresAt;
delete metadata.worksSquareOneApiBaseUrl;
delete metadata.worksSquareModelCapabilities;
delete metadata.worksSquareModelCapabilitiesV2;
const credentialExpiresAt = modelConfig.apiKeyExpiresIn === null
? undefined
: new Date(nowMs + modelConfig.apiKeyExpiresIn * 1000).toISOString();
return {
...metadata,
customModels: modelConfig.models,
...(modelConfig.modelCapabilitiesV2 ? { worksSquareModelCapabilitiesV2: modelConfig.modelCapabilitiesV2 } : {}),
...(modelConfig.modelCapabilities
? { worksSquareModelCapabilities: modelConfig.modelCapabilities }
: {}),
@@ -309,6 +321,7 @@ function providerAccountRuntimeShape(account: ProviderAccount): unknown {
metadata: {
customModels: account.metadata?.customModels,
worksSquareModelCapabilities: account.metadata?.worksSquareModelCapabilities,
worksSquareModelCapabilitiesV2: account.metadata?.worksSquareModelCapabilitiesV2,
worksSquareCredentialMode: account.metadata?.worksSquareCredentialMode,
worksSquareOneApiBaseUrl: account.metadata?.worksSquareOneApiBaseUrl,
},