( body: ChatCompletionRequest, accessToken: string, )
| 426 | } |
| 427 | |
| 428 | function handleChatCompletion( |
| 429 | body: ChatCompletionRequest, |
| 430 | accessToken: string, |
| 431 | ): Response | Promise<Response> { |
| 432 | const { systemPrompt, userText, turns, toolResults } = parseMessages(body.messages); |
| 433 | const modelId = body.model; |
| 434 | const tools = body.tools ?? []; |
| 435 | |
| 436 | if (!userText && toolResults.length === 0) { |
| 437 | return new Response( |
| 438 | JSON.stringify({ |
| 439 | error: { |
| 440 | message: "No user message found", |
| 441 | type: "invalid_request_error", |
| 442 | }, |
| 443 | }), |
| 444 | { status: 400, headers: { "Content-Type": "application/json" } }, |
| 445 | ); |
| 446 | } |
| 447 | |
| 448 | // bridgeKey: model-specific, for active tool-call bridges |
| 449 | // convKey: model-independent, for conversation state that survives model switches |
| 450 | const bridgeKey = deriveBridgeKey(modelId, body.messages); |
| 451 | const convKey = deriveConversationKey(body.messages); |
| 452 | const activeBridge = activeBridges.get(bridgeKey); |
| 453 | |
| 454 | if (activeBridge && toolResults.length > 0) { |
| 455 | activeBridges.delete(bridgeKey); |
| 456 | |
| 457 | if (activeBridge.bridge.alive) { |
| 458 | // Resume the live bridge with tool results |
| 459 | return handleToolResultResume(activeBridge, toolResults, modelId, bridgeKey, convKey); |
| 460 | } |
| 461 | |
| 462 | // Bridge died (timeout, server disconnect, etc.). |
| 463 | // Clean up and fall through to start a fresh bridge. |
| 464 | clearInterval(activeBridge.heartbeatTimer); |
| 465 | activeBridge.bridge.end(); |
| 466 | } |
| 467 | |
| 468 | // Clean up stale bridge if present |
| 469 | if (activeBridge && activeBridges.has(bridgeKey)) { |
| 470 | clearInterval(activeBridge.heartbeatTimer); |
| 471 | activeBridge.bridge.end(); |
| 472 | activeBridges.delete(bridgeKey); |
| 473 | } |
| 474 | |
| 475 | let stored = conversationStates.get(convKey); |
| 476 | if (!stored) { |
| 477 | stored = { |
| 478 | conversationId: deterministicConversationId(convKey), |
| 479 | checkpoint: null, |
| 480 | blobStore: new Map(), |
| 481 | lastAccessMs: Date.now(), |
| 482 | }; |
| 483 | conversationStates.set(convKey, stored); |
| 484 | } |
| 485 | stored.lastAccessMs = Date.now(); |
no test coverage detected