Remove unused code

This commit is contained in:
cte 2025-02-24 22:13:45 -08:00
parent 09902904f3
commit 52030b12e1
7 changed files with 349 additions and 810 deletions

View file

@ -9,7 +9,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
import { ApiHandler, SingleCompletionHandler } from "../"
import { ApiHandlerOptions, BedrockModelId, ModelInfo, bedrockDefaultModelId, bedrockModels } from "../../shared/api"
import { ApiStream } from "../transform/stream"
import { convertToBedrockConverseMessages, convertToAnthropicMessage } from "../transform/bedrock-converse-format"
import { convertToBedrockConverseMessages } from "../transform/bedrock-converse-format"
const BEDROCK_DEFAULT_TEMPERATURE = 0.3

View file

@ -1,254 +1,167 @@
// npx jest src/api/transform/__tests__/bedrock-converse-format.test.ts
import { convertToBedrockConverseMessages, convertToAnthropicMessage } from "../bedrock-converse-format"
import { convertToBedrockConverseMessages } from "../bedrock-converse-format"
import { Anthropic } from "@anthropic-ai/sdk"
import { ContentBlock, ToolResultContentBlock } from "@aws-sdk/client-bedrock-runtime"
import { StreamEvent } from "../../providers/bedrock"
describe("bedrock-converse-format", () => {
describe("convertToBedrockConverseMessages", () => {
test("converts simple text messages correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there" },
]
describe("convertToBedrockConverseMessages", () => {
test("converts simple text messages correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there" },
]
const result = convertToBedrockConverseMessages(messages)
const result = convertToBedrockConverseMessages(messages)
expect(result).toEqual([
{
role: "user",
content: [{ text: "Hello" }],
},
{
role: "assistant",
content: [{ text: "Hi there" }],
},
])
})
test("converts messages with images correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
type: "text",
text: "Look at this image:",
},
{
type: "image",
source: {
type: "base64",
data: "SGVsbG8=", // "Hello" in base64
media_type: "image/jpeg" as const,
},
},
],
},
]
const result = convertToBedrockConverseMessages(messages)
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("user")
expect(result[0].content).toHaveLength(2)
expect(result[0].content[0]).toEqual({ text: "Look at this image:" })
const imageBlock = result[0].content[1] as ContentBlock
if ("image" in imageBlock && imageBlock.image && imageBlock.image.source) {
expect(imageBlock.image.format).toBe("jpeg")
expect(imageBlock.image.source).toBeDefined()
expect(imageBlock.image.source.bytes).toBeDefined()
} else {
fail("Expected image block not found")
}
})
test("converts tool use messages correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [
{
type: "tool_use",
id: "test-id",
name: "read_file",
input: {
path: "test.txt",
},
},
],
},
]
const result = convertToBedrockConverseMessages(messages)
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("assistant")
const toolBlock = result[0].content[0] as ContentBlock
if ("toolUse" in toolBlock && toolBlock.toolUse) {
expect(toolBlock.toolUse).toEqual({
toolUseId: "test-id",
name: "read_file",
input: "<read_file>\n<path>\ntest.txt\n</path>\n</read_file>",
})
} else {
fail("Expected tool use block not found")
}
})
test("converts tool result messages correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [
{
type: "tool_result",
tool_use_id: "test-id",
content: [{ type: "text", text: "File contents here" }],
},
],
},
]
const result = convertToBedrockConverseMessages(messages)
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("assistant")
const resultBlock = result[0].content[0] as ContentBlock
if ("toolResult" in resultBlock && resultBlock.toolResult) {
const expectedContent: ToolResultContentBlock[] = [{ text: "File contents here" }]
expect(resultBlock.toolResult).toEqual({
toolUseId: "test-id",
content: expectedContent,
status: "success",
})
} else {
fail("Expected tool result block not found")
}
})
test("handles text content correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
type: "text",
text: "Hello world",
},
],
},
]
const result = convertToBedrockConverseMessages(messages)
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("user")
expect(result[0].content).toHaveLength(1)
const textBlock = result[0].content[0] as ContentBlock
expect(textBlock).toEqual({ text: "Hello world" })
})
expect(result).toEqual([
{
role: "user",
content: [{ text: "Hello" }],
},
{
role: "assistant",
content: [{ text: "Hi there" }],
},
])
})
describe("convertToAnthropicMessage", () => {
test("converts metadata events correctly", () => {
const event: StreamEvent = {
metadata: {
usage: {
inputTokens: 10,
outputTokens: 20,
test("converts messages with images correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
type: "text",
text: "Look at this image:",
},
},
}
const result = convertToAnthropicMessage(event, "test-model")
expect(result).toEqual({
id: "",
type: "message",
role: "assistant",
model: "test-model",
usage: {
input_tokens: 10,
output_tokens: 20,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
})
})
test("converts content block start events correctly", () => {
const event: StreamEvent = {
contentBlockStart: {
start: {
text: "Hello",
{
type: "image",
source: {
type: "base64",
data: "SGVsbG8=", // "Hello" in base64
media_type: "image/jpeg" as const,
},
},
},
}
],
},
]
const result = convertToAnthropicMessage(event, "test-model")
const result = convertToBedrockConverseMessages(messages)
expect(result).toEqual({
type: "message",
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("user")
expect(result[0].content).toHaveLength(2)
expect(result[0].content[0]).toEqual({ text: "Look at this image:" })
const imageBlock = result[0].content[1] as ContentBlock
if ("image" in imageBlock && imageBlock.image && imageBlock.image.source) {
expect(imageBlock.image.format).toBe("jpeg")
expect(imageBlock.image.source).toBeDefined()
expect(imageBlock.image.source.bytes).toBeDefined()
} else {
fail("Expected image block not found")
}
})
test("converts tool use messages correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [{ type: "text", text: "Hello", citations: null }],
model: "test-model",
})
})
test("converts content block delta events correctly", () => {
const event: StreamEvent = {
contentBlockDelta: {
delta: {
text: " world",
content: [
{
type: "tool_use",
id: "test-id",
name: "read_file",
input: {
path: "test.txt",
},
},
},
}
],
},
]
const result = convertToAnthropicMessage(event, "test-model")
const result = convertToBedrockConverseMessages(messages)
expect(result).toEqual({
type: "message",
role: "assistant",
content: [{ type: "text", text: " world", citations: null }],
model: "test-model",
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("assistant")
const toolBlock = result[0].content[0] as ContentBlock
if ("toolUse" in toolBlock && toolBlock.toolUse) {
expect(toolBlock.toolUse).toEqual({
toolUseId: "test-id",
name: "read_file",
input: "<read_file>\n<path>\ntest.txt\n</path>\n</read_file>",
})
})
} else {
fail("Expected tool use block not found")
}
})
test("converts message stop events correctly", () => {
const event: StreamEvent = {
messageStop: {
stopReason: "end_turn" as const,
},
}
const result = convertToAnthropicMessage(event, "test-model")
expect(result).toEqual({
type: "message",
test("converts tool result messages correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
stop_reason: "end_turn",
stop_sequence: null,
model: "test-model",
content: [
{
type: "tool_result",
tool_use_id: "test-id",
content: [{ type: "text", text: "File contents here" }],
},
],
},
]
const result = convertToBedrockConverseMessages(messages)
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("assistant")
const resultBlock = result[0].content[0] as ContentBlock
if ("toolResult" in resultBlock && resultBlock.toolResult) {
const expectedContent: ToolResultContentBlock[] = [{ text: "File contents here" }]
expect(resultBlock.toolResult).toEqual({
toolUseId: "test-id",
content: expectedContent,
status: "success",
})
})
} else {
fail("Expected tool result block not found")
}
})
test("handles text content correctly", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
type: "text",
text: "Hello world",
},
],
},
]
const result = convertToBedrockConverseMessages(messages)
if (!result[0] || !result[0].content) {
fail("Expected result to have content")
return
}
expect(result[0].role).toBe("user")
expect(result[0].content).toHaveLength(1)
const textBlock = result[0].content[0] as ContentBlock
expect(textBlock).toEqual({ text: "Hello world" })
})
})

