in src/lib/server/endpoints/anthropic/endpointAnthropic.ts [46:158]
export async function endpointAnthropic(
input: z.input<typeof endpointAnthropicParametersSchema>
): Promise<Endpoint> {
const { baseURL, apiKey, model, defaultHeaders, defaultQuery, multimodal } =
endpointAnthropicParametersSchema.parse(input);
let Anthropic;
try {
Anthropic = (await import("@anthropic-ai/sdk")).default;
} catch (e) {
throw new Error("Failed to import @anthropic-ai/sdk", { cause: e });
}
const anthropic = new Anthropic({
apiKey,
baseURL,
defaultHeaders,
defaultQuery,
});
return async ({
messages,
preprompt,
generateSettings,
conversationId,
tools = [],
toolResults = [],
}) => {
let system = preprompt;
if (messages?.[0]?.from === "system") {
system = messages[0].content;
}
let tokenId = 0;
if (tools.length === 0 && toolResults.length > 0) {
const toolNames = new Set(toolResults.map((tool) => tool.call.name));
tools = Array.from(toolNames).map((name) => ({
name,
description: "",
inputs: [],
})) as unknown as Tool[];
}
const parameters = { ...model.parameters, ...generateSettings };
return (async function* () {
const stream = anthropic.messages.stream({
model: model.id ?? model.name,
tools: createAnthropicTools(tools),
tool_choice:
tools.length > 0 ? { type: "auto", disable_parallel_tool_use: false } : undefined,
messages: addToolResults(
await endpointMessagesToAnthropicMessages(messages, multimodal, conversationId),
toolResults
) as MessageParam[],
max_tokens: parameters?.max_new_tokens,
temperature: parameters?.temperature,
top_p: parameters?.top_p,
top_k: parameters?.top_k,
stop_sequences: parameters?.stop,
system,
});
while (true) {
const result = await Promise.race([stream.emitted("text"), stream.emitted("end")]);
if (result === undefined) {
if ("tool_use" === stream.receivedMessages[0].stop_reason) {
// this should really create a new "Assistant" message with the tool id in it.
const toolCalls: ToolCall[] = stream.receivedMessages[0].content
.filter(
(block): block is Anthropic.Messages.ContentBlock & { type: "tool_use" } =>
block.type === "tool_use"
)
.map((block) => ({
name: block.name,
parameters: block.input as Record<string, string | number | boolean>,
id: block.id,
}));
yield {
token: { id: tokenId, text: "", logprob: 0, special: false, toolCalls },
generated_text: null,
details: null,
};
} else {
yield {
token: {
id: tokenId++,
text: "",
logprob: 0,
special: true,
},
generated_text: await stream.finalText(),
details: null,
} satisfies TextGenerationStreamOutput;
}
return;
}
// Text delta
yield {
token: {
id: tokenId++,
text: result as unknown as string,
special: false,
logprob: 0,
},
generated_text: null,
details: null,
} satisfies TextGenerationStreamOutput;
}
})();
};
}