@providerprotocol/ai 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +84 -0
- package/dist/anthropic/index.d.ts +41 -0
- package/dist/anthropic/index.js +500 -0
- package/dist/anthropic/index.js.map +1 -0
- package/dist/chunk-CUCRF5W6.js +136 -0
- package/dist/chunk-CUCRF5W6.js.map +1 -0
- package/dist/chunk-FTFX2VET.js +424 -0
- package/dist/chunk-FTFX2VET.js.map +1 -0
- package/dist/chunk-QUUX4G7U.js +117 -0
- package/dist/chunk-QUUX4G7U.js.map +1 -0
- package/dist/chunk-Y6Q7JCNP.js +39 -0
- package/dist/chunk-Y6Q7JCNP.js.map +1 -0
- package/dist/google/index.d.ts +69 -0
- package/dist/google/index.js +517 -0
- package/dist/google/index.js.map +1 -0
- package/dist/http/index.d.ts +61 -0
- package/dist/http/index.js +43 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +792 -0
- package/dist/index.js +898 -0
- package/dist/index.js.map +1 -0
- package/dist/openai/index.d.ts +204 -0
- package/dist/openai/index.js +1340 -0
- package/dist/openai/index.js.map +1 -0
- package/dist/provider-CUJWjgNl.d.ts +192 -0
- package/dist/retry-I2661_rv.d.ts +118 -0
- package/package.json +88 -0
- package/src/anthropic/index.ts +3 -0
- package/src/core/image.ts +188 -0
- package/src/core/llm.ts +619 -0
- package/src/core/provider.ts +92 -0
- package/src/google/index.ts +3 -0
- package/src/http/errors.ts +112 -0
- package/src/http/fetch.ts +210 -0
- package/src/http/index.ts +31 -0
- package/src/http/keys.ts +136 -0
- package/src/http/retry.ts +205 -0
- package/src/http/sse.ts +136 -0
- package/src/index.ts +32 -0
- package/src/openai/index.ts +9 -0
- package/src/providers/anthropic/index.ts +17 -0
- package/src/providers/anthropic/llm.ts +196 -0
- package/src/providers/anthropic/transform.ts +452 -0
- package/src/providers/anthropic/types.ts +213 -0
- package/src/providers/google/index.ts +17 -0
- package/src/providers/google/llm.ts +203 -0
- package/src/providers/google/transform.ts +487 -0
- package/src/providers/google/types.ts +214 -0
- package/src/providers/openai/index.ts +151 -0
- package/src/providers/openai/llm.completions.ts +201 -0
- package/src/providers/openai/llm.responses.ts +211 -0
- package/src/providers/openai/transform.completions.ts +628 -0
- package/src/providers/openai/transform.responses.ts +718 -0
- package/src/providers/openai/types.ts +711 -0
- package/src/types/content.ts +133 -0
- package/src/types/errors.ts +85 -0
- package/src/types/index.ts +105 -0
- package/src/types/llm.ts +211 -0
- package/src/types/messages.ts +182 -0
- package/src/types/provider.ts +195 -0
- package/src/types/schema.ts +58 -0
- package/src/types/stream.ts +146 -0
- package/src/types/thread.ts +226 -0
- package/src/types/tool.ts +88 -0
- package/src/types/turn.ts +118 -0
- package/src/utils/id.ts +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/providers/anthropic/transform.ts","../../src/providers/anthropic/llm.ts","../../src/providers/anthropic/index.ts"],"sourcesContent":["import type { LLMRequest, LLMResponse } from '../../types/llm.ts';\nimport type { Message } from '../../types/messages.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { Tool, ToolCall } from '../../types/tool.ts';\nimport type { TokenUsage } from '../../types/turn.ts';\nimport type { ContentBlock, TextBlock, ImageBlock } from '../../types/content.ts';\nimport {\n AssistantMessage,\n UserMessage,\n ToolResultMessage,\n isUserMessage,\n isAssistantMessage,\n isToolResultMessage,\n} from '../../types/messages.ts';\nimport type {\n AnthropicLLMParams,\n AnthropicRequest,\n AnthropicMessage,\n AnthropicContent,\n AnthropicTool,\n AnthropicResponse,\n AnthropicStreamEvent,\n AnthropicContentBlockDeltaEvent,\n} from './types.ts';\n\n/**\n * Transform UPP request to Anthropic format\n */\nexport function transformRequest<TParams extends AnthropicLLMParams>(\n request: LLMRequest<TParams>,\n modelId: string\n): AnthropicRequest {\n const params = (request.params ?? { max_tokens: 4096 }) as AnthropicLLMParams;\n\n const anthropicRequest: AnthropicRequest = {\n model: modelId,\n max_tokens: params.max_tokens ?? 4096,\n messages: request.messages.map(transformMessage),\n };\n\n // System prompt (top-level in Anthropic)\n if (request.system) {\n anthropicRequest.system = request.system;\n }\n\n // Model parameters\n if (params.temperature !== undefined) {\n anthropicRequest.temperature = params.temperature;\n }\n if (params.top_p !== undefined) {\n anthropicRequest.top_p = params.top_p;\n }\n if (params.top_k !== undefined) {\n anthropicRequest.top_k = params.top_k;\n }\n if (params.stop_sequences) {\n anthropicRequest.stop_sequences = params.stop_sequences;\n }\n if (params.metadata) {\n anthropicRequest.metadata = params.metadata;\n }\n if (params.thinking) {\n anthropicRequest.thinking = params.thinking;\n }\n if (params.service_tier !== undefined) {\n anthropicRequest.service_tier = params.service_tier;\n }\n\n // Tools\n if (request.tools && request.tools.length > 0) {\n anthropicRequest.tools = request.tools.map(transformTool);\n anthropicRequest.tool_choice = { type: 'auto' };\n }\n\n // Structured output via tool-based approach\n // Anthropic doesn't have native structured output, so we use a tool to enforce the schema\n if (request.structure) {\n const structuredTool: AnthropicTool = {\n name: 'json_response',\n description: 'Return the response in the specified JSON format. You MUST use this tool to provide your response.',\n input_schema: {\n type: 'object',\n properties: request.structure.properties,\n required: request.structure.required,\n },\n };\n\n // Add the structured output tool (may coexist with user tools)\n anthropicRequest.tools = [...(anthropicRequest.tools ?? []), structuredTool];\n // Force the model to use the json_response tool\n anthropicRequest.tool_choice = { type: 'tool', name: 'json_response' };\n }\n\n return anthropicRequest;\n}\n\n/**\n * Filter to only valid content blocks with a type property\n */\nfunction filterValidContent<T extends { type?: string }>(content: T[]): T[] {\n return content.filter((c) => c && typeof c.type === 'string');\n}\n\n/**\n * Transform a UPP Message to Anthropic format\n */\nfunction transformMessage(message: Message): AnthropicMessage {\n if (isUserMessage(message)) {\n const validContent = filterValidContent(message.content);\n return {\n role: 'user',\n content: validContent.map(transformContentBlock),\n };\n }\n\n if (isAssistantMessage(message)) {\n const validContent = filterValidContent(message.content);\n const content: AnthropicContent[] = validContent.map(transformContentBlock);\n\n // Add tool calls as tool_use content blocks\n if (message.toolCalls) {\n for (const call of message.toolCalls) {\n content.push({\n type: 'tool_use',\n id: call.toolCallId,\n name: call.toolName,\n input: call.arguments,\n });\n }\n }\n\n return {\n role: 'assistant',\n content,\n };\n }\n\n if (isToolResultMessage(message)) {\n // Tool results are sent as user messages with tool_result content\n return {\n role: 'user',\n content: message.results.map((result) => ({\n type: 'tool_result' as const,\n tool_use_id: result.toolCallId,\n content:\n typeof result.result === 'string'\n ? result.result\n : JSON.stringify(result.result),\n is_error: result.isError,\n })),\n };\n }\n\n throw new Error(`Unknown message type: ${message.type}`);\n}\n\n/**\n * Transform a content block to Anthropic format\n */\nfunction transformContentBlock(block: ContentBlock): AnthropicContent {\n switch (block.type) {\n case 'text':\n return { type: 'text', text: block.text };\n\n case 'image': {\n const imageBlock = block as ImageBlock;\n if (imageBlock.source.type === 'base64') {\n return {\n type: 'image',\n source: {\n type: 'base64',\n media_type: imageBlock.mimeType,\n data: imageBlock.source.data,\n },\n };\n }\n if (imageBlock.source.type === 'url') {\n return {\n type: 'image',\n source: {\n type: 'url',\n url: imageBlock.source.url,\n },\n };\n }\n if (imageBlock.source.type === 'bytes') {\n // Convert bytes to base64\n const base64 = btoa(\n Array.from(imageBlock.source.data)\n .map((b) => String.fromCharCode(b))\n .join('')\n );\n return {\n type: 'image',\n source: {\n type: 'base64',\n media_type: imageBlock.mimeType,\n data: base64,\n },\n };\n }\n throw new Error(`Unknown image source type`);\n }\n\n default:\n throw new Error(`Unsupported content type: ${block.type}`);\n }\n}\n\n/**\n * Transform a UPP Tool to Anthropic format\n */\nfunction transformTool(tool: Tool): AnthropicTool {\n return {\n name: tool.name,\n description: tool.description,\n input_schema: {\n type: 'object',\n properties: tool.parameters.properties,\n required: tool.parameters.required,\n },\n };\n}\n\n/**\n * Transform Anthropic response to UPP LLMResponse\n */\nexport function transformResponse(data: AnthropicResponse): LLMResponse {\n // Extract text content\n const textContent: TextBlock[] = [];\n const toolCalls: ToolCall[] = [];\n let structuredData: unknown;\n\n for (const block of data.content) {\n if (block.type === 'text') {\n textContent.push({ type: 'text', text: block.text });\n } else if (block.type === 'tool_use') {\n // Check if this is the json_response tool (structured output)\n if (block.name === 'json_response') {\n // Extract structured data from tool arguments\n structuredData = block.input;\n }\n toolCalls.push({\n toolCallId: block.id,\n toolName: block.name,\n arguments: block.input,\n });\n }\n // Skip thinking blocks for now\n }\n\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: data.id,\n metadata: {\n anthropic: {\n stop_reason: data.stop_reason,\n stop_sequence: data.stop_sequence,\n model: data.model,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: data.usage.input_tokens,\n outputTokens: data.usage.output_tokens,\n totalTokens: data.usage.input_tokens + data.usage.output_tokens,\n };\n\n return {\n message,\n usage,\n stopReason: data.stop_reason ?? 'end_turn',\n data: structuredData,\n };\n}\n\n/**\n * State for accumulating streaming response\n */\nexport interface StreamState {\n messageId: string;\n model: string;\n content: Array<{ type: string; text?: string; id?: string; name?: string; input?: string }>;\n stopReason: string | null;\n inputTokens: number;\n outputTokens: number;\n}\n\n/**\n * Create initial stream state\n */\nexport function createStreamState(): StreamState {\n return {\n messageId: '',\n model: '',\n content: [],\n stopReason: null,\n inputTokens: 0,\n outputTokens: 0,\n };\n}\n\n/**\n * Transform Anthropic stream event to UPP StreamEvent\n * Returns null for events that don't produce UPP events\n */\nexport function transformStreamEvent(\n event: AnthropicStreamEvent,\n state: StreamState\n): StreamEvent | null {\n switch (event.type) {\n case 'message_start':\n state.messageId = event.message.id;\n state.model = event.message.model;\n state.inputTokens = event.message.usage.input_tokens;\n return { type: 'message_start', index: 0, delta: {} };\n\n case 'content_block_start':\n // Initialize content block\n if (event.content_block.type === 'text') {\n state.content[event.index] = { type: 'text', text: '' };\n } else if (event.content_block.type === 'tool_use') {\n state.content[event.index] = {\n type: 'tool_use',\n id: event.content_block.id,\n name: event.content_block.name,\n input: '',\n };\n }\n return { type: 'content_block_start', index: event.index, delta: {} };\n\n case 'content_block_delta': {\n const delta = event.delta;\n if (delta.type === 'text_delta') {\n if (state.content[event.index]) {\n state.content[event.index]!.text =\n (state.content[event.index]!.text ?? '') + delta.text;\n }\n return {\n type: 'text_delta',\n index: event.index,\n delta: { text: delta.text },\n };\n }\n if (delta.type === 'input_json_delta') {\n if (state.content[event.index]) {\n state.content[event.index]!.input =\n (state.content[event.index]!.input ?? '') + delta.partial_json;\n }\n return {\n type: 'tool_call_delta',\n index: event.index,\n delta: {\n argumentsJson: delta.partial_json,\n toolCallId: state.content[event.index]?.id,\n toolName: state.content[event.index]?.name,\n },\n };\n }\n if (delta.type === 'thinking_delta') {\n return {\n type: 'reasoning_delta',\n index: event.index,\n delta: { text: delta.thinking },\n };\n }\n return null;\n }\n\n case 'content_block_stop':\n return { type: 'content_block_stop', index: event.index, delta: {} };\n\n case 'message_delta':\n state.stopReason = event.delta.stop_reason;\n state.outputTokens = event.usage.output_tokens;\n return null;\n\n case 'message_stop':\n return { type: 'message_stop', index: 0, delta: {} };\n\n case 'ping':\n case 'error':\n return null;\n\n default:\n return null;\n }\n}\n\n/**\n * Build LLMResponse from accumulated stream state\n */\nexport function buildResponseFromState(state: StreamState): LLMResponse {\n const textContent: TextBlock[] = [];\n const toolCalls: ToolCall[] = [];\n let structuredData: unknown;\n\n for (const block of state.content) {\n if (block.type === 'text' && block.text) {\n textContent.push({ type: 'text', text: block.text });\n } else if (block.type === 'tool_use' && block.id && block.name) {\n let args: Record<string, unknown> = {};\n if (block.input) {\n try {\n args = JSON.parse(block.input);\n } catch {\n // Invalid JSON - use empty object\n }\n }\n // Check if this is the json_response tool (structured output)\n if (block.name === 'json_response') {\n structuredData = args;\n }\n toolCalls.push({\n toolCallId: block.id,\n toolName: block.name,\n arguments: args,\n });\n }\n }\n\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: state.messageId,\n metadata: {\n anthropic: {\n stop_reason: state.stopReason,\n model: state.model,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.inputTokens + state.outputTokens,\n };\n\n return {\n message,\n usage,\n stopReason: state.stopReason ?? 'end_turn',\n data: structuredData,\n };\n}\n","import type { LLMHandler, BoundLLMModel, LLMRequest, LLMResponse, LLMStreamResult, LLMCapabilities } from '../../types/llm.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { LLMProvider } from '../../types/provider.ts';\nimport { UPPError } from '../../types/errors.ts';\nimport { resolveApiKey } from '../../http/keys.ts';\nimport { doFetch, doStreamFetch } from '../../http/fetch.ts';\nimport { parseSSEStream } from '../../http/sse.ts';\nimport { normalizeHttpError } from '../../http/errors.ts';\nimport type { AnthropicLLMParams, AnthropicResponse, AnthropicStreamEvent } from './types.ts';\nimport {\n transformRequest,\n transformResponse,\n transformStreamEvent,\n createStreamState,\n buildResponseFromState,\n} from './transform.ts';\n\nconst ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';\nconst ANTHROPIC_VERSION = '2023-06-01';\n\n/**\n * Anthropic API capabilities\n */\nconst ANTHROPIC_CAPABILITIES: LLMCapabilities = {\n streaming: true,\n tools: true,\n structuredOutput: true,\n imageInput: true,\n videoInput: false,\n audioInput: false,\n};\n\n/**\n * Create Anthropic LLM handler\n */\nexport function createLLMHandler(): LLMHandler<AnthropicLLMParams> {\n // Provider reference injected by createProvider() after construction\n let providerRef: LLMProvider<AnthropicLLMParams> | null = null;\n\n return {\n _setProvider(provider: LLMProvider<AnthropicLLMParams>) {\n providerRef = provider;\n },\n\n bind(modelId: string): BoundLLMModel<AnthropicLLMParams> {\n // Use the injected provider reference (set by createProvider)\n if (!providerRef) {\n throw new UPPError(\n 'Provider reference not set. Handler must be used with createProvider().',\n 'INVALID_REQUEST',\n 'anthropic',\n 'llm'\n );\n }\n\n const model: BoundLLMModel<AnthropicLLMParams> = {\n modelId,\n capabilities: ANTHROPIC_CAPABILITIES,\n\n get provider(): LLMProvider<AnthropicLLMParams> {\n return providerRef!;\n },\n\n async complete(request: LLMRequest<AnthropicLLMParams>): Promise<LLMResponse> {\n const apiKey = await resolveApiKey(\n request.config,\n 'ANTHROPIC_API_KEY',\n 'anthropic',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? ANTHROPIC_API_URL;\n const body = transformRequest(request, modelId);\n\n const response = await doFetch(\n baseUrl,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': request.config.apiVersion ?? ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'anthropic',\n 'llm'\n );\n\n const data = (await response.json()) as AnthropicResponse;\n return transformResponse(data);\n },\n\n stream(request: LLMRequest<AnthropicLLMParams>): LLMStreamResult {\n const state = createStreamState();\n let responseResolve: (value: LLMResponse) => void;\n let responseReject: (error: Error) => void;\n\n const responsePromise = new Promise<LLMResponse>((resolve, reject) => {\n responseResolve = resolve;\n responseReject = reject;\n });\n\n async function* generateEvents(): AsyncGenerator<StreamEvent, void, unknown> {\n try {\n const apiKey = await resolveApiKey(\n request.config,\n 'ANTHROPIC_API_KEY',\n 'anthropic',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? ANTHROPIC_API_URL;\n const body = transformRequest(request, modelId);\n body.stream = true;\n\n const response = await doStreamFetch(\n baseUrl,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': request.config.apiVersion ?? ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'anthropic',\n 'llm'\n );\n\n if (!response.ok) {\n const error = await normalizeHttpError(response, 'anthropic', 'llm');\n responseReject(error);\n throw error;\n }\n\n if (!response.body) {\n const error = new UPPError(\n 'No response body for streaming request',\n 'PROVIDER_ERROR',\n 'anthropic',\n 'llm'\n );\n responseReject(error);\n throw error;\n }\n\n for await (const data of parseSSEStream(response.body)) {\n // Check for Anthropic error event\n if (typeof data === 'object' && data !== null && 'type' in data) {\n const event = data as AnthropicStreamEvent;\n\n if (event.type === 'error') {\n const error = new UPPError(\n event.error.message,\n 'PROVIDER_ERROR',\n 'anthropic',\n 'llm'\n );\n responseReject(error);\n throw error;\n }\n\n const uppEvent = transformStreamEvent(event, state);\n if (uppEvent) {\n yield uppEvent;\n }\n }\n }\n\n // Build final response\n responseResolve(buildResponseFromState(state));\n } catch (error) {\n responseReject(error as Error);\n throw error;\n }\n }\n\n return {\n [Symbol.asyncIterator]() {\n return generateEvents();\n },\n response: responsePromise,\n };\n },\n };\n\n return model;\n },\n };\n}\n","import { createProvider } from '../../core/provider.ts';\nimport { createLLMHandler } from './llm.ts';\n\n/**\n * Anthropic provider\n * Supports LLM modality with Claude models\n */\nexport const anthropic = createProvider({\n name: 'anthropic',\n version: '1.0.0',\n modalities: {\n llm: createLLMHandler(),\n },\n});\n\n// Re-export types\nexport type { AnthropicLLMParams } from './types.ts';\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// src/http/retry.ts
|
|
2
|
+
var ExponentialBackoff = class {
|
|
3
|
+
maxAttempts;
|
|
4
|
+
baseDelay;
|
|
5
|
+
maxDelay;
|
|
6
|
+
jitter;
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.maxAttempts = options.maxAttempts ?? 3;
|
|
9
|
+
this.baseDelay = options.baseDelay ?? 1e3;
|
|
10
|
+
this.maxDelay = options.maxDelay ?? 3e4;
|
|
11
|
+
this.jitter = options.jitter ?? true;
|
|
12
|
+
}
|
|
13
|
+
onRetry(error, attempt) {
|
|
14
|
+
if (attempt > this.maxAttempts) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
if (!this.isRetryable(error)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
let delay = this.baseDelay * Math.pow(2, attempt - 1);
|
|
21
|
+
delay = Math.min(delay, this.maxDelay);
|
|
22
|
+
if (this.jitter) {
|
|
23
|
+
delay = delay * (0.5 + Math.random());
|
|
24
|
+
}
|
|
25
|
+
return Math.floor(delay);
|
|
26
|
+
}
|
|
27
|
+
isRetryable(error) {
|
|
28
|
+
return error.code === "RATE_LIMITED" || error.code === "NETWORK_ERROR" || error.code === "TIMEOUT" || error.code === "PROVIDER_ERROR";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var LinearBackoff = class {
|
|
32
|
+
maxAttempts;
|
|
33
|
+
delay;
|
|
34
|
+
constructor(options = {}) {
|
|
35
|
+
this.maxAttempts = options.maxAttempts ?? 3;
|
|
36
|
+
this.delay = options.delay ?? 1e3;
|
|
37
|
+
}
|
|
38
|
+
onRetry(error, attempt) {
|
|
39
|
+
if (attempt > this.maxAttempts) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (!this.isRetryable(error)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return this.delay * attempt;
|
|
46
|
+
}
|
|
47
|
+
isRetryable(error) {
|
|
48
|
+
return error.code === "RATE_LIMITED" || error.code === "NETWORK_ERROR" || error.code === "TIMEOUT" || error.code === "PROVIDER_ERROR";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var NoRetry = class {
|
|
52
|
+
onRetry() {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var TokenBucket = class {
|
|
57
|
+
tokens;
|
|
58
|
+
maxTokens;
|
|
59
|
+
refillRate;
|
|
60
|
+
// tokens per second
|
|
61
|
+
lastRefill;
|
|
62
|
+
maxAttempts;
|
|
63
|
+
constructor(options = {}) {
|
|
64
|
+
this.maxTokens = options.maxTokens ?? 10;
|
|
65
|
+
this.refillRate = options.refillRate ?? 1;
|
|
66
|
+
this.maxAttempts = options.maxAttempts ?? 3;
|
|
67
|
+
this.tokens = this.maxTokens;
|
|
68
|
+
this.lastRefill = Date.now();
|
|
69
|
+
}
|
|
70
|
+
beforeRequest() {
|
|
71
|
+
this.refill();
|
|
72
|
+
if (this.tokens >= 1) {
|
|
73
|
+
this.tokens -= 1;
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
const msPerToken = 1e3 / this.refillRate;
|
|
77
|
+
return Math.ceil(msPerToken);
|
|
78
|
+
}
|
|
79
|
+
onRetry(error, attempt) {
|
|
80
|
+
if (attempt > this.maxAttempts) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if (error.code !== "RATE_LIMITED") {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const msPerToken = 1e3 / this.refillRate;
|
|
87
|
+
return Math.ceil(msPerToken * 2);
|
|
88
|
+
}
|
|
89
|
+
reset() {
|
|
90
|
+
this.tokens = this.maxTokens;
|
|
91
|
+
this.lastRefill = Date.now();
|
|
92
|
+
}
|
|
93
|
+
refill() {
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
const elapsed = (now - this.lastRefill) / 1e3;
|
|
96
|
+
const newTokens = elapsed * this.refillRate;
|
|
97
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);
|
|
98
|
+
this.lastRefill = now;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var RetryAfterStrategy = class {
|
|
102
|
+
maxAttempts;
|
|
103
|
+
fallbackDelay;
|
|
104
|
+
lastRetryAfter;
|
|
105
|
+
constructor(options = {}) {
|
|
106
|
+
this.maxAttempts = options.maxAttempts ?? 3;
|
|
107
|
+
this.fallbackDelay = options.fallbackDelay ?? 5e3;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Set the Retry-After value from response headers
|
|
111
|
+
* Call this before onRetry when you have a Retry-After header
|
|
112
|
+
*/
|
|
113
|
+
setRetryAfter(seconds) {
|
|
114
|
+
this.lastRetryAfter = seconds * 1e3;
|
|
115
|
+
}
|
|
116
|
+
onRetry(error, attempt) {
|
|
117
|
+
if (attempt > this.maxAttempts) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
if (error.code !== "RATE_LIMITED") {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const delay = this.lastRetryAfter ?? this.fallbackDelay;
|
|
124
|
+
this.lastRetryAfter = void 0;
|
|
125
|
+
return delay;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export {
|
|
130
|
+
ExponentialBackoff,
|
|
131
|
+
LinearBackoff,
|
|
132
|
+
NoRetry,
|
|
133
|
+
TokenBucket,
|
|
134
|
+
RetryAfterStrategy
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=chunk-CUCRF5W6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/http/retry.ts"],"sourcesContent":["import type { RetryStrategy } from '../types/provider.ts';\nimport type { UPPError } from '../types/errors.ts';\n\n/**\n * Exponential backoff retry strategy\n */\nexport class ExponentialBackoff implements RetryStrategy {\n private maxAttempts: number;\n private baseDelay: number;\n private maxDelay: number;\n private jitter: boolean;\n\n constructor(options: {\n maxAttempts?: number;\n baseDelay?: number;\n maxDelay?: number;\n jitter?: boolean;\n } = {}) {\n this.maxAttempts = options.maxAttempts ?? 3;\n this.baseDelay = options.baseDelay ?? 1000;\n this.maxDelay = options.maxDelay ?? 30000;\n this.jitter = options.jitter ?? true;\n }\n\n onRetry(error: UPPError, attempt: number): number | null {\n if (attempt > this.maxAttempts) {\n return null;\n }\n\n // Only retry on retryable errors\n if (!this.isRetryable(error)) {\n return null;\n }\n\n // Calculate delay with exponential backoff\n let delay = this.baseDelay * Math.pow(2, attempt - 1);\n delay = Math.min(delay, this.maxDelay);\n\n // Add jitter\n if (this.jitter) {\n delay = delay * (0.5 + Math.random());\n }\n\n return Math.floor(delay);\n }\n\n private isRetryable(error: UPPError): boolean {\n return (\n error.code === 'RATE_LIMITED' ||\n error.code === 'NETWORK_ERROR' ||\n error.code === 'TIMEOUT' ||\n error.code === 'PROVIDER_ERROR'\n );\n }\n}\n\n/**\n * Linear backoff retry strategy\n */\nexport class LinearBackoff implements RetryStrategy {\n private maxAttempts: number;\n private delay: number;\n\n constructor(options: {\n maxAttempts?: number;\n delay?: number;\n } = {}) {\n this.maxAttempts = options.maxAttempts ?? 3;\n this.delay = options.delay ?? 1000;\n }\n\n onRetry(error: UPPError, attempt: number): number | null {\n if (attempt > this.maxAttempts) {\n return null;\n }\n\n // Only retry on retryable errors\n if (!this.isRetryable(error)) {\n return null;\n }\n\n return this.delay * attempt;\n }\n\n private isRetryable(error: UPPError): boolean {\n return (\n error.code === 'RATE_LIMITED' ||\n error.code === 'NETWORK_ERROR' ||\n error.code === 'TIMEOUT' ||\n error.code === 'PROVIDER_ERROR'\n );\n }\n}\n\n/**\n * No retry strategy - fail immediately\n */\nexport class NoRetry implements RetryStrategy {\n onRetry(): null {\n return null;\n }\n}\n\n/**\n * Token bucket rate limiter with retry\n */\nexport class TokenBucket implements RetryStrategy {\n private tokens: number;\n private maxTokens: number;\n private refillRate: number; // tokens per second\n private lastRefill: number;\n private maxAttempts: number;\n\n constructor(options: {\n maxTokens?: number;\n refillRate?: number;\n maxAttempts?: number;\n } = {}) {\n this.maxTokens = options.maxTokens ?? 10;\n this.refillRate = options.refillRate ?? 1;\n this.maxAttempts = options.maxAttempts ?? 3;\n this.tokens = this.maxTokens;\n this.lastRefill = Date.now();\n }\n\n beforeRequest(): number {\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return 0;\n }\n\n // Calculate time until next token\n const msPerToken = 1000 / this.refillRate;\n return Math.ceil(msPerToken);\n }\n\n onRetry(error: UPPError, attempt: number): number | null {\n if (attempt > this.maxAttempts) {\n return null;\n }\n\n if (error.code !== 'RATE_LIMITED') {\n return null;\n }\n\n // Wait for token bucket to refill\n const msPerToken = 1000 / this.refillRate;\n return Math.ceil(msPerToken * 2); // Wait for 2 tokens\n }\n\n reset(): void {\n this.tokens = this.maxTokens;\n this.lastRefill = Date.now();\n }\n\n private refill(): void {\n const now = Date.now();\n const elapsed = (now - this.lastRefill) / 1000;\n const newTokens = elapsed * this.refillRate;\n\n this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);\n this.lastRefill = now;\n }\n}\n\n/**\n * Retry strategy that respects Retry-After headers\n */\nexport class RetryAfterStrategy implements RetryStrategy {\n private maxAttempts: number;\n private fallbackDelay: number;\n private lastRetryAfter?: number;\n\n constructor(options: {\n maxAttempts?: number;\n fallbackDelay?: number;\n } = {}) {\n this.maxAttempts = options.maxAttempts ?? 3;\n this.fallbackDelay = options.fallbackDelay ?? 5000;\n }\n\n /**\n * Set the Retry-After value from response headers\n * Call this before onRetry when you have a Retry-After header\n */\n setRetryAfter(seconds: number): void {\n this.lastRetryAfter = seconds * 1000;\n }\n\n onRetry(error: UPPError, attempt: number): number | null {\n if (attempt > this.maxAttempts) {\n return null;\n }\n\n if (error.code !== 'RATE_LIMITED') {\n return null;\n }\n\n const delay = this.lastRetryAfter ?? this.fallbackDelay;\n this.lastRetryAfter = undefined;\n return delay;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
// src/types/errors.ts
|
|
2
|
+
var UPPError = class _UPPError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
provider;
|
|
5
|
+
modality;
|
|
6
|
+
statusCode;
|
|
7
|
+
cause;
|
|
8
|
+
name = "UPPError";
|
|
9
|
+
constructor(message, code, provider, modality, statusCode, cause) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.provider = provider;
|
|
13
|
+
this.modality = modality;
|
|
14
|
+
this.statusCode = statusCode;
|
|
15
|
+
this.cause = cause;
|
|
16
|
+
if (Error.captureStackTrace) {
|
|
17
|
+
Error.captureStackTrace(this, _UPPError);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a string representation of the error
|
|
22
|
+
*/
|
|
23
|
+
toString() {
|
|
24
|
+
let str = `UPPError [${this.code}]: ${this.message}`;
|
|
25
|
+
str += ` (provider: ${this.provider}, modality: ${this.modality}`;
|
|
26
|
+
if (this.statusCode) {
|
|
27
|
+
str += `, status: ${this.statusCode}`;
|
|
28
|
+
}
|
|
29
|
+
str += ")";
|
|
30
|
+
return str;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert to JSON for serialization
|
|
34
|
+
*/
|
|
35
|
+
toJSON() {
|
|
36
|
+
return {
|
|
37
|
+
name: this.name,
|
|
38
|
+
message: this.message,
|
|
39
|
+
code: this.code,
|
|
40
|
+
provider: this.provider,
|
|
41
|
+
modality: this.modality,
|
|
42
|
+
statusCode: this.statusCode,
|
|
43
|
+
cause: this.cause?.message
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/http/keys.ts
|
|
49
|
+
var RoundRobinKeys = class {
|
|
50
|
+
keys;
|
|
51
|
+
index = 0;
|
|
52
|
+
constructor(keys) {
|
|
53
|
+
if (keys.length === 0) {
|
|
54
|
+
throw new Error("RoundRobinKeys requires at least one key");
|
|
55
|
+
}
|
|
56
|
+
this.keys = keys;
|
|
57
|
+
}
|
|
58
|
+
getKey() {
|
|
59
|
+
const key = this.keys[this.index];
|
|
60
|
+
this.index = (this.index + 1) % this.keys.length;
|
|
61
|
+
return key;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var WeightedKeys = class {
|
|
65
|
+
entries;
|
|
66
|
+
totalWeight;
|
|
67
|
+
constructor(keys) {
|
|
68
|
+
if (keys.length === 0) {
|
|
69
|
+
throw new Error("WeightedKeys requires at least one key");
|
|
70
|
+
}
|
|
71
|
+
this.entries = keys;
|
|
72
|
+
this.totalWeight = keys.reduce((sum, k) => sum + k.weight, 0);
|
|
73
|
+
}
|
|
74
|
+
getKey() {
|
|
75
|
+
const random = Math.random() * this.totalWeight;
|
|
76
|
+
let cumulative = 0;
|
|
77
|
+
for (const entry of this.entries) {
|
|
78
|
+
cumulative += entry.weight;
|
|
79
|
+
if (random <= cumulative) {
|
|
80
|
+
return entry.key;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return this.entries[this.entries.length - 1].key;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var DynamicKey = class {
|
|
87
|
+
selector;
|
|
88
|
+
constructor(selector) {
|
|
89
|
+
this.selector = selector;
|
|
90
|
+
}
|
|
91
|
+
async getKey() {
|
|
92
|
+
return this.selector();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
function isKeyStrategy(value) {
|
|
96
|
+
return typeof value === "object" && value !== null && "getKey" in value && typeof value.getKey === "function";
|
|
97
|
+
}
|
|
98
|
+
async function resolveApiKey(config, envVar, provider = "unknown", modality = "llm") {
|
|
99
|
+
const { apiKey } = config;
|
|
100
|
+
if (apiKey !== void 0) {
|
|
101
|
+
if (typeof apiKey === "string") {
|
|
102
|
+
return apiKey;
|
|
103
|
+
}
|
|
104
|
+
if (typeof apiKey === "function") {
|
|
105
|
+
return apiKey();
|
|
106
|
+
}
|
|
107
|
+
if (isKeyStrategy(apiKey)) {
|
|
108
|
+
return apiKey.getKey();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (envVar) {
|
|
112
|
+
const envValue = process.env[envVar];
|
|
113
|
+
if (envValue) {
|
|
114
|
+
return envValue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw new UPPError(
|
|
118
|
+
envVar ? `API key not found. Set ${envVar} environment variable or provide apiKey in config.` : "API key not found. Provide apiKey in config.",
|
|
119
|
+
"AUTHENTICATION_FAILED",
|
|
120
|
+
provider,
|
|
121
|
+
modality
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/http/errors.ts
|
|
126
|
+
function statusToErrorCode(status) {
|
|
127
|
+
switch (status) {
|
|
128
|
+
case 400:
|
|
129
|
+
return "INVALID_REQUEST";
|
|
130
|
+
case 401:
|
|
131
|
+
case 403:
|
|
132
|
+
return "AUTHENTICATION_FAILED";
|
|
133
|
+
case 404:
|
|
134
|
+
return "MODEL_NOT_FOUND";
|
|
135
|
+
case 408:
|
|
136
|
+
return "TIMEOUT";
|
|
137
|
+
case 413:
|
|
138
|
+
return "CONTEXT_LENGTH_EXCEEDED";
|
|
139
|
+
case 429:
|
|
140
|
+
return "RATE_LIMITED";
|
|
141
|
+
case 500:
|
|
142
|
+
case 502:
|
|
143
|
+
case 503:
|
|
144
|
+
case 504:
|
|
145
|
+
return "PROVIDER_ERROR";
|
|
146
|
+
default:
|
|
147
|
+
return "PROVIDER_ERROR";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function normalizeHttpError(response, provider, modality) {
|
|
151
|
+
const code = statusToErrorCode(response.status);
|
|
152
|
+
let message = `HTTP ${response.status}: ${response.statusText}`;
|
|
153
|
+
try {
|
|
154
|
+
const body = await response.text();
|
|
155
|
+
if (body) {
|
|
156
|
+
try {
|
|
157
|
+
const json = JSON.parse(body);
|
|
158
|
+
const extractedMessage = json.error?.message || json.message || json.error?.error?.message || json.detail;
|
|
159
|
+
if (extractedMessage) {
|
|
160
|
+
message = extractedMessage;
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
if (body.length < 200) {
|
|
164
|
+
message = body;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
return new UPPError(message, code, provider, modality, response.status);
|
|
171
|
+
}
|
|
172
|
+
function networkError(error, provider, modality) {
|
|
173
|
+
return new UPPError(
|
|
174
|
+
`Network error: ${error.message}`,
|
|
175
|
+
"NETWORK_ERROR",
|
|
176
|
+
provider,
|
|
177
|
+
modality,
|
|
178
|
+
void 0,
|
|
179
|
+
error
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
function timeoutError(timeout, provider, modality) {
|
|
183
|
+
return new UPPError(
|
|
184
|
+
`Request timed out after ${timeout}ms`,
|
|
185
|
+
"TIMEOUT",
|
|
186
|
+
provider,
|
|
187
|
+
modality
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
function cancelledError(provider, modality) {
|
|
191
|
+
return new UPPError("Request was cancelled", "CANCELLED", provider, modality);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/http/fetch.ts
|
|
195
|
+
var DEFAULT_TIMEOUT = 12e4;
|
|
196
|
+
async function doFetch(url, init, config, provider, modality) {
|
|
197
|
+
const fetchFn = config.fetch ?? fetch;
|
|
198
|
+
const timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
199
|
+
const strategy = config.retryStrategy;
|
|
200
|
+
if (strategy?.beforeRequest) {
|
|
201
|
+
const delay = await strategy.beforeRequest();
|
|
202
|
+
if (delay > 0) {
|
|
203
|
+
await sleep(delay);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
let lastError;
|
|
207
|
+
let attempt = 0;
|
|
208
|
+
while (true) {
|
|
209
|
+
attempt++;
|
|
210
|
+
try {
|
|
211
|
+
const response = await fetchWithTimeout(
|
|
212
|
+
fetchFn,
|
|
213
|
+
url,
|
|
214
|
+
init,
|
|
215
|
+
timeout,
|
|
216
|
+
provider,
|
|
217
|
+
modality
|
|
218
|
+
);
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
const error = await normalizeHttpError(response, provider, modality);
|
|
221
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
222
|
+
if (retryAfter && strategy) {
|
|
223
|
+
const seconds = parseInt(retryAfter, 10);
|
|
224
|
+
if (!isNaN(seconds) && "setRetryAfter" in strategy) {
|
|
225
|
+
strategy.setRetryAfter(
|
|
226
|
+
seconds
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (strategy) {
|
|
231
|
+
const delay = await strategy.onRetry(error, attempt);
|
|
232
|
+
if (delay !== null) {
|
|
233
|
+
await sleep(delay);
|
|
234
|
+
lastError = error;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
strategy?.reset?.();
|
|
241
|
+
return response;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (error instanceof UPPError) {
|
|
244
|
+
if (strategy) {
|
|
245
|
+
const delay = await strategy.onRetry(error, attempt);
|
|
246
|
+
if (delay !== null) {
|
|
247
|
+
await sleep(delay);
|
|
248
|
+
lastError = error;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
const uppError = networkError(error, provider, modality);
|
|
255
|
+
if (strategy) {
|
|
256
|
+
const delay = await strategy.onRetry(uppError, attempt);
|
|
257
|
+
if (delay !== null) {
|
|
258
|
+
await sleep(delay);
|
|
259
|
+
lastError = uppError;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
throw uppError;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function fetchWithTimeout(fetchFn, url, init, timeout, provider, modality) {
|
|
268
|
+
const controller = new AbortController();
|
|
269
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
270
|
+
const existingSignal = init.signal;
|
|
271
|
+
if (existingSignal) {
|
|
272
|
+
existingSignal.addEventListener("abort", () => controller.abort());
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const response = await fetchFn(url, {
|
|
276
|
+
...init,
|
|
277
|
+
signal: controller.signal
|
|
278
|
+
});
|
|
279
|
+
return response;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (error.name === "AbortError") {
|
|
282
|
+
if (existingSignal?.aborted) {
|
|
283
|
+
throw cancelledError(provider, modality);
|
|
284
|
+
}
|
|
285
|
+
throw timeoutError(timeout, provider, modality);
|
|
286
|
+
}
|
|
287
|
+
throw error;
|
|
288
|
+
} finally {
|
|
289
|
+
clearTimeout(timeoutId);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function sleep(ms) {
|
|
293
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
294
|
+
}
|
|
295
|
+
async function doStreamFetch(url, init, config, provider, modality) {
|
|
296
|
+
const fetchFn = config.fetch ?? fetch;
|
|
297
|
+
const timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
298
|
+
const strategy = config.retryStrategy;
|
|
299
|
+
if (strategy?.beforeRequest) {
|
|
300
|
+
const delay = await strategy.beforeRequest();
|
|
301
|
+
if (delay > 0) {
|
|
302
|
+
await sleep(delay);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const response = await fetchWithTimeout(
|
|
307
|
+
fetchFn,
|
|
308
|
+
url,
|
|
309
|
+
init,
|
|
310
|
+
timeout,
|
|
311
|
+
provider,
|
|
312
|
+
modality
|
|
313
|
+
);
|
|
314
|
+
return response;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (error instanceof UPPError) {
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
throw networkError(error, provider, modality);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/http/sse.ts
|
|
324
|
+
async function* parseSSEStream(body) {
|
|
325
|
+
const reader = body.getReader();
|
|
326
|
+
const decoder = new TextDecoder();
|
|
327
|
+
let buffer = "";
|
|
328
|
+
try {
|
|
329
|
+
while (true) {
|
|
330
|
+
const { done, value } = await reader.read();
|
|
331
|
+
if (done) {
|
|
332
|
+
if (buffer.trim()) {
|
|
333
|
+
const event = parseSSEEvent(buffer);
|
|
334
|
+
if (event !== null && event !== void 0) {
|
|
335
|
+
yield event;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
buffer += decoder.decode(value, { stream: true });
|
|
341
|
+
const events = buffer.split(/\r?\n\r?\n/);
|
|
342
|
+
buffer = events.pop() ?? "";
|
|
343
|
+
for (const eventText of events) {
|
|
344
|
+
if (!eventText.trim()) continue;
|
|
345
|
+
const event = parseSSEEvent(eventText);
|
|
346
|
+
if (event === "DONE") {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (event !== null && event !== void 0) {
|
|
350
|
+
yield event;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} finally {
|
|
355
|
+
reader.releaseLock();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function parseSSEEvent(eventText) {
|
|
359
|
+
const lines = eventText.split("\n");
|
|
360
|
+
let data = "";
|
|
361
|
+
let eventType = "";
|
|
362
|
+
for (const line of lines) {
|
|
363
|
+
const trimmedLine = line.trim();
|
|
364
|
+
if (trimmedLine.startsWith("event:")) {
|
|
365
|
+
eventType = trimmedLine.slice(6).trim();
|
|
366
|
+
} else if (trimmedLine.startsWith("data:")) {
|
|
367
|
+
const lineData = trimmedLine.slice(5).trim();
|
|
368
|
+
data += (data ? "\n" : "") + lineData;
|
|
369
|
+
} else if (trimmedLine.startsWith(":")) {
|
|
370
|
+
continue;
|
|
371
|
+
} else if (trimmedLine.startsWith("{") || trimmedLine.startsWith("[")) {
|
|
372
|
+
data += (data ? "\n" : "") + trimmedLine;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!data) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
if (data === "[DONE]") {
|
|
379
|
+
return "DONE";
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
const parsed = JSON.parse(data);
|
|
383
|
+
if (eventType) {
|
|
384
|
+
return { _eventType: eventType, ...parsed };
|
|
385
|
+
}
|
|
386
|
+
return parsed;
|
|
387
|
+
} catch {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async function* parseSimpleTextStream(body) {
|
|
392
|
+
const reader = body.getReader();
|
|
393
|
+
const decoder = new TextDecoder();
|
|
394
|
+
try {
|
|
395
|
+
while (true) {
|
|
396
|
+
const { done, value } = await reader.read();
|
|
397
|
+
if (done) break;
|
|
398
|
+
const text = decoder.decode(value, { stream: true });
|
|
399
|
+
if (text) {
|
|
400
|
+
yield text;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} finally {
|
|
404
|
+
reader.releaseLock();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export {
|
|
409
|
+
UPPError,
|
|
410
|
+
RoundRobinKeys,
|
|
411
|
+
WeightedKeys,
|
|
412
|
+
DynamicKey,
|
|
413
|
+
resolveApiKey,
|
|
414
|
+
statusToErrorCode,
|
|
415
|
+
normalizeHttpError,
|
|
416
|
+
networkError,
|
|
417
|
+
timeoutError,
|
|
418
|
+
cancelledError,
|
|
419
|
+
doFetch,
|
|
420
|
+
doStreamFetch,
|
|
421
|
+
parseSSEStream,
|
|
422
|
+
parseSimpleTextStream
|
|
423
|
+
};
|
|
424
|
+
//# sourceMappingURL=chunk-FTFX2VET.js.map
|