From a555d03c656e58c6bc8328e256d0653f71fbd07e Mon Sep 17 00:00:00 2001 From: cometapii Date: Wed, 5 Nov 2025 21:54:13 +0100 Subject: [PATCH] Switch LLM streaming to OpenAI client --- app/lib/.server/llm/api-key.ts | 12 +- app/lib/.server/llm/constants.ts | 2 +- app/lib/.server/llm/model.ts | 27 +++- app/lib/.server/llm/stream-text.ts | 204 +++++++++++++++++++++-- app/lib/fetch.ts | 2 +- app/lib/hooks/useMessageParser.ts | 2 +- app/lib/stores/theme.ts | 2 +- app/utils/logger.ts | 13 +- package.json | 2 +- pnpm-lock.yaml | 250 +++++++++++++++++++++++++++-- worker-configuration.d.ts | 3 +- 11 files changed, 473 insertions(+), 46 deletions(-) diff --git a/app/lib/.server/llm/api-key.ts b/app/lib/.server/llm/api-key.ts index 863f763673..b117bdba1a 100644 --- a/app/lib/.server/llm/api-key.ts +++ b/app/lib/.server/llm/api-key.ts @@ -5,5 +5,15 @@ export function getAPIKey(cloudflareEnv: Env) { * The `cloudflareEnv` is only used when deployed or when previewing locally. * In development the environment variables are available through `env`. */ - return env.ANTHROPIC_API_KEY || cloudflareEnv.ANTHROPIC_API_KEY; + const apiKey = + env.NVIDIA_API_KEY || + cloudflareEnv.NVIDIA_API_KEY || + env.ANTHROPIC_API_KEY || + cloudflareEnv.ANTHROPIC_API_KEY; + + if (!apiKey) { + throw new Error('Missing NVIDIA_API_KEY environment variable'); + } + + return apiKey; } diff --git a/app/lib/.server/llm/constants.ts b/app/lib/.server/llm/constants.ts index b24acdf272..8bd43847db 100644 --- a/app/lib/.server/llm/constants.ts +++ b/app/lib/.server/llm/constants.ts @@ -1,4 +1,4 @@ -// see https://docs.anthropic.com/en/docs/about-claude/models +// Maximum completion tokens for the configured model. export const MAX_TOKENS = 8192; // limits the number of model responses that can be returned in a single request diff --git a/app/lib/.server/llm/model.ts b/app/lib/.server/llm/model.ts index f0d695c47f..e312b193f6 100644 --- a/app/lib/.server/llm/model.ts +++ b/app/lib/.server/llm/model.ts @@ -1,9 +1,24 @@ -import { createAnthropic } from '@ai-sdk/anthropic'; +import OpenAI from 'openai'; -export function getAnthropicModel(apiKey: string) { - const anthropic = createAnthropic({ - apiKey, - }); +const BASE_URL = 'https://integrate.api.nvidia.com/v1'; +const MODEL_NAME = 'moonshotai/kimi-k2-instruct-0905'; - return anthropic('claude-3-5-sonnet-20240620'); +let cachedClient: { apiKey: string; client: OpenAI } | undefined; + +export function getOpenAIClient(apiKey: string) { + if (!cachedClient || cachedClient.apiKey !== apiKey) { + cachedClient = { + apiKey, + client: new OpenAI({ + apiKey, + baseURL: BASE_URL, + }), + }; + } + + return cachedClient.client; +} + +export function getModelName() { + return MODEL_NAME; } diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index cf937fd00e..ad8263ef8d 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -1,6 +1,11 @@ -import { streamText as _streamText, convertToCoreMessages } from 'ai'; +import { formatStreamPart } from 'ai'; +import type { + ChatCompletionChunk, + ChatCompletionMessageParam, + ChatCompletionToolChoiceOption, +} from 'openai/resources/chat/completions'; import { getAPIKey } from '~/lib/.server/llm/api-key'; -import { getAnthropicModel } from '~/lib/.server/llm/model'; +import { getModelName, getOpenAIClient } from '~/lib/.server/llm/model'; import { MAX_TOKENS } from './constants'; import { getSystemPrompt } from './prompts'; @@ -19,17 +24,188 @@ interface Message { export type Messages = Message[]; -export type StreamingOptions = Omit[0], 'model'>; - -export function streamText(messages: Messages, env: Env, options?: StreamingOptions) { - return _streamText({ - model: getAnthropicModel(getAPIKey(env)), - system: getSystemPrompt(), - maxTokens: MAX_TOKENS, - headers: { - 'anthropic-beta': 'max-tokens-3-5-sonnet-2024-07-15', - }, - messages: convertToCoreMessages(messages), - ...options, +type FinishReason = + | 'stop' + | 'length' + | 'content-filter' + | 'tool-calls' + | 'error' + | 'other' + | 'unknown'; + +export interface StreamingOptions { + temperature?: number; + topP?: number; + toolChoice?: ChatCompletionToolChoiceOption; + onFinish?: (result: { text: string; finishReason: FinishReason }) => void | Promise; +} + +const encoder = new TextEncoder(); + +export async function streamText(messages: Messages, env: Env, options: StreamingOptions = {}) { + const client = getOpenAIClient(getAPIKey(env)); + + const openAIMessages: ChatCompletionMessageParam[] = [ + { role: 'system', content: getSystemPrompt() }, + ...convertMessages(messages), + ]; + + const response = await client.chat.completions.create({ + model: getModelName(), + messages: openAIMessages, + max_tokens: MAX_TOKENS, + temperature: options.temperature ?? 0, + top_p: options.topP ?? 0.9, + stream: true, + stream_options: { include_usage: true }, + tool_choice: options.toolChoice, }); + + return new OpenAIStreamTextResult(response, options.onFinish); +} + +class OpenAIStreamTextResult { + private consumed = false; + + constructor( + private readonly response: AsyncIterable, + private readonly onFinish?: StreamingOptions['onFinish'], + ) {} + + toAIStream() { + if (this.consumed) { + throw new Error('Stream has already been consumed.'); + } + + this.consumed = true; + + return new ReadableStream({ + start: async (controller) => { + let aggregatedText = ''; + let finishReason: string | null = null; + let usage: { promptTokens: number; completionTokens: number } | null = null; + + try { + for await (const chunk of this.response) { + const choice = chunk.choices[0]; + + if (!choice) { + continue; + } + + const textDelta = extractDeltaText(choice.delta); + + if (textDelta.length > 0) { + aggregatedText += textDelta; + controller.enqueue(encoder.encode(formatStreamPart('text', textDelta))); + } + + if (choice.finish_reason) { + finishReason = choice.finish_reason; + } + + if (chunk.usage) { + usage = { + promptTokens: chunk.usage.prompt_tokens ?? 0, + completionTokens: chunk.usage.completion_tokens ?? 0, + }; + } + } + + const normalizedFinishReason = normalizeFinishReason(finishReason); + const usagePayload = + usage ?? { + promptTokens: 0, + completionTokens: 0, + }; + + controller.enqueue( + encoder.encode( + formatStreamPart('finish_message', { + finishReason: normalizedFinishReason, + usage: usagePayload, + }), + ), + ); + + if (this.onFinish) { + await this.onFinish({ + text: aggregatedText, + finishReason: normalizedFinishReason, + }); + } + + controller.close(); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + controller.enqueue(encoder.encode(formatStreamPart('error', JSON.stringify(message)))); + controller.error(error); + } + }, + }); + } +} + +function convertMessages(messages: Messages): ChatCompletionMessageParam[] { + return messages.map((message) => ({ + role: message.role, + content: message.content, + })); +} + +function extractDeltaText( + delta: ChatCompletionChunk['choices'][number]['delta'], +): string { + const content = delta?.content; + + if (!content) { + return ''; + } + + if (typeof content === 'string') { + return content; + } + + if (Array.isArray(content)) { + return (content as Array>) + .map((part) => { + if (typeof part === 'string') { + return part; + } + + if (isTextPart(part)) { + return part.text; + } + + return ''; + }) + .join(''); + } + + return ''; +} + +function normalizeFinishReason(finishReason: string | null): FinishReason { + switch (finishReason) { + case 'stop': + return 'stop'; + case 'length': + return 'length'; + case 'content_filter': + return 'content-filter'; + case 'tool_calls': + case 'function_call': + return 'tool-calls'; + case 'error': + return 'error'; + case null: + case undefined: + return 'unknown'; + default: + return 'other'; + } +} + +function isTextPart(part: Record): part is { text: string } { + return typeof part.text === 'string'; } diff --git a/app/lib/fetch.ts b/app/lib/fetch.ts index 4c5f53d2a7..c667f712e0 100644 --- a/app/lib/fetch.ts +++ b/app/lib/fetch.ts @@ -1,7 +1,7 @@ type CommonRequest = Omit & { body?: URLSearchParams }; export async function request(url: string, init?: CommonRequest) { - if (import.meta.env.DEV) { + if (process.env.NODE_ENV !== 'production' && typeof window === 'undefined') { const nodeFetch = await import('node-fetch'); const https = await import('node:https'); diff --git a/app/lib/hooks/useMessageParser.ts b/app/lib/hooks/useMessageParser.ts index a70fb82f42..44ae7aa0ad 100644 --- a/app/lib/hooks/useMessageParser.ts +++ b/app/lib/hooks/useMessageParser.ts @@ -45,7 +45,7 @@ export function useMessageParser() { const parseMessages = useCallback((messages: Message[], isLoading: boolean) => { let reset = false; - if (import.meta.env.DEV && !isLoading) { + if (process.env.NODE_ENV !== 'production' && !isLoading) { reset = true; messageParser.reset(); } diff --git a/app/lib/stores/theme.ts b/app/lib/stores/theme.ts index 4f3e47bd4d..db1fc91995 100644 --- a/app/lib/stores/theme.ts +++ b/app/lib/stores/theme.ts @@ -13,7 +13,7 @@ export const DEFAULT_THEME = 'light'; export const themeStore = atom(initStore()); function initStore() { - if (!import.meta.env.SSR) { + if (typeof window !== 'undefined') { const persistedTheme = localStorage.getItem(kTheme) as Theme | undefined; const themeAttribute = document.querySelector('html')?.getAttribute('data-theme'); diff --git a/app/utils/logger.ts b/app/utils/logger.ts index 1a5c932c5c..2e8269c3b2 100644 --- a/app/utils/logger.ts +++ b/app/utils/logger.ts @@ -11,7 +11,16 @@ interface Logger { setLevel: (level: DebugLevel) => void; } -let currentLevel: DebugLevel = import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV ? 'debug' : 'info'; +const DEBUG_LEVELS: DebugLevel[] = ['trace', 'debug', 'info', 'warn', 'error']; +const isProduction = process.env.NODE_ENV === 'production'; +const envDebugLevel = + (process.env.NEXT_PUBLIC_LOG_LEVEL as DebugLevel | undefined) ?? + (process.env.LOG_LEVEL as DebugLevel | undefined); +const resolvedEnvLevel = envDebugLevel && DEBUG_LEVELS.includes(envDebugLevel as DebugLevel) + ? (envDebugLevel as DebugLevel) + : undefined; + +let currentLevel: DebugLevel = resolvedEnvLevel ?? (isProduction ? 'info' : 'debug'); const isWorker = 'HTMLRewriter' in globalThis; const supportsColor = !isWorker; @@ -37,7 +46,7 @@ export function createScopedLogger(scope: string): Logger { } function setLevel(level: DebugLevel) { - if ((level === 'trace' || level === 'debug') && import.meta.env.PROD) { + if ((level === 'trace' || level === 'debug') && isProduction) { return; } diff --git a/package.json b/package.json index 5583455603..4ed40a1859 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "node": ">=18.18.0" }, "dependencies": { - "@ai-sdk/anthropic": "^0.0.39", "@codemirror/autocomplete": "^6.17.0", "@codemirror/commands": "^6.6.0", "@codemirror/lang-cpp": "^6.0.2", @@ -74,6 +73,7 @@ "remix-island": "^0.2.0", "remix-utils": "^7.6.0", "shiki": "^1.9.1", + "openai": "^4.56.0", "unist-util-visit": "^5.0.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0896b714b7..1a7f489ec0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: .: dependencies: - '@ai-sdk/anthropic': - specifier: ^0.0.39 - version: 0.0.39(zod@3.23.8) '@codemirror/autocomplete': specifier: ^6.17.0 version: 6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.28.4)(@lezer/common@1.2.1) @@ -106,7 +103,7 @@ importers: version: 5.5.0 ai: specifier: ^3.3.4 - version: 3.3.4(react@18.3.1)(sswr@2.1.0(svelte@4.2.18))(svelte@4.2.18)(vue@3.4.30(typescript@5.5.2))(zod@3.23.8) + version: 3.3.4(openai@4.104.0(ws@8.18.0)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.18))(svelte@4.2.18)(vue@3.4.30(typescript@5.5.2))(zod@3.23.8) date-fns: specifier: ^3.6.0 version: 3.6.0 @@ -128,6 +125,9 @@ importers: nanostores: specifier: ^0.10.3 version: 0.10.3 + openai: + specifier: ^4.56.0 + version: 4.104.0(ws@8.18.0)(zod@3.23.8) react: specifier: ^18.2.0 version: 18.3.1 @@ -231,12 +231,6 @@ importers: packages: - '@ai-sdk/anthropic@0.0.39': - resolution: {integrity: sha512-Ouku41O9ebyRi0EUW7pB8+lk4sI74SfJKydzK7FjynhNmCSvi42+U4WPlEjP64NluXUzpkYLvBa6BAd36VY4/g==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.0.0 - '@ai-sdk/provider-utils@1.0.9': resolution: {integrity: sha512-yfdanjUiCJbtGoRGXrcrmXn0pTyDfRIeY6ozDG96D66f2wupZaZvAgKptUa3zDYXtUCQQvcNJ+tipBBfQD/UYA==} engines: {node: '>=18'} @@ -1723,9 +1717,15 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + '@types/node@20.14.9': resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==} @@ -2000,6 +2000,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -2093,6 +2097,9 @@ packages: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} hasBin: true + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2208,6 +2215,10 @@ packages: resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} @@ -2317,6 +2328,10 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -2485,6 +2500,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2535,6 +2554,10 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -2581,6 +2604,10 @@ packages: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} @@ -2588,6 +2615,14 @@ packages: es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild-plugins-node-modules-polyfill@1.6.4: resolution: {integrity: sha512-x3MCOvZrKDGAfqAYS/pZUUSwiN+XH7x84A+Prup0CZBJKuGfuGkTAC4g01D6JPs/GCM9wzZVfd8bmiy+cP/iXA==} engines: {node: '>=14.0.0'} @@ -2833,10 +2868,21 @@ packages: resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -2900,6 +2946,10 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} @@ -2908,6 +2958,10 @@ packages: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-source@2.0.12: resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} @@ -2957,6 +3011,10 @@ packages: gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -2990,6 +3048,10 @@ packages: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} @@ -3067,6 +3129,9 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -3382,6 +3447,10 @@ packages: markdown-table@3.0.3: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + md5.js@1.3.5: resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} @@ -3817,6 +3886,15 @@ packages: node-fetch-native@1.6.4: resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3906,6 +3984,18 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + openai@4.104.0: + resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -4738,6 +4828,9 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -5085,6 +5178,16 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-typed-array@1.1.15: resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} @@ -5196,12 +5299,6 @@ packages: snapshots: - '@ai-sdk/anthropic@0.0.39(zod@3.23.8)': - dependencies: - '@ai-sdk/provider': 0.0.17 - '@ai-sdk/provider-utils': 1.0.9(zod@3.23.8) - zod: 3.23.8 - '@ai-sdk/provider-utils@1.0.9(zod@3.23.8)': dependencies: '@ai-sdk/provider': 0.0.17 @@ -6731,10 +6828,19 @@ snapshots: '@types/ms@0.7.34': {} + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 20.14.9 + form-data: 4.0.4 + '@types/node-forge@1.3.11': dependencies: '@types/node': 20.14.9 + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + '@types/node@20.14.9': dependencies: undici-types: 5.26.5 @@ -7172,12 +7278,16 @@ snapshots: acorn@8.12.0: {} + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@3.3.4(react@18.3.1)(sswr@2.1.0(svelte@4.2.18))(svelte@4.2.18)(vue@3.4.30(typescript@5.5.2))(zod@3.23.8): + ai@3.3.4(openai@4.104.0(ws@8.18.0)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.18))(svelte@4.2.18)(vue@3.4.30(typescript@5.5.2))(zod@3.23.8): dependencies: '@ai-sdk/provider': 0.0.17 '@ai-sdk/provider-utils': 1.0.9(zod@3.23.8) @@ -7194,6 +7304,7 @@ snapshots: secure-json-parse: 2.7.0 zod-to-json-schema: 3.22.5(zod@3.23.8) optionalDependencies: + openai: 4.104.0(ws@8.18.0)(zod@3.23.8) react: 18.3.1 sswr: 2.1.0(svelte@4.2.18) svelte: 4.2.18 @@ -7268,6 +7379,8 @@ snapshots: astring@1.8.6: {} + asynckit@0.4.0: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 @@ -7429,6 +7542,11 @@ snapshots: tar: 6.2.1 unique-filename: 3.0.0 + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 @@ -7540,6 +7658,10 @@ snapshots: colorette@2.0.20: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} common-tags@1.8.2: {} @@ -7695,6 +7817,8 @@ snapshots: defu@6.1.4: {} + delayed-stream@1.0.0: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -7734,6 +7858,12 @@ snapshots: dotenv@16.4.5: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + duplexer@0.1.2: {} duplexify@3.7.1: @@ -7781,10 +7911,23 @@ snapshots: dependencies: get-intrinsic: 1.2.4 + es-define-property@1.0.1: {} + es-errors@1.3.0: {} es-module-lexer@1.5.4: {} + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild-plugins-node-modules-polyfill@1.6.4(esbuild@0.17.6): dependencies: '@jspm/core': 2.0.1 @@ -8171,8 +8314,23 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + form-data-encoder@1.7.2: {} + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + format@0.2.2: {} + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 @@ -8225,10 +8383,28 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} get-port@5.1.1: {} + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-source@2.0.12: dependencies: data-uri-to-buffer: 2.0.2 @@ -8278,6 +8454,8 @@ snapshots: dependencies: get-intrinsic: 1.2.4 + gopd@1.2.0: {} + graceful-fs@4.2.11: {} graphemer@1.4.0: {} @@ -8307,6 +8485,8 @@ snapshots: has-symbols@1.0.3: {} + has-symbols@1.1.0: {} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 @@ -8460,6 +8640,10 @@ snapshots: human-signals@5.0.0: {} + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -8711,6 +8895,8 @@ snapshots: markdown-table@3.0.3: {} + math-intrinsics@1.1.0: {} + md5.js@1.3.5: dependencies: hash-base: 3.1.0 @@ -9533,6 +9719,10 @@ snapshots: node-fetch-native@1.6.4: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 @@ -9656,6 +9846,21 @@ snapshots: dependencies: mimic-fn: 4.0.0 + openai@4.104.0(ws@8.18.0)(zod@3.23.8): + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + optionalDependencies: + ws: 8.18.0 + zod: 3.23.8 + transitivePeerDependencies: + - encoding + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -10563,6 +10768,8 @@ snapshots: totalist@3.0.1: {} + tr46@0.0.3: {} + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -10977,6 +11184,15 @@ snapshots: web-streams-polyfill@3.3.3: {} + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 606a4e521c..bd7f83615f 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,3 +1,4 @@ interface Env { - ANTHROPIC_API_KEY: string; + NVIDIA_API_KEY: string; + ANTHROPIC_API_KEY?: string; }