@providerprotocol/ai 0.0.33 → 0.0.35

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.
Files changed (133) hide show
  1. package/README.md +542 -3
  2. package/dist/anthropic/index.d.ts +2 -1
  3. package/dist/anthropic/index.js +151 -145
  4. package/dist/anthropic/index.js.map +1 -1
  5. package/dist/cerebras/index.d.ts +392 -0
  6. package/dist/cerebras/index.js +648 -0
  7. package/dist/cerebras/index.js.map +1 -0
  8. package/dist/chunk-3GWM5GR3.js +153 -0
  9. package/dist/chunk-3GWM5GR3.js.map +1 -0
  10. package/dist/chunk-4OGB7JZA.js +157 -0
  11. package/dist/chunk-4OGB7JZA.js.map +1 -0
  12. package/dist/chunk-7DXVRILR.js +49 -0
  13. package/dist/chunk-7DXVRILR.js.map +1 -0
  14. package/dist/{chunk-3C7O2RNO.js → chunk-A2IM7PGT.js} +6 -4
  15. package/dist/{chunk-3C7O2RNO.js.map → chunk-A2IM7PGT.js.map} +1 -1
  16. package/dist/{chunk-3D6XGGVG.js → chunk-ARVM24K2.js} +2 -2
  17. package/dist/{chunk-4J6OFUKX.js → chunk-AY55T37A.js} +70 -162
  18. package/dist/chunk-AY55T37A.js.map +1 -0
  19. package/dist/{chunk-ILR2D5PN.js → chunk-BRP5XJ6Q.js} +2 -86
  20. package/dist/chunk-BRP5XJ6Q.js.map +1 -0
  21. package/dist/chunk-C4JP64VW.js +298 -0
  22. package/dist/chunk-C4JP64VW.js.map +1 -0
  23. package/dist/chunk-COS4ON4G.js +111 -0
  24. package/dist/chunk-COS4ON4G.js.map +1 -0
  25. package/dist/chunk-ETBFOLQN.js +34 -0
  26. package/dist/chunk-ETBFOLQN.js.map +1 -0
  27. package/dist/chunk-HB4ZIH3T.js +31 -0
  28. package/dist/chunk-HB4ZIH3T.js.map +1 -0
  29. package/dist/chunk-I53CI6ZZ.js +142 -0
  30. package/dist/chunk-I53CI6ZZ.js.map +1 -0
  31. package/dist/chunk-IDZOVWP3.js +29 -0
  32. package/dist/chunk-IDZOVWP3.js.map +1 -0
  33. package/dist/chunk-JA3UZALR.js +88 -0
  34. package/dist/chunk-JA3UZALR.js.map +1 -0
  35. package/dist/{chunk-WAKD3OO5.js → chunk-N5DX5JW3.js} +31 -31
  36. package/dist/chunk-N5DX5JW3.js.map +1 -0
  37. package/dist/chunk-OIEWDFQU.js +97 -0
  38. package/dist/chunk-OIEWDFQU.js.map +1 -0
  39. package/dist/{chunk-TOJCZMVU.js → chunk-PMK5LZ5Z.js} +40 -40
  40. package/dist/chunk-PMK5LZ5Z.js.map +1 -0
  41. package/dist/chunk-UFFJDYCE.js +94 -0
  42. package/dist/chunk-UFFJDYCE.js.map +1 -0
  43. package/dist/chunk-VGKZIGVI.js +222 -0
  44. package/dist/chunk-VGKZIGVI.js.map +1 -0
  45. package/dist/chunk-VOEWHQUB.js +31 -0
  46. package/dist/chunk-VOEWHQUB.js.map +1 -0
  47. package/dist/{chunk-KUPF5KHT.js → chunk-Y5H7C5J4.js} +2 -2
  48. package/dist/chunk-ZI67WIQS.js +30 -0
  49. package/dist/chunk-ZI67WIQS.js.map +1 -0
  50. package/dist/{embedding-D2BYIehX.d.ts → embedding-CW6SaOOz.d.ts} +1 -1
  51. package/dist/google/index.d.ts +2 -1
  52. package/dist/google/index.js +202 -199
  53. package/dist/google/index.js.map +1 -1
  54. package/dist/groq/index.d.ts +410 -0
  55. package/dist/groq/index.js +649 -0
  56. package/dist/groq/index.js.map +1 -0
  57. package/dist/http/index.d.ts +3 -2
  58. package/dist/http/index.js +5 -4
  59. package/dist/image-stream-C0ciACM2.d.ts +11 -0
  60. package/dist/index.d.ts +8 -118
  61. package/dist/index.js +518 -767
  62. package/dist/index.js.map +1 -1
  63. package/dist/{llm-BQJZj3cD.d.ts → llm-DwbUK7un.d.ts} +12 -1632
  64. package/dist/middleware/logging/index.d.ts +76 -0
  65. package/dist/middleware/logging/index.js +74 -0
  66. package/dist/middleware/logging/index.js.map +1 -0
  67. package/dist/middleware/parsed-object/index.d.ts +45 -0
  68. package/dist/middleware/parsed-object/index.js +73 -0
  69. package/dist/middleware/parsed-object/index.js.map +1 -0
  70. package/dist/middleware/pubsub/index.d.ts +104 -0
  71. package/dist/middleware/pubsub/index.js +230 -0
  72. package/dist/middleware/pubsub/index.js.map +1 -0
  73. package/dist/middleware/pubsub/server/express/index.d.ts +52 -0
  74. package/dist/middleware/pubsub/server/express/index.js +11 -0
  75. package/dist/middleware/pubsub/server/express/index.js.map +1 -0
  76. package/dist/middleware/pubsub/server/fastify/index.d.ts +53 -0
  77. package/dist/middleware/pubsub/server/fastify/index.js +11 -0
  78. package/dist/middleware/pubsub/server/fastify/index.js.map +1 -0
  79. package/dist/middleware/pubsub/server/h3/index.d.ts +56 -0
  80. package/dist/middleware/pubsub/server/h3/index.js +11 -0
  81. package/dist/middleware/pubsub/server/h3/index.js.map +1 -0
  82. package/dist/middleware/pubsub/server/index.d.ts +78 -0
  83. package/dist/middleware/pubsub/server/index.js +34 -0
  84. package/dist/middleware/pubsub/server/index.js.map +1 -0
  85. package/dist/middleware/pubsub/server/webapi/index.d.ts +53 -0
  86. package/dist/middleware/pubsub/server/webapi/index.js +11 -0
  87. package/dist/middleware/pubsub/server/webapi/index.js.map +1 -0
  88. package/dist/ollama/index.d.ts +2 -1
  89. package/dist/ollama/index.js +48 -45
  90. package/dist/ollama/index.js.map +1 -1
  91. package/dist/openai/index.d.ts +2 -1
  92. package/dist/openai/index.js +319 -313
  93. package/dist/openai/index.js.map +1 -1
  94. package/dist/openrouter/index.d.ts +2 -1
  95. package/dist/openrouter/index.js +379 -383
  96. package/dist/openrouter/index.js.map +1 -1
  97. package/dist/proxy/index.d.ts +10 -914
  98. package/dist/proxy/index.js +275 -1007
  99. package/dist/proxy/index.js.map +1 -1
  100. package/dist/proxy/server/express/index.d.ts +161 -0
  101. package/dist/proxy/server/express/index.js +24 -0
  102. package/dist/proxy/server/express/index.js.map +1 -0
  103. package/dist/proxy/server/fastify/index.d.ts +162 -0
  104. package/dist/proxy/server/fastify/index.js +24 -0
  105. package/dist/proxy/server/fastify/index.js.map +1 -0
  106. package/dist/proxy/server/h3/index.d.ts +189 -0
  107. package/dist/proxy/server/h3/index.js +28 -0
  108. package/dist/proxy/server/h3/index.js.map +1 -0
  109. package/dist/proxy/server/index.d.ts +151 -0
  110. package/dist/proxy/server/index.js +48 -0
  111. package/dist/proxy/server/index.js.map +1 -0
  112. package/dist/proxy/server/webapi/index.d.ts +278 -0
  113. package/dist/proxy/server/webapi/index.js +32 -0
  114. package/dist/proxy/server/webapi/index.js.map +1 -0
  115. package/dist/responses/index.d.ts +650 -0
  116. package/dist/responses/index.js +930 -0
  117. package/dist/responses/index.js.map +1 -0
  118. package/dist/{retry-8Ch-WWgX.d.ts → retry-YayV42GV.d.ts} +1 -1
  119. package/dist/stream-CecfVCPO.d.ts +1632 -0
  120. package/dist/types-C8Gciizr.d.ts +168 -0
  121. package/dist/utils/index.d.ts +53 -0
  122. package/dist/utils/index.js +7 -0
  123. package/dist/utils/index.js.map +1 -0
  124. package/dist/xai/index.d.ts +2 -1
  125. package/dist/xai/index.js +310 -310
  126. package/dist/xai/index.js.map +1 -1
  127. package/package.json +94 -4
  128. package/dist/chunk-4J6OFUKX.js.map +0 -1
  129. package/dist/chunk-ILR2D5PN.js.map +0 -1
  130. package/dist/chunk-TOJCZMVU.js.map +0 -1
  131. package/dist/chunk-WAKD3OO5.js.map +0 -1
  132. /package/dist/{chunk-3D6XGGVG.js.map → chunk-ARVM24K2.js.map} +0 -0
  133. /package/dist/{chunk-KUPF5KHT.js.map → chunk-Y5H7C5J4.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/providers/cerebras/transform.ts","../../src/providers/cerebras/llm.ts","../../src/providers/cerebras/index.ts"],"sourcesContent":["/**\n * @fileoverview Cerebras API Message Transformers\n *\n * This module provides transformation functions for converting between the\n * Universal Provider Protocol (UPP) message format and Cerebras's Chat Completions\n * API format (OpenAI-compatible). It handles both request transformation (UPP -> Cerebras)\n * and response transformation (Cerebras -> UPP), including streaming event transformation.\n *\n * @module providers/cerebras/transform\n */\n\nimport type { LLMRequest, LLMResponse } from '../../types/llm.ts';\nimport type { Message } from '../../types/messages.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport { StreamEventType } from '../../types/stream.ts';\nimport type { Tool, ToolCall } from '../../types/tool.ts';\nimport type { TokenUsage } from '../../types/turn.ts';\nimport type { ContentBlock, TextBlock } from '../../types/content.ts';\nimport {\n AssistantMessage,\n isUserMessage,\n isAssistantMessage,\n isToolResultMessage,\n} from '../../types/messages.ts';\nimport { UPPError, ErrorCode, ModalityType } from '../../types/errors.ts';\nimport { generateId } from '../../utils/id.ts';\nimport type {\n CerebrasLLMParams,\n CerebrasRequest,\n CerebrasMessage,\n CerebrasUserContent,\n CerebrasTool,\n CerebrasResponse,\n CerebrasStreamChunk,\n CerebrasToolCall,\n} from './types.ts';\n\n/**\n * Normalizes system prompt to string.\n */\nfunction normalizeSystem(system: string | unknown[] | undefined): string | undefined {\n if (system === undefined || system === null) return undefined;\n if (typeof system === 'string') return system;\n if (!Array.isArray(system)) {\n throw new UPPError(\n 'System prompt must be a string or an array of text blocks',\n ErrorCode.InvalidRequest,\n 'cerebras',\n ModalityType.LLM\n );\n }\n\n const texts: string[] = [];\n for (const block of system) {\n if (!block || typeof block !== 'object' || !('text' in block)) {\n throw new UPPError(\n 'System prompt array must contain objects with a text field',\n ErrorCode.InvalidRequest,\n 'cerebras',\n ModalityType.LLM\n );\n }\n const textValue = (block as { text?: unknown }).text;\n if (typeof textValue !== 'string') {\n throw new UPPError(\n 'System prompt text must be a string',\n ErrorCode.InvalidRequest,\n 'cerebras',\n ModalityType.LLM\n );\n }\n if (textValue.length > 0) {\n texts.push(textValue);\n }\n }\n\n return texts.length > 0 ? texts.join('\\n\\n') : undefined;\n}\n\n/**\n * Filters content blocks to only include those with a valid 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 * Transforms a UPP content block to Cerebras user content format.\n */\nfunction transformContentBlock(block: ContentBlock): CerebrasUserContent {\n switch (block.type) {\n case 'text':\n return { type: 'text', text: block.text };\n\n case 'image':\n throw new UPPError(\n 'Cerebras does not support image input',\n ErrorCode.InvalidRequest,\n 'cerebras',\n ModalityType.LLM\n );\n\n case 'document':\n throw new UPPError(\n 'Cerebras does not support document input',\n ErrorCode.InvalidRequest,\n 'cerebras',\n ModalityType.LLM\n );\n\n default:\n throw new UPPError(\n `Unsupported content type: ${block.type}`,\n ErrorCode.InvalidRequest,\n 'cerebras',\n ModalityType.LLM\n );\n }\n}\n\n/**\n * Transforms a single UPP message to Cerebras format.\n */\nfunction transformMessage(message: Message): CerebrasMessage | null {\n if (isUserMessage(message)) {\n const validContent = filterValidContent(message.content);\n if (validContent.length === 1 && validContent[0]?.type === 'text') {\n return {\n role: 'user',\n content: (validContent[0] as TextBlock).text,\n };\n }\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 textContent = validContent\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n const assistantMessage: CerebrasMessage = {\n role: 'assistant',\n content: textContent || null,\n };\n\n if (message.toolCalls && message.toolCalls.length > 0) {\n (assistantMessage as { tool_calls?: CerebrasToolCall[] }).tool_calls =\n message.toolCalls.map((call) => ({\n id: call.toolCallId,\n type: 'function' as const,\n function: {\n name: call.toolName,\n arguments: JSON.stringify(call.arguments),\n },\n }));\n }\n\n return assistantMessage;\n }\n\n if (isToolResultMessage(message)) {\n const results = message.results.map((result) => ({\n role: 'tool' as const,\n tool_call_id: result.toolCallId,\n content:\n typeof result.result === 'string'\n ? result.result\n : JSON.stringify(result.result),\n }));\n\n return results[0] ?? null;\n }\n\n return null;\n}\n\n/**\n * Transforms tool result messages into multiple Cerebras tool messages.\n */\nexport function transformToolResults(message: Message): CerebrasMessage[] {\n if (!isToolResultMessage(message)) {\n const single = transformMessage(message);\n return single ? [single] : [];\n }\n\n return message.results.map((result) => ({\n role: 'tool' as const,\n tool_call_id: result.toolCallId,\n content:\n typeof result.result === 'string'\n ? result.result\n : JSON.stringify(result.result),\n }));\n}\n\n/**\n * Transforms UPP messages to Cerebras message format.\n *\n * @param messages - Array of UPP messages to transform\n * @param system - Optional system prompt\n * @returns Array of Cerebras-formatted messages\n */\nfunction transformMessages(\n messages: Message[],\n system?: string | unknown[]\n): CerebrasMessage[] {\n const result: CerebrasMessage[] = [];\n const normalizedSystem = normalizeSystem(system);\n\n if (normalizedSystem) {\n result.push({\n role: 'system',\n content: normalizedSystem,\n });\n }\n\n for (const message of messages) {\n if (isToolResultMessage(message)) {\n const toolMessages = transformToolResults(message);\n result.push(...toolMessages);\n } else {\n const transformed = transformMessage(message);\n if (transformed) {\n result.push(transformed);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Extracts Cerebras-specific options from tool metadata.\n */\nfunction extractToolOptions(tool: Tool): { strict?: boolean } {\n const cerebrasMeta = tool.metadata?.cerebras as { strict?: boolean } | undefined;\n return { strict: cerebrasMeta?.strict };\n}\n\n/**\n * Transforms a UPP tool definition to Cerebras function tool format.\n */\nfunction transformTool(tool: Tool): CerebrasTool {\n const { strict } = extractToolOptions(tool);\n\n return {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: {\n type: 'object',\n properties: tool.parameters.properties,\n required: tool.parameters.required,\n ...(tool.parameters.additionalProperties !== undefined\n ? { additionalProperties: tool.parameters.additionalProperties }\n : {}),\n },\n ...(strict !== undefined ? { strict } : {}),\n },\n };\n}\n\n/**\n * Transforms a UPP LLM request into Cerebras Chat Completions API format.\n *\n * @param request - The UPP LLM request containing messages, tools, and configuration\n * @param modelId - The Cerebras model identifier (e.g., 'llama-3.3-70b')\n * @returns A Cerebras Chat Completions API request body\n */\nexport function transformRequest(\n request: LLMRequest<CerebrasLLMParams>,\n modelId: string\n): CerebrasRequest {\n const params = request.params ?? ({} as CerebrasLLMParams);\n\n const cerebrasRequest: CerebrasRequest = {\n ...params,\n model: modelId,\n messages: transformMessages(request.messages, request.system),\n };\n\n if (request.tools && request.tools.length > 0) {\n cerebrasRequest.tools = request.tools.map(transformTool);\n }\n\n if (request.structure) {\n const schema: Record<string, unknown> = {\n type: 'object',\n properties: request.structure.properties,\n required: request.structure.required,\n ...(request.structure.additionalProperties !== undefined\n ? { additionalProperties: request.structure.additionalProperties }\n : { additionalProperties: false }),\n };\n if (request.structure.description) {\n schema.description = request.structure.description;\n }\n\n cerebrasRequest.response_format = {\n type: 'json_schema',\n json_schema: {\n name: 'json_response',\n description: request.structure.description,\n schema,\n strict: true,\n },\n };\n }\n\n return cerebrasRequest;\n}\n\n/**\n * Transforms a Cerebras response to UPP LLMResponse format.\n */\nexport function transformResponse(data: CerebrasResponse): LLMResponse {\n const choice = data.choices[0];\n if (!choice) {\n throw new UPPError(\n 'No choices in Cerebras response',\n ErrorCode.InvalidResponse,\n 'cerebras',\n ModalityType.LLM\n );\n }\n\n const textContent: TextBlock[] = [];\n let structuredData: unknown;\n if (choice.message.content) {\n textContent.push({ type: 'text', text: choice.message.content });\n try {\n structuredData = JSON.parse(choice.message.content);\n } catch {\n // Not JSON - expected for non-structured responses\n }\n }\n\n const toolCalls: ToolCall[] = [];\n if (choice.message.tool_calls) {\n for (const call of choice.message.tool_calls) {\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(call.function.arguments);\n } catch {\n // Invalid JSON - use empty object\n }\n toolCalls.push({\n toolCallId: call.id,\n toolName: call.function.name,\n arguments: args,\n });\n }\n }\n\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: data.id || generateId(),\n metadata: {\n cerebras: {\n model: data.model,\n finish_reason: choice.finish_reason,\n system_fingerprint: data.system_fingerprint,\n reasoning: choice.message.reasoning,\n time_info: data.time_info,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: data.usage.prompt_tokens,\n outputTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n cacheReadTokens: data.usage.prompt_tokens_details?.cached_tokens ?? 0,\n cacheWriteTokens: 0,\n };\n\n let stopReason = 'end_turn';\n switch (choice.finish_reason) {\n case 'stop':\n stopReason = 'end_turn';\n break;\n case 'length':\n stopReason = 'max_tokens';\n break;\n case 'tool_calls':\n stopReason = 'tool_use';\n break;\n case 'content_filter':\n stopReason = 'content_filter';\n break;\n }\n\n return {\n message,\n usage,\n stopReason,\n data: structuredData,\n };\n}\n\n/**\n * Mutable state object for accumulating data during streaming responses.\n */\nexport interface CerebrasStreamState {\n /** Response ID from the first chunk */\n id: string;\n /** Model identifier */\n model: string;\n /** Accumulated text content */\n text: string;\n /** Accumulated reasoning content */\n reasoning: string;\n /** Map of tool call index to accumulated tool call data */\n toolCalls: Map<number, { id: string; name: string; arguments: string }>;\n /** The finish reason when streaming completes */\n finishReason: string | null;\n /** Input token count (from usage chunk) */\n inputTokens: number;\n /** Output token count (from usage chunk) */\n outputTokens: number;\n /** Number of tokens read from cache */\n cacheReadTokens: number;\n /** Time info from the response */\n timeInfo?: {\n queue_time?: number;\n prompt_time?: number;\n completion_time?: number;\n total_time?: number;\n };\n}\n\n/**\n * Creates a fresh stream state object for a new streaming session.\n */\nexport function createStreamState(): CerebrasStreamState {\n return {\n id: '',\n model: '',\n text: '',\n reasoning: '',\n toolCalls: new Map(),\n finishReason: null,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n };\n}\n\n/**\n * Transforms a Cerebras streaming chunk into UPP stream events.\n */\nexport function transformStreamEvent(\n chunk: CerebrasStreamChunk,\n state: CerebrasStreamState\n): StreamEvent[] {\n const events: StreamEvent[] = [];\n\n if (chunk.id && !state.id) {\n state.id = chunk.id;\n events.push({ type: StreamEventType.MessageStart, index: 0, delta: {} });\n }\n if (chunk.model) {\n state.model = chunk.model;\n }\n\n const choice = chunk.choices[0];\n if (choice) {\n if (choice.delta.content) {\n state.text += choice.delta.content;\n events.push({\n type: StreamEventType.TextDelta,\n index: 0,\n delta: { text: choice.delta.content },\n });\n }\n\n if (choice.delta.reasoning) {\n state.reasoning += choice.delta.reasoning;\n events.push({\n type: StreamEventType.ReasoningDelta,\n index: 0,\n delta: { text: choice.delta.reasoning },\n });\n }\n\n if (choice.delta.tool_calls) {\n for (const toolCallDelta of choice.delta.tool_calls) {\n const index = toolCallDelta.index;\n let toolCall = state.toolCalls.get(index);\n\n if (!toolCall) {\n toolCall = { id: '', name: '', arguments: '' };\n state.toolCalls.set(index, toolCall);\n }\n\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id;\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name;\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments;\n events.push({\n type: StreamEventType.ToolCallDelta,\n index: index,\n delta: {\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n argumentsJson: toolCallDelta.function.arguments,\n },\n });\n }\n }\n }\n\n if (choice.finish_reason) {\n state.finishReason = choice.finish_reason;\n events.push({ type: StreamEventType.MessageStop, index: 0, delta: {} });\n }\n }\n\n if (chunk.usage) {\n state.inputTokens = chunk.usage.prompt_tokens;\n state.outputTokens = chunk.usage.completion_tokens;\n state.cacheReadTokens = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;\n }\n\n if (chunk.time_info) {\n state.timeInfo = chunk.time_info;\n }\n\n return events;\n}\n\n/**\n * Builds a complete LLMResponse from accumulated streaming state.\n */\nexport function buildResponseFromState(state: CerebrasStreamState): LLMResponse {\n const textContent: TextBlock[] = [];\n let structuredData: unknown;\n if (state.text) {\n textContent.push({ type: 'text', text: state.text });\n try {\n structuredData = JSON.parse(state.text);\n } catch {\n // Not JSON - expected for non-structured responses\n }\n }\n\n const toolCalls: ToolCall[] = [];\n for (const [, toolCall] of state.toolCalls) {\n let args: Record<string, unknown> = {};\n if (toolCall.arguments) {\n try {\n args = JSON.parse(toolCall.arguments);\n } catch {\n // Invalid JSON - use empty object\n }\n }\n toolCalls.push({\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n arguments: args,\n });\n }\n\n const messageId = state.id || generateId();\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: messageId,\n metadata: {\n cerebras: {\n model: state.model,\n finish_reason: state.finishReason,\n reasoning: state.reasoning || undefined,\n time_info: state.timeInfo,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.inputTokens + state.outputTokens,\n cacheReadTokens: state.cacheReadTokens,\n cacheWriteTokens: 0,\n };\n\n let stopReason = 'end_turn';\n switch (state.finishReason) {\n case 'stop':\n stopReason = 'end_turn';\n break;\n case 'length':\n stopReason = 'max_tokens';\n break;\n case 'tool_calls':\n stopReason = 'tool_use';\n break;\n case 'content_filter':\n stopReason = 'content_filter';\n break;\n }\n\n return {\n message,\n usage,\n stopReason,\n data: structuredData,\n };\n}\n","/**\n * @fileoverview Cerebras Chat Completions API Handler\n *\n * This module implements the LLM handler for Cerebras's Chat Completions API\n * (OpenAI-compatible at `/v1/chat/completions`).\n *\n * @see {@link https://inference-docs.cerebras.ai/introduction Cerebras API Reference}\n * @module providers/cerebras/llm\n */\n\nimport type { BoundLLMModel, LLMRequest, LLMResponse, LLMStreamResult, LLMCapabilities } from '../../types/llm.ts';\nimport type { LLMHandler } from '../../types/provider.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport { StreamEventType, objectDelta } from '../../types/stream.ts';\nimport type { LLMProvider } from '../../types/provider.ts';\nimport { UPPError, ErrorCode, ModalityType } 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 { parseJsonResponse } from '../../http/json.ts';\nimport { toError } from '../../utils/error.ts';\nimport type { CerebrasLLMParams, CerebrasResponse, CerebrasStreamChunk } from './types.ts';\nimport {\n transformRequest,\n transformResponse,\n transformStreamEvent,\n createStreamState,\n buildResponseFromState,\n} from './transform.ts';\n\n/** Base URL for Cerebras's Chat Completions API endpoint */\nconst CEREBRAS_API_URL = 'https://api.cerebras.ai/v1/chat/completions';\n\n/**\n * Capability declaration for the Cerebras Chat Completions API.\n *\n * Defines what features are supported by this handler:\n * - Streaming: Real-time token-by-token response streaming via SSE\n * - Tools: Function calling for structured interactions\n * - Structured Output: JSON schema-based response formatting\n * - Image Input: Not supported\n */\nconst CEREBRAS_CAPABILITIES: LLMCapabilities = {\n streaming: true,\n tools: true,\n structuredOutput: true,\n imageInput: false,\n documentInput: false,\n videoInput: false,\n audioInput: false,\n};\n\n/**\n * Creates an LLM handler for Cerebras's Chat Completions API.\n *\n * This factory function creates a handler that communicates with the\n * `/v1/chat/completions` endpoint. The handler supports both synchronous\n * completion requests and streaming responses.\n *\n * @returns An LLM handler configured for the Cerebras API\n *\n * @example\n * ```typescript\n * const handler = createLLMHandler();\n * const model = handler.bind('llama-3.3-70b');\n *\n * // Synchronous completion\n * const response = await model.complete({\n * messages: [{ role: 'user', content: 'Hello!' }],\n * config: { apiKey: 'csk-...' }\n * });\n *\n * // Streaming completion\n * const stream = model.stream({\n * messages: [{ role: 'user', content: 'Tell me a story' }],\n * config: { apiKey: 'csk-...' }\n * });\n *\n * for await (const event of stream) {\n * if (event.type === StreamEventType.TextDelta) {\n * process.stdout.write(event.delta.text);\n * }\n * }\n * ```\n */\nexport function createLLMHandler(): LLMHandler<CerebrasLLMParams> {\n let providerRef: LLMProvider<CerebrasLLMParams> | null = null;\n\n return {\n _setProvider(provider: LLMProvider<CerebrasLLMParams>) {\n providerRef = provider;\n },\n\n bind(modelId: string): BoundLLMModel<CerebrasLLMParams> {\n if (!providerRef) {\n throw new UPPError(\n 'Provider reference not set. Handler must be used with createProvider() or have _setProvider called.',\n ErrorCode.InvalidRequest,\n 'cerebras',\n ModalityType.LLM\n );\n }\n\n const model: BoundLLMModel<CerebrasLLMParams> = {\n modelId,\n capabilities: CEREBRAS_CAPABILITIES,\n\n get provider(): LLMProvider<CerebrasLLMParams> {\n return providerRef!;\n },\n\n async complete(request: LLMRequest<CerebrasLLMParams>): Promise<LLMResponse> {\n const apiKey = await resolveApiKey(\n request.config,\n 'CEREBRAS_API_KEY',\n 'cerebras',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? CEREBRAS_API_URL;\n const body = transformRequest(request, modelId);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n };\n\n if (request.config.headers) {\n for (const [key, value] of Object.entries(request.config.headers)) {\n if (value !== undefined) {\n headers[key] = value;\n }\n }\n }\n\n const response = await doFetch(\n baseUrl,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'cerebras',\n 'llm'\n );\n\n const data = await parseJsonResponse<CerebrasResponse>(response, 'cerebras', 'llm');\n return transformResponse(data);\n },\n\n stream(request: LLMRequest<CerebrasLLMParams>): 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 'CEREBRAS_API_KEY',\n 'cerebras',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? CEREBRAS_API_URL;\n const body = transformRequest(request, modelId);\n body.stream = true;\n body.stream_options = { include_usage: true };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n Accept: 'text/event-stream',\n };\n\n if (request.config.headers) {\n for (const [key, value] of Object.entries(request.config.headers)) {\n if (value !== undefined) {\n headers[key] = value;\n }\n }\n }\n\n const response = await doStreamFetch(\n baseUrl,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'cerebras',\n 'llm'\n );\n\n if (!response.ok) {\n const error = await normalizeHttpError(response, 'cerebras', '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 ErrorCode.ProviderError,\n 'cerebras',\n ModalityType.LLM\n );\n responseReject(error);\n throw error;\n }\n\n for await (const data of parseSSEStream(response.body)) {\n // Skip [DONE] marker\n if (data === '[DONE]') {\n continue;\n }\n\n // Check for Cerebras error event\n if (typeof data === 'object' && data !== null) {\n const chunk = data as CerebrasStreamChunk;\n\n // Check for error in chunk\n if ('error' in chunk && chunk.error) {\n const errorData = chunk.error as { message?: string; type?: string };\n const error = new UPPError(\n errorData.message ?? 'Unknown error',\n ErrorCode.ProviderError,\n 'cerebras',\n ModalityType.LLM\n );\n responseReject(error);\n throw error;\n }\n\n const uppEvents = transformStreamEvent(chunk, state);\n for (const event of uppEvents) {\n yield event;\n // Also emit ObjectDelta for structured output - gives developers explicit hook\n if (request.structure && event.type === StreamEventType.TextDelta) {\n yield objectDelta(event.delta.text ?? '', event.index);\n }\n }\n }\n }\n\n // Build final response\n responseResolve(buildResponseFromState(state));\n } catch (error) {\n const err = toError(error);\n responseReject(err);\n throw err;\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","/**\n * @fileoverview Cerebras Provider Factory\n *\n * This module provides the main Cerebras provider implementation for the\n * OpenAI-compatible Chat Completions API. Cerebras offers extremely fast\n * inference with models like Llama, Qwen, and their reasoning models.\n *\n * @module providers/cerebras\n */\n\nimport { createProvider } from '../../core/provider.ts';\nimport { createLLMHandler } from './llm.ts';\n\n/**\n * Configuration options for the Cerebras provider.\n *\n * Currently Cerebras only supports one API endpoint (Chat Completions),\n * so no additional options are needed.\n */\nexport interface CerebrasProviderOptions {\n // Reserved for future use (e.g., if Cerebras adds multiple API modes)\n}\n\n/**\n * The Cerebras provider instance.\n *\n * Use this provider to create model references for Cerebras models like\n * Llama 3.3, Qwen 3, GPT-OSS (reasoning), and other models available on Cerebras.\n *\n * @example Basic usage\n * ```typescript\n * import { cerebras } from './providers/cerebras';\n * import { llm } from './core/llm';\n *\n * const model = llm({\n * model: cerebras('llama-3.3-70b'),\n * params: { max_completion_tokens: 1000 }\n * });\n *\n * const turn = await model.generate('Hello!');\n * console.log(turn.response.text);\n * ```\n *\n * @example With streaming\n * ```typescript\n * const stream = model.stream('Tell me a story');\n *\n * for await (const event of stream) {\n * if (event.type === StreamEventType.TextDelta) {\n * process.stdout.write(event.delta.text ?? '');\n * }\n * }\n *\n * const turn = await stream.turn;\n * console.log('Tokens used:', turn.usage.totalTokens);\n * ```\n *\n * @example With tools\n * ```typescript\n * const calculator = {\n * name: 'calculate',\n * description: 'Calculate a math expression',\n * parameters: {\n * type: 'object',\n * properties: {\n * expression: { type: 'string' }\n * },\n * required: ['expression']\n * },\n * run: async (params: { expression: string }) => {\n * return eval(params.expression);\n * }\n * };\n *\n * const model = llm({\n * model: cerebras('llama-3.3-70b'),\n * tools: [calculator]\n * });\n *\n * const turn = await model.generate('What is 15 + 27?');\n * ```\n *\n * @example With reasoning\n * ```typescript\n * const model = llm({\n * model: cerebras('gpt-oss-120b'),\n * params: {\n * reasoning_effort: 'high',\n * reasoning_format: 'parsed'\n * }\n * });\n *\n * const turn = await model.generate('Solve this complex math problem...');\n * // Reasoning is available in turn.response.metadata.cerebras.reasoning\n * ```\n *\n * @example Available models\n * Production models:\n * - `llama3.1-8b` - Fast Llama 3.1 8B model (~2200 tok/s)\n * - `llama-3.3-70b` - Llama 3.3 70B with tool use (~2100 tok/s)\n * - `qwen-3-32b` - Qwen 3 32B with reasoning support (~2600 tok/s)\n * - `qwen-3-235b-a22b-instruct-2507` - Large Qwen model (~1400 tok/s)\n * - `gpt-oss-120b` - Reasoning model with high performance (~3000 tok/s)\n * - `zai-glm-4.6` - Z.ai GLM model with reasoning (~1000 tok/s)\n * - `zai-glm-4.7` - Z.ai GLM model with reasoning (~1000 tok/s)\n */\nexport const cerebras = createProvider<CerebrasProviderOptions>({\n name: 'cerebras',\n version: '1.0.0',\n handlers: {\n llm: createLLMHandler(),\n },\n});\n\nexport type {\n CerebrasLLMParams,\n CerebrasHeaders,\n CerebrasResponseFormat,\n CerebrasMessage,\n CerebrasRequest,\n CerebrasResponse,\n CerebrasStreamChunk,\n CerebrasTool,\n CerebrasToolCall,\n CerebrasToolChoice,\n CerebrasUsage,\n CerebrasTimeInfo,\n} from './types.ts';\n"],"mappings":"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","names":[]}
@@ -0,0 +1,153 @@
1
+ import {
2
+ serializeStreamEvent
3
+ } from "./chunk-ETBFOLQN.js";
4
+
5
+ // src/middleware/pubsub/server/shared.ts
6
+ var DEFAULT_CREATION_TIMEOUT = 5e3;
7
+ var CREATION_POLL_INTERVAL = 50;
8
+ function formatSSE(event) {
9
+ const serialized = serializeStreamEvent(event);
10
+ return `data: ${JSON.stringify(serialized)}
11
+
12
+ `;
13
+ }
14
+ async function waitForStream(streamId, adapter, timeout, signal) {
15
+ const deadline = Date.now() + timeout;
16
+ while (Date.now() < deadline) {
17
+ if (signal?.aborted) return false;
18
+ const exists = await adapter.exists(streamId);
19
+ if (exists) return true;
20
+ await new Promise((resolve) => setTimeout(resolve, CREATION_POLL_INTERVAL));
21
+ }
22
+ return false;
23
+ }
24
+ async function runSubscriberStream(streamId, adapter, writer, options = {}) {
25
+ const { signal, creationTimeout = DEFAULT_CREATION_TIMEOUT } = options;
26
+ if (signal?.aborted) {
27
+ writer.end();
28
+ return;
29
+ }
30
+ try {
31
+ const streamExists = await waitForStream(streamId, adapter, creationTimeout, signal);
32
+ if (signal?.aborted) {
33
+ writer.end();
34
+ return;
35
+ }
36
+ if (!streamExists) {
37
+ writer.write(`data: ${JSON.stringify({ error: "Stream not found" })}
38
+
39
+ `);
40
+ writer.end();
41
+ return;
42
+ }
43
+ const queue = [];
44
+ let resolveWait = null;
45
+ let done = false;
46
+ let lastSentCursor = -1;
47
+ const unsubscribe = adapter.subscribe(streamId, (event, cursor) => {
48
+ queue.push({ event, cursor: cursor ?? null });
49
+ resolveWait?.();
50
+ });
51
+ const onAbort = () => {
52
+ done = true;
53
+ resolveWait?.();
54
+ };
55
+ signal?.addEventListener("abort", onAbort);
56
+ const drainQueue = () => {
57
+ while (queue.length > 0 && !signal?.aborted) {
58
+ const item = queue.shift();
59
+ if (!item) {
60
+ break;
61
+ }
62
+ const { event, cursor } = item;
63
+ if (cursor !== null && cursor <= lastSentCursor) {
64
+ continue;
65
+ }
66
+ writer.write(formatSSE(event));
67
+ if (cursor !== null && cursor > lastSentCursor) {
68
+ lastSentCursor = cursor;
69
+ }
70
+ }
71
+ };
72
+ const dropReplayDuplicates = () => {
73
+ if (queue.length === 0) {
74
+ return;
75
+ }
76
+ const filtered = [];
77
+ for (const item of queue) {
78
+ if (item.cursor !== null && item.cursor <= lastSentCursor) {
79
+ continue;
80
+ }
81
+ filtered.push(item);
82
+ }
83
+ queue.length = 0;
84
+ queue.push(...filtered);
85
+ };
86
+ const waitForNewEvents = () => new Promise((resolve) => {
87
+ resolveWait = resolve;
88
+ setTimeout(resolve, 500);
89
+ });
90
+ try {
91
+ const events = await adapter.getEvents(streamId);
92
+ if (!events) {
93
+ writer.write(`data: ${JSON.stringify({ error: "Stream not found" })}
94
+
95
+ `);
96
+ writer.end();
97
+ return;
98
+ }
99
+ for (const event of events) {
100
+ if (signal?.aborted) break;
101
+ writer.write(formatSSE(event));
102
+ }
103
+ lastSentCursor = events.length - 1;
104
+ dropReplayDuplicates();
105
+ if (signal?.aborted) {
106
+ writer.end();
107
+ return;
108
+ }
109
+ const completed = await adapter.isCompleted(streamId).catch(() => false);
110
+ if (completed) {
111
+ drainQueue();
112
+ writer.write("data: [DONE]\n\n");
113
+ writer.end();
114
+ return;
115
+ }
116
+ while (!done) {
117
+ if (signal?.aborted) break;
118
+ drainQueue();
119
+ if (signal?.aborted) break;
120
+ const isComplete = await adapter.isCompleted(streamId).catch(() => false);
121
+ if (isComplete) {
122
+ done = true;
123
+ break;
124
+ }
125
+ await waitForNewEvents();
126
+ resolveWait = null;
127
+ }
128
+ if (!signal?.aborted) {
129
+ drainQueue();
130
+ }
131
+ } finally {
132
+ signal?.removeEventListener("abort", onAbort);
133
+ unsubscribe();
134
+ }
135
+ if (!signal?.aborted) {
136
+ writer.write("data: [DONE]\n\n");
137
+ }
138
+ writer.end();
139
+ } catch (error) {
140
+ if (!signal?.aborted) {
141
+ const errorMsg = error instanceof Error ? error.message : String(error);
142
+ writer.write(`data: ${JSON.stringify({ error: errorMsg })}
143
+
144
+ `);
145
+ }
146
+ writer.end();
147
+ }
148
+ }
149
+
150
+ export {
151
+ runSubscriberStream
152
+ };
153
+ //# sourceMappingURL=chunk-3GWM5GR3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware/pubsub/server/shared.ts"],"sourcesContent":["/**\n * @fileoverview Shared utilities for pub-sub server adapters.\n *\n * @module middleware/pubsub/server/shared\n * @internal\n */\n\nimport type { StreamEvent } from '../../../types/stream.ts';\nimport type { PubSubAdapter } from '../types.ts';\nimport { serializeStreamEvent } from '../../../stream/serialization.ts';\n\n/**\n * Writer interface for abstracting how data is written to responses.\n * @internal\n */\nexport interface StreamWriter {\n write(data: string): void;\n end(): void;\n}\n\n/**\n * Options for runSubscriberStream.\n * @internal\n */\nexport interface StreamOptions {\n signal?: AbortSignal;\n /** Max time to wait for stream creation (ms). @default 5000 */\n creationTimeout?: number;\n}\n\nconst DEFAULT_CREATION_TIMEOUT = 5000;\nconst CREATION_POLL_INTERVAL = 50;\n\n/**\n * Formats a stream event as an SSE data line.\n */\nexport function formatSSE(event: StreamEvent): string {\n const serialized = serializeStreamEvent(event);\n return `data: ${JSON.stringify(serialized)}\\n\\n`;\n}\n\n/**\n * Waits for a stream to be created, with timeout.\n * Returns true if stream exists, false if timed out.\n */\nasync function waitForStream(\n streamId: string,\n adapter: PubSubAdapter,\n timeout: number,\n signal?: AbortSignal\n): Promise<boolean> {\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n if (signal?.aborted) return false;\n\n const exists = await adapter.exists(streamId);\n if (exists) return true;\n\n await new Promise((resolve) => setTimeout(resolve, CREATION_POLL_INTERVAL));\n }\n\n return false;\n}\n\n/**\n * Core subscriber stream logic shared across all adapters.\n *\n * Handles:\n * 1. Waiting for stream creation (with timeout)\n * 2. Subscribing to live events FIRST (to prevent event loss)\n * 3. Replaying buffered events\n * 4. Checking if already completed\n * 5. Processing live events until completion\n * 6. Final cleanup\n * 7. Client disconnect via AbortSignal\n *\n * @internal\n */\nexport async function runSubscriberStream(\n streamId: string,\n adapter: PubSubAdapter,\n writer: StreamWriter,\n options: StreamOptions = {}\n): Promise<void> {\n const { signal, creationTimeout = DEFAULT_CREATION_TIMEOUT } = options;\n\n // Early exit if already aborted\n if (signal?.aborted) {\n writer.end();\n return;\n }\n\n try {\n // 1. Wait for stream to be created (handles race with background generation)\n const streamExists = await waitForStream(streamId, adapter, creationTimeout, signal);\n\n if (signal?.aborted) {\n writer.end();\n return;\n }\n\n if (!streamExists) {\n writer.write(`data: ${JSON.stringify({ error: 'Stream not found' })}\\n\\n`);\n writer.end();\n return;\n }\n\n // 2. Subscribe FIRST to prevent event loss during replay\n // Any events emitted while we replay will queue up\n const queue: Array<{ event: StreamEvent; cursor: number | null }> = [];\n let resolveWait: (() => void) | null = null;\n let done = false;\n let lastSentCursor = -1;\n\n const unsubscribe = adapter.subscribe(streamId, (event: StreamEvent, cursor?: number) => {\n queue.push({ event, cursor: cursor ?? null });\n resolveWait?.();\n });\n\n // Handle client disconnect\n const onAbort = (): void => {\n done = true;\n resolveWait?.();\n };\n signal?.addEventListener('abort', onAbort);\n\n const drainQueue = (): void => {\n while (queue.length > 0 && !signal?.aborted) {\n const item = queue.shift();\n if (!item) {\n break;\n }\n const { event, cursor } = item;\n if (cursor !== null && cursor <= lastSentCursor) {\n continue;\n }\n writer.write(formatSSE(event));\n if (cursor !== null && cursor > lastSentCursor) {\n lastSentCursor = cursor;\n }\n }\n };\n\n const dropReplayDuplicates = (): void => {\n if (queue.length === 0) {\n return;\n }\n const filtered: Array<{ event: StreamEvent; cursor: number | null }> = [];\n for (const item of queue) {\n if (item.cursor !== null && item.cursor <= lastSentCursor) {\n continue;\n }\n filtered.push(item);\n }\n queue.length = 0;\n queue.push(...filtered);\n };\n\n const waitForNewEvents = (): Promise<void> => new Promise<void>((resolve) => {\n resolveWait = resolve;\n setTimeout(resolve, 500);\n });\n\n try {\n // 3. Replay buffered events (subscription is already active)\n const events = await adapter.getEvents(streamId);\n\n if (!events) {\n writer.write(`data: ${JSON.stringify({ error: 'Stream not found' })}\\n\\n`);\n writer.end();\n return;\n }\n\n for (const event of events) {\n if (signal?.aborted) break;\n writer.write(formatSSE(event));\n }\n\n lastSentCursor = events.length - 1;\n\n // Drop queued events that are already included in the replay (cursor-aware)\n dropReplayDuplicates();\n\n // Check abort after replay\n if (signal?.aborted) {\n writer.end();\n return;\n }\n\n // 4. Check if already completed\n const completed = await adapter.isCompleted(streamId).catch(() => false);\n if (completed) {\n // Drain any queued events first (these are post-replay events)\n drainQueue();\n writer.write('data: [DONE]\\n\\n');\n writer.end();\n return;\n }\n\n // 5. Process live events until completion\n while (!done) {\n // Check abort\n if (signal?.aborted) break;\n\n // Drain queue (all events here are post-replay, no duplicates)\n drainQueue();\n\n // Check abort again\n if (signal?.aborted) break;\n\n // Check completion\n const isComplete = await adapter.isCompleted(streamId).catch(() => false);\n if (isComplete) {\n done = true;\n break;\n }\n\n // Wait for new events\n await waitForNewEvents();\n resolveWait = null;\n }\n\n // Final drain (only if not aborted)\n if (!signal?.aborted) {\n drainQueue();\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n unsubscribe();\n }\n\n // Only send DONE if not aborted\n if (!signal?.aborted) {\n writer.write('data: [DONE]\\n\\n');\n }\n writer.end();\n } catch (error) {\n // Don't send error if client disconnected\n if (!signal?.aborted) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n writer.write(`data: ${JSON.stringify({ error: errorMsg })}\\n\\n`);\n }\n writer.end();\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,157 @@
1
+ import {
2
+ resolveImageResult,
3
+ serializeImageResult,
4
+ serializeImageStreamEvent,
5
+ serializeStreamEvent,
6
+ serializeTurn
7
+ } from "./chunk-C4JP64VW.js";
8
+
9
+ // src/providers/proxy/server/h3.ts
10
+ function sendJSON(turn, event) {
11
+ event.node.res.setHeader("Content-Type", "application/json");
12
+ return serializeTurn(turn);
13
+ }
14
+ function sendEmbeddingJSON(result, event) {
15
+ event.node.res.setHeader("Content-Type", "application/json");
16
+ return result;
17
+ }
18
+ function sendImageJSON(result, event) {
19
+ event.node.res.setHeader("Content-Type", "application/json");
20
+ return serializeImageResult(result);
21
+ }
22
+ function streamSSE(stream, event) {
23
+ const res = event.node.res;
24
+ res.setHeader("Content-Type", "text/event-stream");
25
+ res.setHeader("Cache-Control", "no-cache");
26
+ res.setHeader("Connection", "keep-alive");
27
+ (async () => {
28
+ try {
29
+ for await (const evt of stream) {
30
+ const serialized = serializeStreamEvent(evt);
31
+ res.write(`data: ${JSON.stringify(serialized)}
32
+
33
+ `);
34
+ }
35
+ const turn = await stream.turn;
36
+ res.write(`data: ${JSON.stringify(serializeTurn(turn))}
37
+
38
+ `);
39
+ res.write("data: [DONE]\n\n");
40
+ } catch (error) {
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ res.write(`data: ${JSON.stringify({ error: message })}
43
+
44
+ `);
45
+ } finally {
46
+ res.end();
47
+ }
48
+ })();
49
+ }
50
+ function streamImageSSE(stream, event) {
51
+ const res = event.node.res;
52
+ res.setHeader("Content-Type", "text/event-stream");
53
+ res.setHeader("Cache-Control", "no-cache");
54
+ res.setHeader("Connection", "keep-alive");
55
+ (async () => {
56
+ try {
57
+ for await (const evt of stream) {
58
+ const serialized = serializeImageStreamEvent(evt);
59
+ res.write(`data: ${JSON.stringify(serialized)}
60
+
61
+ `);
62
+ }
63
+ const result = await resolveImageResult(stream);
64
+ res.write(`data: ${JSON.stringify(serializeImageResult(result))}
65
+
66
+ `);
67
+ res.write("data: [DONE]\n\n");
68
+ } catch (error) {
69
+ const message = error instanceof Error ? error.message : String(error);
70
+ res.write(`data: ${JSON.stringify({ error: message })}
71
+
72
+ `);
73
+ } finally {
74
+ res.end();
75
+ }
76
+ })();
77
+ }
78
+ function createSSEStream(stream) {
79
+ const encoder = new TextEncoder();
80
+ return new ReadableStream({
81
+ async start(controller) {
82
+ try {
83
+ for await (const event of stream) {
84
+ const serialized = serializeStreamEvent(event);
85
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(serialized)}
86
+
87
+ `));
88
+ }
89
+ const turn = await stream.turn;
90
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(serializeTurn(turn))}
91
+
92
+ `));
93
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
94
+ } catch (error) {
95
+ const message = error instanceof Error ? error.message : String(error);
96
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify({ error: message })}
97
+
98
+ `));
99
+ } finally {
100
+ controller.close();
101
+ }
102
+ }
103
+ });
104
+ }
105
+ function createImageSSEStream(stream) {
106
+ const encoder = new TextEncoder();
107
+ return new ReadableStream({
108
+ async start(controller) {
109
+ try {
110
+ for await (const event of stream) {
111
+ const serialized = serializeImageStreamEvent(event);
112
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(serialized)}
113
+
114
+ `));
115
+ }
116
+ const result = await resolveImageResult(stream);
117
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(serializeImageResult(result))}
118
+
119
+ `));
120
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
121
+ } catch (error) {
122
+ const message = error instanceof Error ? error.message : String(error);
123
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify({ error: message })}
124
+
125
+ `));
126
+ } finally {
127
+ controller.close();
128
+ }
129
+ }
130
+ });
131
+ }
132
+ function sendError(message, status, event) {
133
+ return { error: message, statusCode: status };
134
+ }
135
+ var h3 = {
136
+ sendJSON,
137
+ sendEmbeddingJSON,
138
+ sendImageJSON,
139
+ streamSSE,
140
+ streamImageSSE,
141
+ createSSEStream,
142
+ createImageSSEStream,
143
+ sendError
144
+ };
145
+
146
+ export {
147
+ sendJSON,
148
+ sendEmbeddingJSON,
149
+ sendImageJSON,
150
+ streamSSE,
151
+ streamImageSSE,
152
+ createSSEStream,
153
+ createImageSSEStream,
154
+ sendError,
155
+ h3
156
+ };
157
+ //# sourceMappingURL=chunk-4OGB7JZA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/proxy/server/h3.ts"],"sourcesContent":["/**\n * @fileoverview H3/Nitro/Nuxt adapter for proxy server.\n *\n * Provides utilities for using PP proxy with H3-based servers\n * (Nuxt, Nitro, or standalone H3).\n *\n * @module providers/proxy/server/h3\n */\n\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport type { EmbeddingResult } from '../../../types/embedding.ts';\nimport type { ImageResult } from '../../../types/image.ts';\nimport { serializeTurn, serializeStreamEvent } from '../serialization.ts';\nimport { serializeImageResult, serializeImageStreamEvent } from '../serialization.media.ts';\nimport { resolveImageResult, type ImageStreamLike } from './image-stream.ts';\n\n/**\n * H3 Event interface (minimal type to avoid dependency).\n */\ninterface H3Event {\n node: {\n res: {\n setHeader(name: string, value: string): void;\n write(chunk: string): boolean;\n end(): void;\n };\n };\n}\n\n/**\n * Send a Turn as JSON response.\n *\n * @param turn - The completed inference turn\n * @param event - H3 event object\n * @returns Serialized turn data\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * return h3Adapter.sendJSON(turn, event);\n * ```\n */\nexport function sendJSON(turn: Turn, event: H3Event): unknown {\n event.node.res.setHeader('Content-Type', 'application/json');\n return serializeTurn(turn);\n}\n\n/**\n * Send an EmbeddingResult as JSON response.\n *\n * @param result - The embedding result\n * @param event - H3 event object\n * @returns Serialized result data\n */\nexport function sendEmbeddingJSON(result: EmbeddingResult, event: H3Event): unknown {\n event.node.res.setHeader('Content-Type', 'application/json');\n return result;\n}\n\n/**\n * Send an ImageResult as JSON response.\n *\n * @param result - The image result\n * @param event - H3 event object\n * @returns Serialized image result data\n */\nexport function sendImageJSON(result: ImageResult, event: H3Event): unknown {\n event.node.res.setHeader('Content-Type', 'application/json');\n return serializeImageResult(result);\n}\n\n/**\n * Stream a StreamResult as Server-Sent Events.\n *\n * @param stream - The StreamResult from instance.stream()\n * @param event - H3 event object\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * return h3Adapter.streamSSE(stream, event);\n * ```\n */\nexport function streamSSE(stream: StreamResult, event: H3Event): void {\n const res = event.node.res;\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n (async () => {\n try {\n for await (const evt of stream) {\n const serialized = serializeStreamEvent(evt);\n res.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const turn = await stream.turn;\n res.write(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`);\n res.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n res.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n}\n\n/**\n * Stream an ImageStreamResult as Server-Sent Events.\n *\n * @param stream - The ImageStreamResult or ImageProviderStreamResult from image().stream()\n * @param event - H3 event object\n */\nexport function streamImageSSE(stream: ImageStreamLike, event: H3Event): void {\n const res = event.node.res;\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n (async () => {\n try {\n for await (const evt of stream) {\n const serialized = serializeImageStreamEvent(evt);\n res.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const result = await resolveImageResult(stream);\n res.write(`data: ${JSON.stringify(serializeImageResult(result))}\\n\\n`);\n res.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n res.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n}\n\n/**\n * Create a ReadableStream for H3's sendStream utility.\n *\n * Use this with H3's sendStream for better integration:\n * ```typescript\n * import { sendStream } from 'h3';\n * return sendStream(event, h3Adapter.createSSEStream(stream));\n * ```\n *\n * @param stream - The StreamResult from instance.stream()\n * @returns A ReadableStream of SSE data\n */\nexport function createSSEStream(stream: StreamResult): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serialized)}\\n\\n`));\n }\n\n const turn = await stream.turn;\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`));\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ error: message })}\\n\\n`));\n } finally {\n controller.close();\n }\n },\n });\n}\n\n/**\n * Create a ReadableStream for image SSE data.\n *\n * @param stream - The ImageStreamResult or ImageProviderStreamResult from image().stream()\n * @returns A ReadableStream of SSE data\n */\nexport function createImageSSEStream(stream: ImageStreamLike): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const event of stream) {\n const serialized = serializeImageStreamEvent(event);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serialized)}\\n\\n`));\n }\n\n const result = await resolveImageResult(stream);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serializeImageResult(result))}\\n\\n`));\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ error: message })}\\n\\n`));\n } finally {\n controller.close();\n }\n },\n });\n}\n\n/**\n * Send an error response.\n *\n * @param message - Error message\n * @param status - HTTP status code\n * @param event - H3 event object\n * @returns Error object for H3 to serialize\n */\nexport function sendError(message: string, status: number, event: H3Event): { error: string; statusCode: number } {\n return { error: message, statusCode: status };\n}\n\n/**\n * H3/Nitro/Nuxt adapter utilities.\n *\n * @example Basic usage\n * ```typescript\n * // Nuxt server route: server/api/ai.post.ts\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';\n *\n * export default defineEventHandler(async (event) => {\n * const body = await readBody(event);\n * const { messages, system, params } = parseBody(body);\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * const wantsStream = getHeader(event, 'accept')?.includes('text/event-stream');\n * if (wantsStream) {\n * return h3Adapter.streamSSE(instance.stream(messages), event);\n * } else {\n * const turn = await instance.generate(messages);\n * return h3Adapter.sendJSON(turn, event);\n * }\n * });\n * ```\n *\n * @example API Gateway with authentication (Nuxt)\n * ```typescript\n * // server/api/ai.post.ts\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';\n *\n * // Server manages AI provider keys - users never see them\n * const claude = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * config: {\n * apiKey: new RoundRobinKeys([\n * process.env.ANTHROPIC_KEY_1!,\n * process.env.ANTHROPIC_KEY_2!,\n * ]),\n * retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),\n * },\n * });\n *\n * export default defineEventHandler(async (event) => {\n * // Authenticate with your platform credentials\n * const token = getHeader(event, 'authorization')?.replace('Bearer ', '');\n * const user = await validatePlatformToken(token);\n * if (!user) {\n * throw createError({ statusCode: 401, message: 'Unauthorized' });\n * }\n *\n * // Track usage per user\n * // await trackUsage(user.id);\n *\n * const body = await readBody(event);\n * const { messages, system, params } = parseBody(body);\n *\n * if (params?.stream) {\n * return h3Adapter.streamSSE(claude.stream(messages, { system }), event);\n * }\n * const turn = await claude.generate(messages, { system });\n * return h3Adapter.sendJSON(turn, event);\n * });\n * ```\n */\nexport const h3 = {\n sendJSON,\n sendEmbeddingJSON,\n sendImageJSON,\n streamSSE,\n streamImageSSE,\n createSSEStream,\n createImageSSEStream,\n sendError,\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,49 @@
1
+ import {
2
+ runSubscriberStream
3
+ } from "./chunk-3GWM5GR3.js";
4
+
5
+ // src/middleware/pubsub/server/webapi.ts
6
+ function createSubscriberStream(streamId, adapter) {
7
+ const encoder = new TextEncoder();
8
+ const abortController = new AbortController();
9
+ let closed = false;
10
+ return new ReadableStream({
11
+ async start(controller) {
12
+ await runSubscriberStream(
13
+ streamId,
14
+ adapter,
15
+ {
16
+ write: (data) => {
17
+ if (closed) {
18
+ return;
19
+ }
20
+ controller.enqueue(encoder.encode(data));
21
+ },
22
+ end: () => {
23
+ if (closed) {
24
+ return;
25
+ }
26
+ closed = true;
27
+ try {
28
+ controller.close();
29
+ } catch {
30
+ }
31
+ }
32
+ },
33
+ { signal: abortController.signal }
34
+ );
35
+ },
36
+ cancel() {
37
+ abortController.abort();
38
+ }
39
+ });
40
+ }
41
+ var webapi = {
42
+ createSubscriberStream
43
+ };
44
+
45
+ export {
46
+ createSubscriberStream,
47
+ webapi
48
+ };
49
+ //# sourceMappingURL=chunk-7DXVRILR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware/pubsub/server/webapi.ts"],"sourcesContent":["/**\n * @fileoverview Web API adapter for pub-sub stream resumption.\n *\n * Provides utilities for Web API native frameworks (Bun, Deno, Next.js App Router,\n * Cloudflare Workers) to handle stream reconnections.\n *\n * @module middleware/pubsub/server/webapi\n */\n\nimport type { PubSubAdapter } from '../types.ts';\nimport { runSubscriberStream } from './shared.ts';\n\n/**\n * Creates a ReadableStream that replays buffered events and subscribes to live events.\n *\n * This utility handles the reconnection pattern for server routes:\n * 1. Replays all buffered events from the adapter\n * 2. If stream is already completed, closes immediately\n * 3. Otherwise, subscribes to live events until completion\n *\n * Works with any framework that supports web standard ReadableStream.\n *\n * @param streamId - The stream ID to subscribe to\n * @param adapter - The pub-sub adapter instance\n * @returns A ReadableStream of SSE-formatted data\n *\n * @example\n * ```typescript\n * import { createSubscriberStream } from '@providerprotocol/ai/middleware/pubsub/server/webapi';\n *\n * // Next.js App Router\n * export async function POST(req: Request) {\n * const { streamId } = await req.json();\n *\n * return new Response(createSubscriberStream(streamId, adapter), {\n * headers: {\n * 'Content-Type': 'text/event-stream',\n * 'Cache-Control': 'no-cache',\n * 'Connection': 'keep-alive',\n * },\n * });\n * }\n * ```\n */\nexport function createSubscriberStream(\n streamId: string,\n adapter: PubSubAdapter\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n const abortController = new AbortController();\n let closed = false;\n\n return new ReadableStream({\n async start(controller) {\n await runSubscriberStream(\n streamId,\n adapter,\n {\n write: (data: string) => {\n if (closed) {\n return;\n }\n controller.enqueue(encoder.encode(data));\n },\n end: () => {\n if (closed) {\n return;\n }\n closed = true;\n try {\n controller.close();\n } catch {\n // Ignore close errors after cancellation\n }\n },\n },\n { signal: abortController.signal }\n );\n },\n cancel() {\n abortController.abort();\n },\n });\n}\n\n/**\n * Web API adapter namespace for pub-sub server utilities.\n */\nexport const webapi = {\n createSubscriberStream,\n};\n"],"mappings":";;;;;AA4CO,SAAS,uBACd,UACA,SAC4B;AAC5B,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,kBAAkB,IAAI,gBAAgB;AAC5C,MAAI,SAAS;AAEb,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,UACE,OAAO,CAAC,SAAiB;AACvB,gBAAI,QAAQ;AACV;AAAA,YACF;AACA,uBAAW,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,UACzC;AAAA,UACA,KAAK,MAAM;AACT,gBAAI,QAAQ;AACV;AAAA,YACF;AACA,qBAAS;AACT,gBAAI;AACF,yBAAW,MAAM;AAAA,YACnB,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF;AAAA,QACA,EAAE,QAAQ,gBAAgB,OAAO;AAAA,MACnC;AAAA,IACF;AAAA,IACA,SAAS;AACP,sBAAgB,MAAM;AAAA,IACxB;AAAA,EACF,CAAC;AACH;AAKO,IAAM,SAAS;AAAA,EACpB;AACF;","names":[]}
@@ -1,8 +1,10 @@
1
1
  import {
2
- ErrorCode,
3
- UPPError,
4
2
  toError
5
- } from "./chunk-4J6OFUKX.js";
3
+ } from "./chunk-AY55T37A.js";
4
+ import {
5
+ ErrorCode,
6
+ UPPError
7
+ } from "./chunk-COS4ON4G.js";
6
8
 
