( systemPrompt: string, messages: Anthropic.Messages.MessageParam[], metadata?: ApiHandlerCreateMessageMetadata, )
| 175 | } |
| 176 | |
| 177 | override async *createMessage( |
| 178 | systemPrompt: string, |
| 179 | messages: Anthropic.Messages.MessageParam[], |
| 180 | metadata?: ApiHandlerCreateMessageMetadata, |
| 181 | ): ApiStream { |
| 182 | this.ensureAuthenticated() |
| 183 | |
| 184 | const { id: modelId, info } = await this.fetchModel() |
| 185 | |
| 186 | const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ |
| 187 | { role: "system", content: systemPrompt }, |
| 188 | ...convertToOpenAiMessages(messages), |
| 189 | ] |
| 190 | |
| 191 | // Apply prompt caching for models that support it |
| 192 | // Zoo Gateway serves the same models as Vercel AI Gateway, so caching support is identical |
| 193 | if (VERCEL_AI_GATEWAY_PROMPT_CACHING_MODELS.has(modelId) && info.supportsPromptCache) { |
| 194 | addCacheBreakpoints(systemPrompt, openAiMessages) |
| 195 | } |
| 196 | |
| 197 | // Build request headers with enrichment metadata |
| 198 | const requestHeaders: Record<string, string> = {} |
| 199 | if (metadata?.taskId) { |
| 200 | requestHeaders["X-Zoo-Task-ID"] = metadata.taskId |
| 201 | } |
| 202 | if (metadata?.mode) { |
| 203 | requestHeaders["X-Zoo-Mode"] = metadata.mode |
| 204 | } |
| 205 | |
| 206 | const body: OpenAI.Chat.ChatCompletionCreateParams = { |
| 207 | model: modelId, |
| 208 | messages: openAiMessages, |
| 209 | temperature: this.supportsTemperature(modelId) |
| 210 | ? (this.options.modelTemperature ?? ZOO_GATEWAY_DEFAULT_TEMPERATURE) |
| 211 | : undefined, |
| 212 | max_completion_tokens: info.maxTokens, |
| 213 | stream: true, |
| 214 | stream_options: { include_usage: true }, |
| 215 | tools: this.convertToolsForOpenAI(metadata?.tools), |
| 216 | tool_choice: metadata?.tool_choice, |
| 217 | parallel_tool_calls: metadata?.parallelToolCalls ?? true, |
| 218 | } |
| 219 | |
| 220 | try { |
| 221 | const completion = await this.client.chat.completions.create(body, { |
| 222 | headers: requestHeaders, |
| 223 | }) |
| 224 | |
| 225 | for await (const chunk of completion) { |
| 226 | // Once the gateway starts streaming the HTTP status is already 200, so it |
| 227 | // reports upstream failures (e.g. provider rate limits) as an in-band error |
| 228 | // chunk. Surface it so the user sees the real reason instead of an empty reply. |
| 229 | if ("error" in chunk && chunk.error) { |
| 230 | throw toGatewayStreamError(chunk.error) |
| 231 | } |
| 232 | |
| 233 | const delta = chunk.choices[0]?.delta |
| 234 | if (delta?.content) { |
nothing calls this directly
no test coverage detected