@arizeai/phoenix-client
Version:
A client for the Phoenix API
123 lines • 4.53 kB
JavaScript
import { assertUnreachable } from "../../../utils/assertUnreachable.js";
import { safelyStringifyJSON } from "../../../utils/safelyStringifyJSON.js";
import { openAIMessageRoleSchema, } from "../openai/messageSchemas.js";
import { anthropicMessagePartSchema, } from "./messagePartSchemas.js";
import { anthropicMessageSchema } from "./messageSchemas.js";
import { anthropicToolCallSchema } from "./toolCallSchemas.js";
import { anthropicToolChoiceSchema } from "./toolChoiceSchemas.js";
import { anthropicToolDefinitionSchema } from "./toolSchemas.js";
import invariant from "tiny-invariant";
/*
* Conversion Functions
*
* These follow a hub-and-spoke model where OpenAI is the hub format.
* All conversions between different formats go through OpenAI as an intermediate step.
*/
export const anthropicMessagePartToOpenAI = anthropicMessagePartSchema.transform((anthropic) => {
const type = anthropic.type;
switch (type) {
case "text":
return {
type: "text",
text: anthropic.text,
};
case "image":
return {
type: "image_url",
image_url: { url: anthropic.source.data },
};
case "tool_use":
return null;
case "tool_result":
return null;
default:
return assertUnreachable(type);
}
});
/**
* Spoke → Hub: Convert an Anthropic message to OpenAI format
*/
export const anthropicMessageToOpenAI = anthropicMessageSchema.transform((anthropic) => {
let role = openAIMessageRoleSchema.parse(anthropic.role);
if (Array.isArray(anthropic.content) &&
anthropic.content.some((part) => part.type === "tool_result")) {
role = "tool";
}
const initialContentArray = typeof anthropic.content === "string"
? [{ type: "text", text: anthropic.content }]
: anthropic.content;
const toolCallParts = initialContentArray.filter((part) => part.type === "tool_use");
const nonToolCallParts = initialContentArray.filter((part) => part.type !== "tool_use");
invariant(role === "user" || role === "assistant", `Unexpected anthropic role: ${role}`);
switch (role) {
case "assistant": {
const content = nonToolCallParts
.map((part) => anthropicMessagePartToOpenAI.parse(part))
.filter((part) => part !== null && part.type === "text");
return {
role: "assistant",
tool_calls: toolCallParts.length > 0
? toolCallParts.map((tc) => anthropicToolCallToOpenAI.parse(tc))
: undefined,
content,
};
}
case "user": {
const content = nonToolCallParts
.map((part) => anthropicMessagePartToOpenAI.parse(part))
.filter((part) => part !== null);
return {
role: "user",
content,
};
}
default:
return assertUnreachable(role);
}
});
/**
* Parse incoming object as an Anthropic tool call and immediately convert to OpenAI format
*/
export const anthropicToolCallToOpenAI = anthropicToolCallSchema.transform((anthropic) => ({
type: "function",
id: anthropic.id,
function: {
name: anthropic.name,
arguments: typeof anthropic.input === "string"
? anthropic.input
: (safelyStringifyJSON(anthropic.input).json ?? ""),
},
}));
/**
* Parse incoming object as an Anthropic tool choice and immediately convert to OpenAI format
*/
export const anthropicToolChoiceToOpenAI = anthropicToolChoiceSchema.transform((anthropic) => {
switch (anthropic.type) {
case "any":
return "required";
case "auto":
return "auto";
case "tool":
if (!anthropic.name) {
return "auto";
}
return {
type: "function",
function: { name: anthropic.name },
};
default:
return "auto";
}
});
/**
* Parse incoming object as an Anthropic tool call and immediately convert to OpenAI format
*/
export const anthropicToolDefinitionToOpenAI = anthropicToolDefinitionSchema.transform((anthropic) => ({
type: "function",
function: {
name: anthropic.name,
description: anthropic.description,
parameters: anthropic.input_schema,
},
}));
//# sourceMappingURL=converters.js.map