Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/lib/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,22 @@ export async function forwardError(c: Context, error: unknown) {
consola.error("Error occurred:", error)

if (error instanceof HTTPError) {
consola.error("HTTP error:", await error.response.json())
const errorText = await error.response.text()
const cloned = error.response.clone()
let errorText = ""
let errorJson = null
try {
errorJson = await cloned.json()
consola.error("HTTP error:", errorJson)
errorText = typeof errorJson === 'string' ? errorJson : JSON.stringify(errorJson)
} catch {
try {
errorText = await cloned.text()
consola.error("HTTP error text:", errorText)
} catch {
errorText = "Failed to read error response"
consola.error("Failed to read error response body")
}
}
return c.json(
{
error: {
Expand Down
92 changes: 86 additions & 6 deletions src/routes/messages/non-stream-translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,78 @@ import {
} from "./anthropic-types"
import { mapOpenAIStopReasonToAnthropic } from "./utils"

// Helper function to validate message sequences
function validateMessageSequence(messages: Array<Message>): void {
for (let i = 0; i < messages.length; i++) {
const message = messages[i]
if (message.role === "assistant" && message.tool_calls && message.tool_calls.length > 0) {
// Check if the next messages are tool responses
const toolCallIds = new Set(message.tool_calls.map(tc => tc.id))
let j = i + 1
const foundToolResponses = new Set<string>()

while (j < messages.length && messages[j].role === "tool") {
const toolMessage = messages[j]
if ("tool_call_id" in toolMessage && toolMessage.tool_call_id) {
foundToolResponses.add(toolMessage.tool_call_id)
}
j++
}

// Check if all tool calls have responses
for (const toolCallId of toolCallIds) {
if (!foundToolResponses.has(toolCallId)) {
throw new Error(`Tool call ${toolCallId} is missing a response message`)
}
}
}
}
}

// Helper function to fix message sequences by adding missing tool responses
function fixMessageSequence(messages: Array<Message>): Array<Message> {
const fixedMessages: Array<Message> = []

for (let i = 0; i < messages.length; i++) {
const message = messages[i]
fixedMessages.push(message)

if (message.role === "assistant" && message.tool_calls && message.tool_calls.length > 0) {
// Find which tool calls need responses
const foundToolResponses = new Set<string>()

// Look ahead to see what tool responses exist
let j = i + 1
while (j < messages.length && messages[j].role === "tool") {
const toolMessage = messages[j]
if ("tool_call_id" in toolMessage && toolMessage.tool_call_id) {
foundToolResponses.add(toolMessage.tool_call_id)
}
j++
}

// Add placeholder responses for missing tool calls
for (const toolCall of message.tool_calls) {
if (!foundToolResponses.has(toolCall.id)) {
fixedMessages.push({
role: "tool",
tool_call_id: toolCall.id,
content: "Tool execution completed.",
})
}
}
}
}

return fixedMessages
}

// Payload translation

export function translateToOpenAI(
payload: AnthropicMessagesPayload,
): ChatCompletionsPayload {
return {
const translated = {
model: payload.model,
messages: translateAnthropicMessagesToOpenAI(
payload.messages,
Expand All @@ -43,6 +109,14 @@ export function translateToOpenAI(
tools: translateAnthropicToolsToOpenAI(payload.tools),
tool_choice: translateAnthropicToolChoiceToOpenAI(payload.tool_choice),
}

// Debug: log the messages before validation
// console.log("DEBUG: Translated messages:", JSON.stringify(translated.messages, null, 2))

// Validate and fix the message sequence to ensure tool calls have responses
translated.messages = fixMessageSequence(translated.messages)

return translated
}

function translateAnthropicMessagesToOpenAI(
Expand All @@ -51,11 +125,17 @@ function translateAnthropicMessagesToOpenAI(
): Array<Message> {
const systemMessages = handleSystemPrompt(system)

const otherMessages = anthropicMessages.flatMap((message) =>
message.role === "user" ?
handleUserMessage(message)
: handleAssistantMessage(message),
)
const otherMessages: Array<Message> = []

for (let i = 0; i < anthropicMessages.length; i++) {
const message = anthropicMessages[i]
if (message.role === "user") {
otherMessages.push(...handleUserMessage(message))
} else {
const assistantMessages = handleAssistantMessage(message)
otherMessages.push(...assistantMessages)
}
}

return [...systemMessages, ...otherMessages]
}
Expand Down