View file

@ -1,281 +1,131 @@
// npx jest src/api/transform/__tests__/openai-format.test.ts
import { convertToOpenAiMessages, convertToAnthropicMessage } from "../openai-format"
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
type PartialChatCompletion = Omit<OpenAI.Chat.Completions.ChatCompletion, "choices"> & {
choices: Array<
Partial<OpenAI.Chat.Completions.ChatCompletion.Choice> & {
message: OpenAI.Chat.Completions.ChatCompletion.Choice["message"]
finish_reason: string
index: number
}
>
}
import { convertToOpenAiMessages } from "../openai-format"
describe("OpenAI Format Transformations", () => {
describe("convertToOpenAiMessages", () => {
it("should convert simple text messages", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello",
},
{
role: "assistant",
content: "Hi there!",
},
]
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(2)
expect(openAiMessages[0]).toEqual({
describe("convertToOpenAiMessages", () => {
it("should convert simple text messages", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello",
})
expect(openAiMessages[1]).toEqual({
},
{
role: "assistant",
content: "Hi there!",
})
},
]
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(2)
expect(openAiMessages[0]).toEqual({
role: "user",
content: "Hello",
})
it("should handle messages with image content", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
type: "text",
text: "What is in this image?",
},
{
type: "image",
source: {
type: "base64",
media_type: "image/jpeg",
data: "base64data",
},
},
],
},
]
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(1)
expect(openAiMessages[0].role).toBe("user")
const content = openAiMessages[0].content as Array<{
type: string
text?: string
image_url?: { url: string }
}>
expect(Array.isArray(content)).toBe(true)
expect(content).toHaveLength(2)
expect(content[0]).toEqual({ type: "text", text: "What is in this image?" })
expect(content[1]).toEqual({
type: "image_url",
image_url: { url: "data:image/jpeg;base64,base64data" },
})
})
it("should handle assistant messages with tool use", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [
{
type: "text",
text: "Let me check the weather.",
},
{
type: "tool_use",
id: "weather-123",
name: "get_weather",
input: { city: "London" },
},
],
},
]
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(1)
const assistantMessage = openAiMessages[0] as OpenAI.Chat.ChatCompletionAssistantMessageParam
expect(assistantMessage.role).toBe("assistant")
expect(assistantMessage.content).toBe("Let me check the weather.")
expect(assistantMessage.tool_calls).toHaveLength(1)
expect(assistantMessage.tool_calls![0]).toEqual({
id: "weather-123",
type: "function",
function: {
name: "get_weather",
arguments: JSON.stringify({ city: "London" }),
},
})
})
it("should handle user messages with tool results", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: "weather-123",
content: "Current temperature in London: 20°C",
},
],
},
]
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(1)
const toolMessage = openAiMessages[0] as OpenAI.Chat.ChatCompletionToolMessageParam
expect(toolMessage.role).toBe("tool")
expect(toolMessage.tool_call_id).toBe("weather-123")
expect(toolMessage.content).toBe("Current temperature in London: 20°C")
expect(openAiMessages[1]).toEqual({
role: "assistant",
content: "Hi there!",
})
})
describe("convertToAnthropicMessage", () => {
it("should convert simple completion", () => {
const openAiCompletion: PartialChatCompletion = {
id: "completion-123",
model: "gpt-4",
choices: [
it("should handle messages with image content", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
message: {
role: "assistant",
content: "Hello there!",
refusal: null,
type: "text",
text: "What is in this image?",
},
{
type: "image",
source: {
type: "base64",
media_type: "image/jpeg",
data: "base64data",
},
finish_reason: "stop",
index: 0,
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 5,
total_tokens: 15,
},
created: 123456789,
object: "chat.completion",
}
},
]
const anthropicMessage = convertToAnthropicMessage(
openAiCompletion as OpenAI.Chat.Completions.ChatCompletion,
)
expect(anthropicMessage.id).toBe("completion-123")
expect(anthropicMessage.role).toBe("assistant")
expect(anthropicMessage.content).toHaveLength(1)
expect(anthropicMessage.content[0]).toEqual({
type: "text",
text: "Hello there!",
citations: null,
})
expect(anthropicMessage.stop_reason).toBe("end_turn")
expect(anthropicMessage.usage).toEqual({
input_tokens: 10,
output_tokens: 5,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
})
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(1)
expect(openAiMessages[0].role).toBe("user")
const content = openAiMessages[0].content as Array<{
type: string
text?: string
image_url?: { url: string }
}>
expect(Array.isArray(content)).toBe(true)
expect(content).toHaveLength(2)
expect(content[0]).toEqual({ type: "text", text: "What is in this image?" })
expect(content[1]).toEqual({
type: "image_url",
image_url: { url: "data:image/jpeg;base64,base64data" },
})
})
it("should handle tool calls in completion", () => {
const openAiCompletion: PartialChatCompletion = {
id: "completion-123",
model: "gpt-4",
choices: [
it("should handle assistant messages with tool use", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [
{
message: {
role: "assistant",
content: "Let me check the weather.",
tool_calls: [
{
id: "weather-123",
type: "function",
function: {
name: "get_weather",
arguments: '{"city":"London"}',
},
},
],
refusal: null,
},
finish_reason: "tool_calls",
index: 0,
type: "text",
text: "Let me check the weather.",
},
{
type: "tool_use",
id: "weather-123",
name: "get_weather",
input: { city: "London" },
},
],
usage: {
prompt_tokens: 15,
completion_tokens: 8,
total_tokens: 23,
},
created: 123456789,
object: "chat.completion",
}
},
]
const anthropicMessage = convertToAnthropicMessage(
openAiCompletion as OpenAI.Chat.Completions.ChatCompletion,
)
expect(anthropicMessage.content).toHaveLength(2)
expect(anthropicMessage.content[0]).toEqual({
type: "text",
text: "Let me check the weather.",
citations: null,
})
expect(anthropicMessage.content[1]).toEqual({
type: "tool_use",
id: "weather-123",
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(1)
const assistantMessage = openAiMessages[0] as OpenAI.Chat.ChatCompletionAssistantMessageParam
expect(assistantMessage.role).toBe("assistant")
expect(assistantMessage.content).toBe("Let me check the weather.")
expect(assistantMessage.tool_calls).toHaveLength(1)
expect(assistantMessage.tool_calls![0]).toEqual({
id: "weather-123",
type: "function",
function: {
name: "get_weather",
input: { city: "London" },
})
expect(anthropicMessage.stop_reason).toBe("tool_use")
arguments: JSON.stringify({ city: "London" }),
},
})
})
it("should handle invalid tool call arguments", () => {
const openAiCompletion: PartialChatCompletion = {
id: "completion-123",
model: "gpt-4",
choices: [
it("should handle user messages with tool results", () => {
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{
message: {
role: "assistant",
content: "Testing invalid arguments",
tool_calls: [
{
id: "test-123",
type: "function",
function: {
name: "test_function",
arguments: "invalid json",
},
},
],
refusal: null,
},
finish_reason: "tool_calls",
index: 0,
type: "tool_result",
tool_use_id: "weather-123",
content: "Current temperature in London: 20°C",
},
],
created: 123456789,
object: "chat.completion",
}
},
]
const anthropicMessage = convertToAnthropicMessage(
openAiCompletion as OpenAI.Chat.Completions.ChatCompletion,
)
expect(anthropicMessage.content).toHaveLength(2)
expect(anthropicMessage.content[1]).toEqual({
type: "tool_use",
id: "test-123",
name: "test_function",
input: {}, // Should default to empty object for invalid JSON
})
})
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
expect(openAiMessages).toHaveLength(1)
const toolMessage = openAiMessages[0] as OpenAI.Chat.ChatCompletionToolMessageParam
expect(toolMessage.role).toBe("tool")
expect(toolMessage.tool_call_id).toBe("weather-123")
expect(toolMessage.content).toBe("Current temperature in London: 20°C")
})
})

