import type { TeacherModelCatalog } from '../../shared/coding-teacher'; import { normalizeManagedModelCatalog } from '../../shared/managed-model-capabilities'; import { teacherCloudRequest, assertTeacherAccount, TeacherError, type TeacherAccount } from './config-client'; import { teacherCloudTransport } from './cloud-runner'; export function validateTeacherModelId(value: unknown): string | undefined { if (value === undefined) return undefined; if (typeof value !== 'string' || !value.trim() || value.length > 200 || value !== value.trim()) throw new TeacherError(422, 'teacher_model_invalid', '请选择有效的模型。'); return value; } /** Main strips credentials; each selected model is revalidated when the question is accepted. */ export async function teacherModelCatalog(account: TeacherAccount, runtime: 'local' | 'yuxi'): Promise { if (runtime === 'yuxi') { let result; try { result = await teacherCloudTransport(account).json('/models'); } catch (error) { if (error instanceof TeacherError && error.status === 404) return { supported: false, models: [], message: '老师服务更新后即可切换模型,目前使用老师配置。', }; throw error; } assertTeacherAccount(account); if (result.model_selection === false) return { supported: false, models: [], message: '老师服务更新后即可切换模型,目前使用老师配置。' }; if (result.model_selection !== true || !Array.isArray(result.models) || result.models.some(item => !item || typeof item !== 'object' || typeof item.id !== 'string' || !item.id || item.id.length > 200)) throw new TeacherError(502, 'teacher_models_invalid', '模型列表暂不可用,请重试。'); const local = await teacherModelCatalog(account, 'local'); const textModels = new Set(local.models.map(item => item.id)); return { supported: true, models: result.models.filter(item => textModels.has(item.id)).map(item => ({ id: item.id as string })) }; } const config = await teacherCloudRequest<{ models: string[]; model_capabilities_v2: unknown }>(account, '/api/auth/me/model-config'); const catalog = normalizeManagedModelCatalog(config.model_capabilities_v2, config.models); assertTeacherAccount(account); return { supported: true, models: config.models.filter(id => catalog?.models[id]?.inputModalities?.includes('text') && catalog.models[id]?.outputModalities?.includes('text')).map(id => ({ id })) }; }