instrument whether an api key is the user's or ours (#33)

introduce an `AnthropicApiKey` type so we aren't passing a string/boolean everywhere.
This commit is contained in:
Chris Toshok 2025-02-19 13:58:40 -08:00 committed by GitHub
parent fabf53b49c
commit 4795d02150
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 18 additions and 9 deletions

View File

@ -6,7 +6,6 @@ import type { FileMap } from './stream-text';
import { StreamingMessageParser } from '~/lib/runtime/message-parser'; import { StreamingMessageParser } from '~/lib/runtime/message-parser';
import { extractRelativePath } from '~/utils/diff'; import { extractRelativePath } from '~/utils/diff';
import { wrapWithSpan, getCurrentSpan } from '~/lib/.server/otel'; import { wrapWithSpan, getCurrentSpan } from '~/lib/.server/otel';
import { context } from '@opentelemetry/api';
const Model = 'claude-3-5-sonnet-20241022'; const Model = 'claude-3-5-sonnet-20241022';
const MaxMessageTokens = 8192; const MaxMessageTokens = 8192;
@ -42,6 +41,10 @@ function flatMessageContent(content: string | ContentBlockParam[]): string {
return "AnthropicUnknownContent"; return "AnthropicUnknownContent";
} }
export interface AnthropicApiKey {
key: string;
isUser: boolean;
}
export interface AnthropicCall { export interface AnthropicCall {
systemPrompt: string; systemPrompt: string;
messages: MessageParam[]; messages: MessageParam[];
@ -60,14 +63,15 @@ const callAnthropic = wrapWithSpan(
}, },
// eslint-disable-next-line prefer-arrow-callback // eslint-disable-next-line prefer-arrow-callback
async function callAnthropic(apiKey: string, systemPrompt: string, messages: MessageParam[]): Promise<AnthropicCall> { async function callAnthropic(apiKey: AnthropicApiKey, systemPrompt: string, messages: MessageParam[]): Promise<AnthropicCall> {
const span = getCurrentSpan(); const span = getCurrentSpan();
span?.setAttributes({ span?.setAttributes({
"llm.chat.calls": 1, // so we can SUM(llm.chat.calls) without doing a COUNT + filter "llm.chat.calls": 1, // so we can SUM(llm.chat.calls) without doing a COUNT + filter
"llm.chat.num_messages": messages.length, "llm.chat.num_messages": messages.length,
"llm.chat.is_user_api_key": apiKey.isUser,
}); });
const anthropic = new Anthropic({ apiKey }); const anthropic = new Anthropic({ apiKey: apiKey.key });
console.log("************************************************"); console.log("************************************************");
console.log("AnthropicMessageSend"); console.log("AnthropicMessageSend");
@ -141,7 +145,7 @@ function shouldRestorePartialFile(existingContent: string, newContent: string):
async function restorePartialFile( async function restorePartialFile(
existingContent: string, existingContent: string,
newContent: string, newContent: string,
apiKey: string, apiKey: AnthropicApiKey,
responseDescription: string responseDescription: string
) { ) {
const systemPrompt = ` const systemPrompt = `
@ -298,7 +302,7 @@ interface FileContents {
content: string; content: string;
} }
async function fixupResponseFiles(files: FileMap, apiKey: string, responseText: string) { async function fixupResponseFiles(files: FileMap, apiKey: AnthropicApiKey, responseText: string) {
const fileContents: FileContents[] = []; const fileContents: FileContents[] = [];
const messageParser = new StreamingMessageParser({ const messageParser = new StreamingMessageParser({
@ -342,7 +346,7 @@ async function fixupResponseFiles(files: FileMap, apiKey: string, responseText:
return { responseText, restoreCalls }; return { responseText, restoreCalls };
} }
export async function chatAnthropic(chatController: ChatStreamController, files: FileMap, apiKey: string, systemPrompt: string, messages: CoreMessage[]) { export async function chatAnthropic(chatController: ChatStreamController, files: FileMap, apiKey: AnthropicApiKey, systemPrompt: string, messages: CoreMessage[]) {
const messageParams: MessageParam[] = []; const messageParams: MessageParam[] = [];
for (const message of messages) { for (const message of messages) {

View File

@ -2,7 +2,7 @@ import { type ActionFunctionArgs } from '@remix-run/cloudflare';
import { ChatStreamController } from '~/utils/chatStreamController'; import { ChatStreamController } from '~/utils/chatStreamController';
import { assert } from '~/lib/replay/ReplayProtocolClient'; import { assert } from '~/lib/replay/ReplayProtocolClient';
import { getStreamTextArguments, type FileMap, type Messages } from '~/lib/.server/llm/stream-text'; import { getStreamTextArguments, type FileMap, type Messages } from '~/lib/.server/llm/stream-text';
import { chatAnthropic } from '~/lib/.server/llm/chat-anthropic'; import { chatAnthropic, type AnthropicApiKey } from '~/lib/.server/llm/chat-anthropic';
import { ensureOpenTelemetryInitialized } from '~/lib/.server/otel'; import { ensureOpenTelemetryInitialized } from '~/lib/.server/otel';
export async function action(args: ActionFunctionArgs) { export async function action(args: ActionFunctionArgs) {
@ -41,11 +41,16 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
promptId, promptId,
}); });
const anthropicApiKey = clientAnthropicApiKey ?? context.cloudflare.env.ANTHROPIC_API_KEY; const apiKey = clientAnthropicApiKey ?? context.cloudflare.env.ANTHROPIC_API_KEY;
if (!anthropicApiKey) { if (!apiKey) {
throw new Error("Anthropic API key is not set"); throw new Error("Anthropic API key is not set");
} }
const anthropicApiKey: AnthropicApiKey = {
key: apiKey,
isUser: !!clientAnthropicApiKey,
};
const resultStream = new ReadableStream({ const resultStream = new ReadableStream({
async start(controller) { async start(controller) {
const chatController = new ChatStreamController(controller); const chatController = new ChatStreamController(controller);