View file

@ -1,8 +1,8 @@
// npx jest src/api/transform/__tests__/vscode-lm-format.test.ts
import { Anthropic } from "@anthropic-ai/sdk"
import * as vscode from "vscode"
import { convertToVsCodeLmMessages, convertToAnthropicRole, convertToAnthropicMessage } from "../vscode-lm-format"
import { convertToVsCodeLmMessages, convertToAnthropicRole } from "../vscode-lm-format"
// Mock crypto
const mockCrypto = {
@ -29,14 +29,6 @@ interface MockLanguageModelToolResultPart {
parts: MockLanguageModelTextPart[]
}
type MockMessageContent = MockLanguageModelTextPart | MockLanguageModelToolCallPart | MockLanguageModelToolResultPart
interface MockLanguageModelChatMessage {
role: string
name?: string
content: MockMessageContent[]
}
// Mock vscode namespace
jest.mock("vscode", () => {
const LanguageModelChatMessageRole = {
@ -86,174 +78,115 @@ jest.mock("vscode", () => {
}
})
describe("vscode-lm-format", () => {
describe("convertToVsCodeLmMessages", () => {
it("should convert simple string messages", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there" },
]
describe("convertToVsCodeLmMessages", () => {
it("should convert simple string messages", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there" },
]
const result = convertToVsCodeLmMessages(messages)
const result = convertToVsCodeLmMessages(messages)
expect(result).toHaveLength(2)
expect(result[0].role).toBe("user")
expect((result[0].content[0] as MockLanguageModelTextPart).value).toBe("Hello")
expect(result[1].role).toBe("assistant")
expect((result[1].content[0] as MockLanguageModelTextPart).value).toBe("Hi there")
})
it("should handle complex user messages with tool results", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{ type: "text", text: "Here is the result:" },
{
type: "tool_result",
tool_use_id: "tool-1",
content: "Tool output",
},
],
},
]
const result = convertToVsCodeLmMessages(messages)
expect(result).toHaveLength(1)
expect(result[0].role).toBe("user")
expect(result[0].content).toHaveLength(2)
const [toolResult, textContent] = result[0].content as [
MockLanguageModelToolResultPart,
MockLanguageModelTextPart,
]
expect(toolResult.type).toBe("tool_result")
expect(textContent.type).toBe("text")
})
it("should handle complex assistant messages with tool calls", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [
{ type: "text", text: "Let me help you with that." },
{
type: "tool_use",
id: "tool-1",
name: "calculator",
input: { operation: "add", numbers: [2, 2] },
},
],
},
]
const result = convertToVsCodeLmMessages(messages)
expect(result).toHaveLength(1)
expect(result[0].role).toBe("assistant")
expect(result[0].content).toHaveLength(2)
const [toolCall, textContent] = result[0].content as [
MockLanguageModelToolCallPart,
MockLanguageModelTextPart,
]
expect(toolCall.type).toBe("tool_call")
expect(textContent.type).toBe("text")
})
it("should handle image blocks with appropriate placeholders", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{ type: "text", text: "Look at this:" },
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: "base64data",
},
},
],
},
]
const result = convertToVsCodeLmMessages(messages)
expect(result).toHaveLength(1)
const imagePlaceholder = result[0].content[1] as MockLanguageModelTextPart
expect(imagePlaceholder.value).toContain("[Image (base64): image/png not supported by VSCode LM API]")
})
expect(result).toHaveLength(2)
expect(result[0].role).toBe("user")
expect((result[0].content[0] as MockLanguageModelTextPart).value).toBe("Hello")
expect(result[1].role).toBe("assistant")
expect((result[1].content[0] as MockLanguageModelTextPart).value).toBe("Hi there")
})
describe("convertToAnthropicRole", () => {
it("should convert assistant role correctly", () => {
const result = convertToAnthropicRole("assistant" as any)
expect(result).toBe("assistant")
})
it("should convert user role correctly", () => {
const result = convertToAnthropicRole("user" as any)
expect(result).toBe("user")
})
it("should return null for unknown roles", () => {
const result = convertToAnthropicRole("unknown" as any)
expect(result).toBeNull()
})
})
describe("convertToAnthropicMessage", () => {
it("should convert assistant message with text content", async () => {
const vsCodeMessage = {
role: "assistant",
name: "assistant",
content: [new vscode.LanguageModelTextPart("Hello")],
}
const result = await convertToAnthropicMessage(vsCodeMessage as any)
expect(result.role).toBe("assistant")
expect(result.content).toHaveLength(1)
expect(result.content[0]).toEqual({
type: "text",
text: "Hello",
citations: null,
})
expect(result.id).toBe("test-uuid")
})
it("should convert assistant message with tool calls", async () => {
const vsCodeMessage = {
role: "assistant",
name: "assistant",
content: [
new vscode.LanguageModelToolCallPart("call-1", "calculator", { operation: "add", numbers: [2, 2] }),
],
}
const result = await convertToAnthropicMessage(vsCodeMessage as any)
expect(result.content).toHaveLength(1)
expect(result.content[0]).toEqual({
type: "tool_use",
id: "call-1",
name: "calculator",
input: { operation: "add", numbers: [2, 2] },
})
expect(result.id).toBe("test-uuid")
})
it("should throw error for non-assistant messages", async () => {
const vsCodeMessage = {
it("should handle complex user messages with tool results", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
name: "user",
content: [new vscode.LanguageModelTextPart("Hello")],
}
content: [
{ type: "text", text: "Here is the result:" },
{
type: "tool_result",
tool_use_id: "tool-1",
content: "Tool output",
},
],
},
]
await expect(convertToAnthropicMessage(vsCodeMessage as any)).rejects.toThrow(
"Roo Code <Language Model API>: Only assistant messages are supported.",
)
})
const result = convertToVsCodeLmMessages(messages)
expect(result).toHaveLength(1)
expect(result[0].role).toBe("user")
expect(result[0].content).toHaveLength(2)
const [toolResult, textContent] = result[0].content as [
MockLanguageModelToolResultPart,
MockLanguageModelTextPart,
]
expect(toolResult.type).toBe("tool_result")
expect(textContent.type).toBe("text")
})
it("should handle complex assistant messages with tool calls", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [
{ type: "text", text: "Let me help you with that." },
{
type: "tool_use",
id: "tool-1",
name: "calculator",
input: { operation: "add", numbers: [2, 2] },
},
],
},
]
const result = convertToVsCodeLmMessages(messages)
expect(result).toHaveLength(1)
expect(result[0].role).toBe("assistant")
expect(result[0].content).toHaveLength(2)
const [toolCall, textContent] = result[0].content as [MockLanguageModelToolCallPart, MockLanguageModelTextPart]
expect(toolCall.type).toBe("tool_call")
expect(textContent.type).toBe("text")
})
it("should handle image blocks with appropriate placeholders", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [
{ type: "text", text: "Look at this:" },
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: "base64data",
},
},
],
},
]
const result = convertToVsCodeLmMessages(messages)
expect(result).toHaveLength(1)
const imagePlaceholder = result[0].content[1] as MockLanguageModelTextPart
expect(imagePlaceholder.value).toContain("[Image (base64): image/png not supported by VSCode LM API]")
})
})
describe("convertToAnthropicRole", () => {
it("should convert assistant role correctly", () => {
const result = convertToAnthropicRole("assistant" as any)
expect(result).toBe("assistant")
})
it("should convert user role correctly", () => {
const result = convertToAnthropicRole("user" as any)
expect(result).toBe("user")
})
it("should return null for unknown roles", () => {
const result = convertToAnthropicRole("unknown" as any)
expect(result).toBeNull()
})
})