7
9
  // src/http/json.ts
8
10
  async function parseJsonResponse(response, provider, modality) {
@@ -47,4 +49,4 @@ async function parseJsonResponse(response, provider, modality) {
47
49
  export {
48
50
  parseJsonResponse
49
51
  };
50
- //# sourceMappingURL=chunk-3C7O2RNO.js.map
52
+ //# sourceMappingURL=chunk-A2IM7PGT.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/http/json.ts"],"sourcesContent":["/**\n * @fileoverview JSON response parsing utilities.\n *\n * @module http/json\n */\n\nimport { ErrorCode, UPPError, type Modality } from '../types/errors.ts';\nimport { toError } from '../utils/error.ts';\n\n/**\n * Parses a JSON response body with normalized error handling.\n *\n * @typeParam T - Expected JSON shape\n * @param response - Fetch response to parse\n * @param provider - Provider identifier for error context\n * @param modality - Modality for error context\n * @returns Parsed JSON object\n * @throws {UPPError} INVALID_RESPONSE when JSON parsing fails or body is empty\n */\nexport async function parseJsonResponse<T>(\n response: Response,\n provider: string,\n modality: Modality\n): Promise<T> {\n let bodyText: string;\n try {\n bodyText = await response.text();\n } catch (error) {\n const cause = toError(error);\n throw new UPPError(\n 'Failed to read response body',\n ErrorCode.InvalidResponse,\n provider,\n modality,\n response.status,\n cause\n );\n }\n\n if (!bodyText) {\n throw new UPPError(\n 'Empty response body',\n ErrorCode.InvalidResponse,\n provider,\n modality,\n response.status\n );\n }\n\n try {\n return JSON.parse(bodyText) as T;\n } catch (error) {\n const cause = toError(error);\n throw new UPPError(\n 'Failed to parse JSON response',\n ErrorCode.InvalidResponse,\n provider,\n modality,\n response.status,\n cause\n );\n }\n}\n"],"mappings":";;;;;;;AAmBA,eAAsB,kBACpB,UACA,UACA,UACY;AACZ,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,KAAK;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,QAAQ;AAAA,EAC5B,SAAS,OAAO;AACd,UAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/http/json.ts"],"sourcesContent":["/**\n * @fileoverview JSON response parsing utilities.\n *\n * @module http/json\n */\n\nimport { ErrorCode, UPPError, type Modality } from '../types/errors.ts';\nimport { toError } from '../utils/error.ts';\n\n/**\n * Parses a JSON response body with normalized error handling.\n *\n * @typeParam T - Expected JSON shape\n * @param response - Fetch response to parse\n * @param provider - Provider identifier for error context\n * @param modality - Modality for error context\n * @returns Parsed JSON object\n * @throws {UPPError} INVALID_RESPONSE when JSON parsing fails or body is empty\n */\nexport async function parseJsonResponse<T>(\n response: Response,\n provider: string,\n modality: Modality\n): Promise<T> {\n let bodyText: string;\n try {\n bodyText = await response.text();\n } catch (error) {\n const cause = toError(error);\n throw new UPPError(\n 'Failed to read response body',\n ErrorCode.InvalidResponse,\n provider,\n modality,\n response.status,\n cause\n );\n }\n\n if (!bodyText) {\n throw new UPPError(\n 'Empty response body',\n ErrorCode.InvalidResponse,\n provider,\n modality,\n response.status\n );\n }\n\n try {\n return JSON.parse(bodyText) as T;\n } catch (error) {\n const cause = toError(error);\n throw new UPPError(\n 'Failed to parse JSON response',\n ErrorCode.InvalidResponse,\n provider,\n modality,\n response.status,\n cause\n );\n }\n}\n"],"mappings":";;;;;;;;;AAmBA,eAAsB,kBACpB,UACA,UACA,UACY;AACZ,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,KAAK;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,QAAQ;AAAA,EAC5B,SAAS,OAAO;AACd,UAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ErrorCode,
3
3
  UPPError
4
- } from "./chunk-4J6OFUKX.js";
4
+ } from "./chunk-COS4ON4G.js";
5
5
 
6
6
  // src/http/keys.ts
7
7
  var RoundRobinKeys = class {
@@ -125,4 +125,4 @@ export {
125
125
  maskApiKey,
126
126
  resolveApiKey
127
127
  };
128
- //# sourceMappingURL=chunk-3D6XGGVG.js.map
128
+ //# sourceMappingURL=chunk-ARVM24K2.js.map