Skip to content

Commit 6e0f213

Browse files
committed
refactor: Rename file and translate payload/response in one file
1 parent a8f5f22 commit 6e0f213

File tree

2 files changed

+92
-92
lines changed

2 files changed

+92
-92
lines changed

src/routes/messages/openai-to-anthropic.ts renamed to src/routes/messages/openai-anthropic-translation.ts

Lines changed: 87 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -91,74 +91,26 @@ interface AnthropicToolUseBlock {
9191
input: Record<string, unknown>
9292
}
9393

94-
// Translation functions
94+
// Payload translation
9595

96-
function getAnthropicTextBlocks(
97-
messageContent: Message["content"],
98-
): Array<AnthropicTextBlock> {
99-
if (typeof messageContent === "string") {
100-
return [{ type: "text", text: messageContent }]
101-
}
102-
103-
if (Array.isArray(messageContent)) {
104-
return messageContent
105-
.filter((part): part is TextPart => part.type === "text")
106-
.map((part) => ({ type: "text", text: part.text }))
107-
}
108-
109-
return []
110-
}
111-
112-
function getAnthropicToolUseBlocks(
113-
toolCalls: Array<ToolCall> | undefined,
114-
): Array<AnthropicToolUseBlock> {
115-
if (!toolCalls) {
116-
return []
117-
}
118-
return toolCalls.map((toolCall) => ({
119-
type: "tool_use",
120-
id: toolCall.id,
121-
name: toolCall.function.name,
122-
input: JSON.parse(toolCall.function.arguments) as Record<string, unknown>,
123-
}))
124-
}
125-
126-
function mapOpenAIStopReasonToAnthropic(
127-
finishReason: ChatCompletionResponse["choices"][0]["finish_reason"],
128-
): AnthropicResponse["stop_reason"] {
129-
const stopReasonMap = {
130-
stop: "end_turn",
131-
length: "max_tokens",
132-
tool_calls: "tool_use",
133-
content_filter: "end_turn",
134-
} as const
135-
return stopReasonMap[finishReason]
136-
}
137-
138-
function mapContent(
139-
content: string | Array<AnthropicContentBlock>,
140-
): string | Array<ContentPart> | null {
141-
if (typeof content === "string") {
142-
return content
143-
}
144-
if (!Array.isArray(content)) {
145-
return null
146-
}
147-
148-
const contentParts: Array<ContentPart> = []
149-
for (const block of content) {
150-
if (block.type === "text") {
151-
contentParts.push({ type: "text", text: block.text })
152-
} else if (block.type === "image") {
153-
contentParts.push({
154-
type: "image_url",
155-
image_url: {
156-
url: `data:${block.source.media_type};base64,${block.source.data}`,
157-
},
158-
})
159-
}
96+
export function translateToOpenAI(
97+
payload: AnthropicMessagesPayload,
98+
): ChatCompletionsPayload {
99+
return {
100+
model: payload.model,
101+
messages: translateAnthropicMessagesToOpenAI(
102+
payload.messages,
103+
payload.system,
104+
),
105+
max_tokens: payload.max_tokens,
106+
stop: payload.stop_sequences,
107+
stream: payload.stream,
108+
temperature: payload.temperature,
109+
top_p: payload.top_p,
110+
user: payload.metadata?.user_id,
111+
tools: translateAnthropicToolsToOpenAI(payload.tools),
112+
tool_choice: translateAnthropicToolChoiceToOpenAI(payload.tool_choice),
160113
}
161-
return contentParts
162114
}
163115

164116
function translateAnthropicMessagesToOpenAI(
@@ -210,6 +162,32 @@ function translateAnthropicMessagesToOpenAI(
210162
return messages
211163
}
212164

165+
function mapContent(
166+
content: string | Array<AnthropicContentBlock>,
167+
): string | Array<ContentPart> | null {
168+
if (typeof content === "string") {
169+
return content
170+
}
171+
if (!Array.isArray(content)) {
172+
return null
173+
}
174+
175+
const contentParts: Array<ContentPart> = []
176+
for (const block of content) {
177+
if (block.type === "text") {
178+
contentParts.push({ type: "text", text: block.text })
179+
} else if (block.type === "image") {
180+
contentParts.push({
181+
type: "image_url",
182+
image_url: {
183+
url: `data:${block.source.media_type};base64,${block.source.data}`,
184+
},
185+
})
186+
}
187+
}
188+
return contentParts
189+
}
190+
213191
function translateAnthropicToolsToOpenAI(
214192
anthropicTools: Array<AnthropicTool> | undefined,
215193
): Array<Tool> | undefined {
@@ -238,9 +216,6 @@ function translateAnthropicToolChoiceToOpenAI(
238216
return "auto"
239217
}
240218
case "any": {
241-
// The type definition for tool_choice is missing "required", but it's a valid value.
242-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
243-
// @ts-expect-error
244219
return "required"
245220
}
246221
case "tool": {
@@ -258,25 +233,7 @@ function translateAnthropicToolChoiceToOpenAI(
258233
}
259234
}
260235

261-
export function translateToOpenAI(
262-
payload: AnthropicMessagesPayload,
263-
): ChatCompletionsPayload {
264-
return {
265-
model: payload.model,
266-
messages: translateAnthropicMessagesToOpenAI(
267-
payload.messages,
268-
payload.system,
269-
),
270-
max_tokens: payload.max_tokens,
271-
stop: payload.stop_sequences,
272-
stream: payload.stream,
273-
temperature: payload.temperature,
274-
top_p: payload.top_p,
275-
user: payload.metadata?.user_id,
276-
tools: translateAnthropicToolsToOpenAI(payload.tools),
277-
tool_choice: translateAnthropicToolChoiceToOpenAI(payload.tool_choice),
278-
}
279-
}
236+
// Response translation
280237

281238
export function translateToAnthropic(
282239
response: ChatCompletionResponse,
@@ -299,3 +256,45 @@ export function translateToAnthropic(
299256
},
300257
}
301258
}
259+
260+
function getAnthropicTextBlocks(
261+
messageContent: Message["content"],
262+
): Array<AnthropicTextBlock> {
263+
if (typeof messageContent === "string") {
264+
return [{ type: "text", text: messageContent }]
265+
}
266+
267+
if (Array.isArray(messageContent)) {
268+
return messageContent
269+
.filter((part): part is TextPart => part.type === "text")
270+
.map((part) => ({ type: "text", text: part.text }))
271+
}
272+
273+
return []
274+
}
275+
276+
function getAnthropicToolUseBlocks(
277+
toolCalls: Array<ToolCall> | undefined,
278+
): Array<AnthropicToolUseBlock> {
279+
if (!toolCalls) {
280+
return []
281+
}
282+
return toolCalls.map((toolCall) => ({
283+
type: "tool_use",
284+
id: toolCall.id,
285+
name: toolCall.function.name,
286+
input: JSON.parse(toolCall.function.arguments) as Record<string, unknown>,
287+
}))
288+
}
289+
290+
function mapOpenAIStopReasonToAnthropic(
291+
finishReason: ChatCompletionResponse["choices"][0]["finish_reason"],
292+
): AnthropicResponse["stop_reason"] {
293+
const stopReasonMap = {
294+
stop: "end_turn",
295+
length: "max_tokens",
296+
tool_calls: "tool_use",
297+
content_filter: "end_turn",
298+
} as const
299+
return stopReasonMap[finishReason]
300+
}

src/services/copilot/create-chat-completions.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ export const createChatCompletions = async (
99
) => {
1010
if (!state.copilotToken) throw new Error("Copilot token not found")
1111

12-
const visionEnable = payload.messages.some(
12+
const enableVision = payload.messages.some(
1313
(x) =>
1414
typeof x.content !== "string"
1515
&& x.content?.some((x) => x.type === "image_url"),
1616
)
1717

1818
const response = await fetch(`${copilotBaseUrl(state)}/chat/completions`, {
1919
method: "POST",
20-
headers: copilotHeaders(state, visionEnable),
20+
headers: copilotHeaders(state, enableVision),
2121
body: JSON.stringify(payload),
2222
})
2323

@@ -60,7 +60,7 @@ interface Choice {
6060
index: number
6161
delta: Delta
6262
finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | null
63-
logprobs: null
63+
logprobs: object | null
6464
}
6565

6666
// Non-streaming types
@@ -82,7 +82,7 @@ export interface ChatCompletionResponse {
8282
interface ChoiceNonStreaming {
8383
index: number
8484
message: Message
85-
logprobs: null
85+
logprobs: object | null
8686
finish_reason: "stop" | "length" | "tool_calls" | "content_filter"
8787
}
8888

@@ -108,6 +108,7 @@ export interface ChatCompletionsPayload {
108108
tool_choice?:
109109
| "none"
110110
| "auto"
111+
| "required"
111112
| { type: "function"; function: { name: string } }
112113
| null
113114
user?: string | null

0 commit comments

Comments
 (0)