@copilotkit/runtime
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
241 lines (239 loc) • 10.1 kB
JavaScript
require("reflect-metadata");
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
const require_error_utils = require('../shared/error-utils.cjs');
const require_sdk_client_utils = require('../shared/sdk-client-utils.cjs');
const require_utils = require('./utils.cjs');
let _copilotkit_shared = require("@copilotkit/shared");
let _ai_sdk_anthropic = require("@ai-sdk/anthropic");
//#region src/service-adapters/anthropic/anthropic-adapter.ts
const DEFAULT_MODEL = "claude-3-5-sonnet-latest";
var AnthropicAdapter = class {
get anthropic() {
return this._anthropic;
}
get name() {
return "AnthropicAdapter";
}
constructor(params) {
this.model = DEFAULT_MODEL;
this.provider = "anthropic";
if (params?.anthropic) this._anthropic = params.anthropic;
if (params?.model) this.model = params.model;
this.promptCaching = params?.promptCaching || { enabled: false };
this.maxInputTokens = params?.maxInputTokens;
}
getLanguageModel() {
const anthropic = this.ensureAnthropic();
const options = require_sdk_client_utils.getSdkClientOptions(anthropic);
return (0, _ai_sdk_anthropic.createAnthropic)({
baseURL: anthropic.baseURL,
apiKey: anthropic.apiKey,
headers: options.defaultHeaders,
fetch: options.fetch
})(this.model);
}
ensureAnthropic() {
if (!this._anthropic) {
const Anthropic = require("@anthropic-ai/sdk").default;
this._anthropic = new Anthropic({});
}
return this._anthropic;
}
/**
* Adds cache control to system prompt
*/
addSystemPromptCaching(system, debug = false) {
if (!this.promptCaching.enabled || !system) return system;
const originalTextLength = system.length;
if (debug) console.log(`[ANTHROPIC CACHE DEBUG] Added cache control to system prompt (${originalTextLength} chars).`);
return [{
type: "text",
text: system,
cache_control: { type: "ephemeral" }
}];
}
/**
* Adds cache control to the final message
*/
addIncrementalMessageCaching(messages, debug = false) {
if (!this.promptCaching.enabled || messages.length === 0) return messages;
const finalMessage = messages[messages.length - 1];
const messageNumber = messages.length;
if (Array.isArray(finalMessage.content) && finalMessage.content.length > 0) {
const finalBlock = finalMessage.content[finalMessage.content.length - 1];
const updatedMessages = [...messages.slice(0, -1), {
...finalMessage,
content: [...finalMessage.content.slice(0, -1), {
...finalBlock,
cache_control: { type: "ephemeral" }
}]
}];
if (debug) console.log(`[ANTHROPIC CACHE DEBUG] Added cache control to final message (message ${messageNumber}).`);
return updatedMessages;
}
return messages;
}
shouldGenerateFallbackResponse(messages) {
if (messages.length === 0) return false;
const lastMessage = messages[messages.length - 1];
const endsWithToolResult = lastMessage.role === "user" && Array.isArray(lastMessage.content) && lastMessage.content.some((content) => content.type === "tool_result");
if (messages.length >= 3 && endsWithToolResult) {
const lastThree = messages.slice(-3);
return lastThree[0]?.role === "user" && lastThree[1]?.role === "assistant" && Array.isArray(lastThree[1].content) && lastThree[1].content.some((content) => content.type === "tool_use") && lastThree[2]?.role === "user" && Array.isArray(lastThree[2].content) && lastThree[2].content.some((content) => content.type === "tool_result");
}
return endsWithToolResult;
}
async process(request) {
const { threadId, model = this.model, messages: rawMessages, actions, eventSource, forwardedParameters } = request;
const tools = actions.map(require_utils.convertActionInputToAnthropicTool);
const knownActionNames = new Set(actions.map((a) => a.name));
const messages = [...rawMessages];
const instructionsMessage = messages.shift();
const instructions = instructionsMessage.isTextMessage() ? instructionsMessage.content : "";
const validToolUseIds = /* @__PURE__ */ new Set();
for (const message of messages) if (message.isActionExecutionMessage()) validToolUseIds.add(message.id);
const processedToolResultIds = /* @__PURE__ */ new Set();
const limitedMessages = require_utils.limitMessagesToTokenCount(messages.map((message) => {
if (message.isResultMessage()) {
if (!validToolUseIds.has(message.actionExecutionId)) return null;
if (processedToolResultIds.has(message.actionExecutionId)) return null;
processedToolResultIds.add(message.actionExecutionId);
return {
role: "user",
content: [{
type: "tool_result",
content: message.result || "Action completed successfully",
tool_use_id: message.actionExecutionId
}]
};
}
return require_utils.convertMessageToAnthropicMessage(message);
}).filter(Boolean).filter((msg) => {
if (msg.role === "assistant" && Array.isArray(msg.content)) return !(msg.content.length === 1 && msg.content[0].type === "text" && (!msg.content[0].text || msg.content[0].text.trim() === ""));
return true;
}), tools, model, this.maxInputTokens);
const cachedSystemPrompt = this.addSystemPromptCaching(instructions, this.promptCaching.debug);
const cachedMessages = this.addIncrementalMessageCaching(limitedMessages, this.promptCaching.debug);
let toolChoice = forwardedParameters?.toolChoice;
if (forwardedParameters?.toolChoice === "function") toolChoice = {
type: "tool",
name: forwardedParameters.toolChoiceFunctionName
};
try {
const createParams = {
system: cachedSystemPrompt,
model: this.model,
messages: cachedMessages,
max_tokens: forwardedParameters?.maxTokens || 4096,
...forwardedParameters?.temperature ? { temperature: forwardedParameters.temperature } : {},
...tools.length > 0 && { tools },
...toolChoice && { tool_choice: toolChoice },
stream: true
};
const stream = await this.ensureAnthropic().messages.create(createParams);
eventSource.stream(async (eventStream$) => {
let mode = null;
let didOutputText = false;
let currentMessageId = (0, _copilotkit_shared.randomId)();
let currentToolCallId = (0, _copilotkit_shared.randomId)();
let filterThinkingTextBuffer = new FilterThinkingTextBuffer();
let hasReceivedContent = false;
try {
for await (const chunk of stream) if (chunk.type === "message_start") currentMessageId = chunk.message.id;
else if (chunk.type === "content_block_start") {
if (chunk.content_block.type === "text") {
hasReceivedContent = true;
didOutputText = false;
filterThinkingTextBuffer.reset();
mode = "message";
} else if (chunk.content_block.type === "tool_use") {
if (!knownActionNames.has(chunk.content_block.name)) {
mode = null;
continue;
}
hasReceivedContent = true;
currentToolCallId = chunk.content_block.id;
eventStream$.sendActionExecutionStart({
actionExecutionId: currentToolCallId,
actionName: chunk.content_block.name,
parentMessageId: currentMessageId
});
mode = "function";
}
} else if (chunk.type === "content_block_delta") {
if (mode === null) continue;
if (chunk.delta.type === "text_delta") {
const text = filterThinkingTextBuffer.onTextChunk(chunk.delta.text);
if (text.length > 0) {
if (!didOutputText) {
eventStream$.sendTextMessageStart({ messageId: currentMessageId });
didOutputText = true;
}
eventStream$.sendTextMessageContent({
messageId: currentMessageId,
content: text
});
}
} else if (chunk.delta.type === "input_json_delta") eventStream$.sendActionExecutionArgs({
actionExecutionId: currentToolCallId,
args: chunk.delta.partial_json
});
} else if (chunk.type === "content_block_stop") {
if (mode === "message") {
if (didOutputText) eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
} else if (mode === "function") eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
}
} catch (error) {
throw require_error_utils.convertServiceAdapterError(error, "Anthropic");
}
if (!hasReceivedContent && this.shouldGenerateFallbackResponse(cachedMessages)) {
let fallbackContent = "Task completed successfully.";
const lastMessage = cachedMessages[cachedMessages.length - 1];
if (lastMessage?.role === "user" && Array.isArray(lastMessage.content)) {
const toolResult = lastMessage.content.find((c) => c.type === "tool_result");
if (toolResult?.content && toolResult.content !== "Action completed successfully") fallbackContent = toolResult.content;
}
currentMessageId = (0, _copilotkit_shared.randomId)();
eventStream$.sendTextMessageStart({ messageId: currentMessageId });
eventStream$.sendTextMessageContent({
messageId: currentMessageId,
content: fallbackContent
});
eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
}
eventStream$.complete();
});
} catch (error) {
throw require_error_utils.convertServiceAdapterError(error, "Anthropic");
}
return { threadId: threadId || (0, _copilotkit_shared.randomUUID)() };
}
};
const THINKING_TAG = "<thinking>";
const THINKING_TAG_END = "</thinking>";
var FilterThinkingTextBuffer = class {
constructor() {
this.didFilterThinkingTag = false;
this.buffer = "";
}
onTextChunk(text) {
this.buffer += text;
if (this.didFilterThinkingTag) return text;
const potentialTag = this.buffer.slice(0, 10);
if (THINKING_TAG.startsWith(potentialTag)) if (this.buffer.includes(THINKING_TAG_END)) {
const end = this.buffer.indexOf(THINKING_TAG_END);
const filteredText = this.buffer.slice(end + 11);
this.buffer = filteredText;
this.didFilterThinkingTag = true;
return filteredText;
} else return "";
return text;
}
reset() {
this.buffer = "";
this.didFilterThinkingTag = false;
}
};
//#endregion
exports.AnthropicAdapter = AnthropicAdapter;
//# sourceMappingURL=anthropic-adapter.cjs.map