( systemPrompt: string, messages: Anthropic.Messages.MessageParam[], metadata?: ApiHandlerCreateMessageMetadata, )
| 212 | } |
| 213 | |
| 214 | override async *createMessage( |
| 215 | systemPrompt: string, |
| 216 | messages: Anthropic.Messages.MessageParam[], |
| 217 | metadata?: ApiHandlerCreateMessageMetadata, |
| 218 | ): ApiStream { |
| 219 | await this.ensureAuthenticated() |
| 220 | const client = this.ensureClient() |
| 221 | const model = this.getModel() |
| 222 | |
| 223 | const systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = { |
| 224 | role: "system", |
| 225 | content: systemPrompt, |
| 226 | } |
| 227 | |
| 228 | const convertedMessages = [systemMessage, ...convertToOpenAiMessages(messages)] |
| 229 | |
| 230 | const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { |
| 231 | model: model.id, |
| 232 | temperature: 0, |
| 233 | messages: convertedMessages, |
| 234 | stream: true, |
| 235 | stream_options: { include_usage: true }, |
| 236 | max_completion_tokens: model.info.maxTokens, |
| 237 | tools: this.convertToolsForOpenAI(metadata?.tools), |
| 238 | tool_choice: metadata?.tool_choice, |
| 239 | parallel_tool_calls: metadata?.parallelToolCalls ?? true, |
| 240 | } |
| 241 | |
| 242 | const stream = await this.callApiWithRetry(() => client.chat.completions.create(requestOptions)) |
| 243 | |
| 244 | let fullContent = "" |
| 245 | |
| 246 | for await (const apiChunk of stream) { |
| 247 | const delta = apiChunk.choices[0]?.delta ?? {} |
| 248 | const finishReason = apiChunk.choices[0]?.finish_reason |
| 249 | |
| 250 | if (delta.content) { |
| 251 | let newText = delta.content |
| 252 | if (newText.startsWith(fullContent)) { |
| 253 | newText = newText.substring(fullContent.length) |
| 254 | } |
| 255 | fullContent = delta.content |
| 256 | |
| 257 | if (newText) { |
| 258 | // Check for thinking blocks |
| 259 | if (newText.includes("<think>") || newText.includes("</think>")) { |
| 260 | // Simple parsing for thinking blocks |
| 261 | const parts = newText.split(/<\/?think>/g) |
| 262 | for (let i = 0; i < parts.length; i++) { |
| 263 | if (parts[i]) { |
| 264 | if (i % 2 === 0) { |
| 265 | // Outside thinking block |
| 266 | yield { |
| 267 | type: "text", |
| 268 | text: parts[i], |
| 269 | } |
| 270 | } else { |
| 271 | // Inside thinking block |
nothing calls this directly
no test coverage detected