View file

@ -1,9 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { MessageContent } from "../../shared/api"
import { ConversationRole, Message, ContentBlock } from "@aws-sdk/client-bedrock-runtime"
// Import StreamEvent type from bedrock.ts
import { StreamEvent } from "../providers/bedrock"
import { MessageContent } from "../../shared/api"
/**
* Convert Anthropic messages to Bedrock Converse format
@ -175,51 +173,3 @@ export function convertToBedrockConverseMessages(anthropicMessages: Anthropic.Me
}
})
}
/**
* Convert Bedrock Converse stream events to Anthropic message format
*/
export function convertToAnthropicMessage(
streamEvent: StreamEvent,
modelId: string,
): Partial<Anthropic.Messages.Message> {
// Handle metadata events
if (streamEvent.metadata?.usage) {
return {
id: "", // Bedrock doesn't provide message IDs
type: "message",
role: "assistant",
model: modelId,
usage: {
input_tokens: streamEvent.metadata.usage.inputTokens || 0,
output_tokens: streamEvent.metadata.usage.outputTokens || 0,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
}
}
// Handle content blocks
const text = streamEvent.contentBlockStart?.start?.text || streamEvent.contentBlockDelta?.delta?.text
if (text !== undefined) {
return {
type: "message",
role: "assistant",
content: [{ type: "text", text: text, citations: null }],
model: modelId,
}
}
// Handle message stop
if (streamEvent.messageStop) {
return {
type: "message",
role: "assistant",
stop_reason: streamEvent.messageStop.stopReason || null,
stop_sequence: null,
model: modelId,
}
}
return {}
}

View file

@ -144,63 +144,3 @@ export function convertToOpenAiMessages(
return openAiMessages
}
// Convert OpenAI response to Anthropic format
export function convertToAnthropicMessage(
completion: OpenAI.Chat.Completions.ChatCompletion,
): Anthropic.Messages.Message {
const openAiMessage = completion.choices[0].message
const anthropicMessage: Anthropic.Messages.Message = {
id: completion.id,
type: "message",
role: openAiMessage.role, // always "assistant"
content: [
{
type: "text",
text: openAiMessage.content || "",
citations: null,
},
],
model: completion.model,
stop_reason: (() => {
switch (completion.choices[0].finish_reason) {
case "stop":
return "end_turn"
case "length":
return "max_tokens"
case "tool_calls":
return "tool_use"
case "content_filter": // Anthropic doesn't have an exact equivalent
default:
return null
}
})(),
stop_sequence: null, // which custom stop_sequence was generated, if any (not applicable if you don't use stop_sequence)
usage: {
input_tokens: completion.usage?.prompt_tokens || 0,
output_tokens: completion.usage?.completion_tokens || 0,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
}
if (openAiMessage.tool_calls && openAiMessage.tool_calls.length > 0) {
anthropicMessage.content.push(
...openAiMessage.tool_calls.map((toolCall): Anthropic.ToolUseBlock => {
let parsedInput = {}
try {
parsedInput = JSON.parse(toolCall.function.arguments || "{}")
} catch (error) {
console.error("Failed to parse tool arguments:", error)
}
return {
type: "tool_use",
id: toolCall.id,
name: toolCall.function.name,
input: parsedInput,
}
}),
)
}
return anthropicMessage
}

View file

@ -155,50 +155,3 @@ export function convertToAnthropicRole(vsCodeLmMessageRole: vscode.LanguageModel
return null
}
}
export async function convertToAnthropicMessage(
vsCodeLmMessage: vscode.LanguageModelChatMessage,
): Promise<Anthropic.Messages.Message> {
const anthropicRole: string | null = convertToAnthropicRole(vsCodeLmMessage.role)
if (anthropicRole !== "assistant") {
throw new Error("Roo Code <Language Model API>: Only assistant messages are supported.")
}
return {
id: crypto.randomUUID(),
type: "message",
model: "vscode-lm",
role: anthropicRole,
content: vsCodeLmMessage.content
.map((part): Anthropic.ContentBlock | null => {
if (part instanceof vscode.LanguageModelTextPart) {
return {
type: "text",
text: part.value,
citations: null,
}
}
if (part instanceof vscode.LanguageModelToolCallPart) {
return {
type: "tool_use",
id: part.callId || crypto.randomUUID(),
name: part.name,
input: asObjectSafe(part.input),
}
}
return null
})
.filter((part): part is Anthropic.ContentBlock => part !== null),
stop_reason: null,
stop_sequence: null,
usage: {
input_tokens: 0,
output_tokens: 0,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
},
}
}