/** User image controls are execution constraints, separate from the planning model. */ export type AgentImageParameters = { aspectRatio?: string; imageSize?: string; imageCount?: number; n?: number; quality?: "auto" | "low" | "medium" | "high"; size?: string; negativePrompt?: string; promptExtend?: boolean; watermark?: boolean; seed?: number; }; export type AgentImagePreferences = { modelId?: string; parameters?: AgentImageParameters }; export function applyAgentImagePreferences>( input: T, preferences?: AgentImagePreferences, ): T & AgentImageParameters & { modelId?: string } { const parameters = Object.fromEntries(Object.entries(preferences?.parameters || {}) .filter(([, value]) => value !== undefined)); const count = preferences?.parameters?.imageCount ?? preferences?.parameters?.n; return { ...input, ...parameters, ...(count !== undefined ? { imageCount: count } : {}), ...(preferences?.modelId ? { modelId: preferences.modelId } : {}), }; } export function agentProviderImageParameters(parameters: AgentImageParameters) { return Object.fromEntries(["quality", "size", "watermark", "seed"].flatMap(key => { const value = parameters[key as keyof AgentImageParameters]; return value !== undefined ? [[key, value]] : []; })); } /** Only user controls or an explicit instruction pin a model; the planner's own * modelId and a preset default remain eligible for automatic recovery. */ export function resolveUserImageModel(text: string, entries: Array<{ model: { id: string; modelId: string; displayName?: string | null } }>, selectedId?: string) { if (selectedId) return selectedId; const names = entries.flatMap(({ model }) => [model.modelId, model.displayName].filter((name): name is string => Boolean(name && name.length >= 4)).map(name => ({ id: model.id, name }))) .sort((a, b) => b.name.length - a.name.length); const matches: Array<{ id: string; index: number; length: number }> = []; for (const { id, name } of names) { const escaped = name.split(/[\s_-]+/).map(part => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("[\\s_-]*"); const pattern = new RegExp(`(?:使用|选择|指定|通过|用)\\s*(?:模型\\s*)?${escaped}(?![a-z0-9.-])`, "ig"); for (const match of text.matchAll(pattern)) { if (!/(?:不要|不|别|禁止|无需)\s*$/.test(text.slice(Math.max(0, match.index! - 8), match.index!))) matches.push({ id, index: match.index!, length: name.length }); } } return matches.sort((a, b) => b.index - a.index || b.length - a.length)[0]?.id; }