import type { JSONObject, LanguageModelV3CallOptions, LanguageModelV3Middleware } from '@ai-sdk/provider'; const isNonEmptyString = (value: unknown): value is string => typeof value === 'string' && value.trim().length > 0; const getSystemInstructions = (prompt: LanguageModelV3CallOptions['prompt']): string | undefined => { const instructions = prompt .filter((message) => message.role === 'system') .map((message) => message.content) .filter(isNonEmptyString); return instructions.length > 0 ? instructions.join('\n') : undefined; }; const hasUnsupportedChatGptCacheRetention = (options: JSONObject): boolean => { return options.promptCacheRetention !== undefined || options.prompt_cache_retention !== undefined; }; /** * ChatGPT's Codex backend requires top-level Responses API instructions. * The standard OpenAI provider otherwise serializes system prompts as input items. */ export function createOpenAiChatGptInstructionsMiddleware(): LanguageModelV3Middleware { return { specificationVersion: 'v3', transformParams: async ({ params }) => { const instructions = getSystemInstructions(params.prompt); const providerOptions = params.providerOptions ?? {}; const openAiProviderOptions = providerOptions.openai ?? {}; const shouldApplyInstructions = !!instructions && !isNonEmptyString(openAiProviderOptions.instructions); const shouldStripCacheRetention = hasUnsupportedChatGptCacheRetention(openAiProviderOptions); if (!shouldApplyInstructions && !shouldStripCacheRetention) { return params; } const nextOpenAiProviderOptions: JSONObject = { ...openAiProviderOptions }; delete nextOpenAiProviderOptions.promptCacheRetention; delete nextOpenAiProviderOptions.prompt_cache_retention; if (shouldApplyInstructions) { nextOpenAiProviderOptions.instructions = instructions; } return { ...params, prompt: shouldApplyInstructions ? params.prompt.filter((message) => message.role !== 'system') : params.prompt, providerOptions: { ...providerOptions, openai: nextOpenAiProviderOptions, }, } satisfies LanguageModelV3CallOptions; }, }; }