@nebulaos/llm-gateway 0.1.2 → 0.1.3
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/dist/index.js +94 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -59
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
var import_openai = __toESM(require("openai"));
|
|
38
38
|
var import_node_crypto = require("crypto");
|
|
39
39
|
var import_core = require("@nebulaos/core");
|
|
40
|
+
var import_types = require("@nebulaos/types");
|
|
40
41
|
var LLMGatewayError = class extends Error {
|
|
41
42
|
constructor(message, code, status, cause) {
|
|
42
43
|
super(message);
|
|
@@ -82,19 +83,22 @@ var LLMGateway = class {
|
|
|
82
83
|
name: t.function.name,
|
|
83
84
|
description: t.function.description?.slice(0, 200)
|
|
84
85
|
}));
|
|
86
|
+
const { responseFormat, ...llmConfig } = mergedOptions ?? {};
|
|
87
|
+
const startData = {
|
|
88
|
+
provider: this.providerName,
|
|
89
|
+
model: this.modelName,
|
|
90
|
+
messagesCount: messages.length,
|
|
91
|
+
toolsCount: tools?.length ?? 0,
|
|
92
|
+
llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : void 0,
|
|
93
|
+
responseFormat,
|
|
94
|
+
messages: messagesPreview,
|
|
95
|
+
tools: toolsPreview
|
|
96
|
+
};
|
|
85
97
|
return import_core.Tracing.withSpan(
|
|
86
98
|
{
|
|
87
|
-
kind:
|
|
99
|
+
kind: import_types.SpanType.llm,
|
|
88
100
|
name: `llm:${this.modelName}`,
|
|
89
|
-
data:
|
|
90
|
-
provider: this.providerName,
|
|
91
|
-
model: this.modelName,
|
|
92
|
-
messagesCount: messages.length,
|
|
93
|
-
toolsCount: tools?.length ?? 0,
|
|
94
|
-
responseFormat: mergedOptions?.responseFormat,
|
|
95
|
-
messages: messagesPreview,
|
|
96
|
-
tools: toolsPreview
|
|
97
|
-
}
|
|
101
|
+
data: startData
|
|
98
102
|
},
|
|
99
103
|
async (llmSpan) => {
|
|
100
104
|
const headers = this.buildGatewayHeaders();
|
|
@@ -129,18 +133,18 @@ var LLMGateway = class {
|
|
|
129
133
|
const usage = this.mapUsage(response.usage);
|
|
130
134
|
const finishReason = this.mapFinishReason(choice.finish_reason);
|
|
131
135
|
const enrichment = this.extractEnrichmentFromHeaders(httpResponse.headers);
|
|
136
|
+
const endData = {
|
|
137
|
+
usage: enrichment.usage ?? usage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
138
|
+
finishReason: finishReason ?? "stop",
|
|
139
|
+
toolCallsCount: message.tool_calls?.length ?? 0,
|
|
140
|
+
choices: response.choices,
|
|
141
|
+
model: enrichment.modelActual,
|
|
142
|
+
fallbackUsed: enrichment.fallbackUsed,
|
|
143
|
+
cost: enrichment.cost ? parseFloat(enrichment.cost.amountUsd) : void 0
|
|
144
|
+
};
|
|
132
145
|
await llmSpan.end({
|
|
133
146
|
status: "success",
|
|
134
|
-
data:
|
|
135
|
-
usage: enrichment.usage ?? usage,
|
|
136
|
-
finishReason,
|
|
137
|
-
toolCallsCount: message.tool_calls?.length ?? 0,
|
|
138
|
-
outputPreview: message.content?.slice(0, 200),
|
|
139
|
-
// Enrichment from backend gateway
|
|
140
|
-
modelActual: enrichment.modelActual,
|
|
141
|
-
fallbackUsed: enrichment.fallbackUsed,
|
|
142
|
-
cost: enrichment.cost
|
|
143
|
-
}
|
|
147
|
+
data: endData
|
|
144
148
|
});
|
|
145
149
|
return {
|
|
146
150
|
content: message.content || "",
|
|
@@ -158,15 +162,16 @@ var LLMGateway = class {
|
|
|
158
162
|
} catch (error) {
|
|
159
163
|
this.logger.error("LLM Gateway request failed", error, void 0, void 0);
|
|
160
164
|
const gatewayError = this.handleError(error);
|
|
165
|
+
const errorEndData = {
|
|
166
|
+
error: {
|
|
167
|
+
message: gatewayError.message,
|
|
168
|
+
code: gatewayError.code,
|
|
169
|
+
status: gatewayError.status
|
|
170
|
+
}
|
|
171
|
+
};
|
|
161
172
|
await llmSpan.end({
|
|
162
173
|
status: "error",
|
|
163
|
-
data:
|
|
164
|
-
error: {
|
|
165
|
-
message: gatewayError.message,
|
|
166
|
-
code: gatewayError.code,
|
|
167
|
-
status: gatewayError.status
|
|
168
|
-
}
|
|
169
|
-
}
|
|
174
|
+
data: errorEndData
|
|
170
175
|
});
|
|
171
176
|
throw gatewayError;
|
|
172
177
|
}
|
|
@@ -184,18 +189,21 @@ var LLMGateway = class {
|
|
|
184
189
|
name: t.function.name,
|
|
185
190
|
description: t.function.description?.slice(0, 200)
|
|
186
191
|
}));
|
|
192
|
+
const { responseFormat, ...llmConfig } = mergedOptions ?? {};
|
|
193
|
+
const startData = {
|
|
194
|
+
provider: this.providerName,
|
|
195
|
+
model: this.modelName,
|
|
196
|
+
messagesCount: messages.length,
|
|
197
|
+
toolsCount: tools?.length ?? 0,
|
|
198
|
+
llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : void 0,
|
|
199
|
+
responseFormat,
|
|
200
|
+
messages: messagesPreview,
|
|
201
|
+
tools: toolsPreview
|
|
202
|
+
};
|
|
187
203
|
const llmSpan = await import_core.Tracing.startSpan({
|
|
188
|
-
kind:
|
|
204
|
+
kind: import_types.SpanType.llm,
|
|
189
205
|
name: `llm:${this.modelName}`,
|
|
190
|
-
data:
|
|
191
|
-
provider: this.providerName,
|
|
192
|
-
model: this.modelName,
|
|
193
|
-
messagesCount: messages.length,
|
|
194
|
-
toolsCount: tools?.length ?? 0,
|
|
195
|
-
responseFormat: mergedOptions?.responseFormat,
|
|
196
|
-
messages: messagesPreview,
|
|
197
|
-
tools: toolsPreview
|
|
198
|
-
}
|
|
206
|
+
data: startData
|
|
199
207
|
});
|
|
200
208
|
const headers = this.buildGatewayHeaders();
|
|
201
209
|
this.logger.debug("LLM Gateway stream request", {
|
|
@@ -226,15 +234,16 @@ var LLMGateway = class {
|
|
|
226
234
|
this.logger.error("LLM Gateway stream request failed", error, void 0, void 0);
|
|
227
235
|
const gatewayError = this.handleError(error);
|
|
228
236
|
if (llmSpan) {
|
|
237
|
+
const errorEndData = {
|
|
238
|
+
error: {
|
|
239
|
+
message: gatewayError.message,
|
|
240
|
+
code: gatewayError.code,
|
|
241
|
+
status: gatewayError.status
|
|
242
|
+
}
|
|
243
|
+
};
|
|
229
244
|
await llmSpan.end({
|
|
230
245
|
status: "error",
|
|
231
|
-
data:
|
|
232
|
-
error: {
|
|
233
|
-
message: gatewayError.message,
|
|
234
|
-
code: gatewayError.code,
|
|
235
|
-
status: gatewayError.status
|
|
236
|
-
}
|
|
237
|
-
}
|
|
246
|
+
data: errorEndData
|
|
238
247
|
});
|
|
239
248
|
}
|
|
240
249
|
throw gatewayError;
|
|
@@ -243,6 +252,8 @@ var LLMGateway = class {
|
|
|
243
252
|
let finalFinishReason;
|
|
244
253
|
let toolCallsCount = 0;
|
|
245
254
|
let outputPreview = "";
|
|
255
|
+
let finalContent = "";
|
|
256
|
+
const toolCallsAccumulator = /* @__PURE__ */ new Map();
|
|
246
257
|
try {
|
|
247
258
|
for await (const chunk of stream) {
|
|
248
259
|
if (chunk.usage) {
|
|
@@ -265,6 +276,7 @@ var LLMGateway = class {
|
|
|
265
276
|
const delta = choice.delta;
|
|
266
277
|
if (!delta) continue;
|
|
267
278
|
if (delta.content) {
|
|
279
|
+
finalContent += delta.content;
|
|
268
280
|
if (outputPreview.length < 200) {
|
|
269
281
|
outputPreview += delta.content.slice(0, 200 - outputPreview.length);
|
|
270
282
|
}
|
|
@@ -272,49 +284,72 @@ var LLMGateway = class {
|
|
|
272
284
|
}
|
|
273
285
|
if (delta.tool_calls) {
|
|
274
286
|
for (const tc of delta.tool_calls) {
|
|
287
|
+
const idx = tc.index;
|
|
275
288
|
if (tc.id && tc.function?.name) {
|
|
276
289
|
toolCallsCount++;
|
|
290
|
+
toolCallsAccumulator.set(idx, { id: tc.id, name: tc.function.name, arguments: "" });
|
|
277
291
|
yield {
|
|
278
292
|
type: "tool_call_start",
|
|
279
|
-
index:
|
|
293
|
+
index: idx,
|
|
280
294
|
id: tc.id,
|
|
281
295
|
name: tc.function.name
|
|
282
296
|
};
|
|
283
297
|
}
|
|
284
298
|
if (tc.function?.arguments) {
|
|
299
|
+
const existing = toolCallsAccumulator.get(idx);
|
|
300
|
+
if (existing) {
|
|
301
|
+
existing.arguments += tc.function.arguments;
|
|
302
|
+
}
|
|
285
303
|
yield {
|
|
286
304
|
type: "tool_call_delta",
|
|
287
|
-
index:
|
|
305
|
+
index: idx,
|
|
288
306
|
args: tc.function.arguments
|
|
289
307
|
};
|
|
290
308
|
}
|
|
291
309
|
}
|
|
292
310
|
}
|
|
293
311
|
}
|
|
312
|
+
const toolCalls = Array.from(toolCallsAccumulator.values()).map((tc) => ({
|
|
313
|
+
id: tc.id,
|
|
314
|
+
type: "function",
|
|
315
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
316
|
+
}));
|
|
317
|
+
const choices = [{
|
|
318
|
+
index: 0,
|
|
319
|
+
message: {
|
|
320
|
+
role: "assistant",
|
|
321
|
+
content: finalContent || null,
|
|
322
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
323
|
+
},
|
|
324
|
+
finish_reason: finalFinishReason
|
|
325
|
+
}];
|
|
294
326
|
if (llmSpan) {
|
|
327
|
+
const endData = {
|
|
328
|
+
usage: finalUsage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
329
|
+
finishReason: finalFinishReason ?? "stop",
|
|
330
|
+
toolCallsCount,
|
|
331
|
+
outputPreview,
|
|
332
|
+
choices
|
|
333
|
+
};
|
|
295
334
|
await llmSpan.end({
|
|
296
335
|
status: "success",
|
|
297
|
-
data:
|
|
298
|
-
usage: finalUsage,
|
|
299
|
-
finishReason: finalFinishReason,
|
|
300
|
-
toolCallsCount,
|
|
301
|
-
outputPreview
|
|
302
|
-
}
|
|
336
|
+
data: endData
|
|
303
337
|
});
|
|
304
338
|
}
|
|
305
339
|
} catch (error) {
|
|
306
340
|
this.logger.error("LLM Gateway stream failed", error, void 0, void 0);
|
|
307
341
|
const gatewayError = this.handleError(error);
|
|
308
342
|
if (llmSpan) {
|
|
343
|
+
const errorEndData = {
|
|
344
|
+
error: {
|
|
345
|
+
message: gatewayError.message,
|
|
346
|
+
code: gatewayError.code,
|
|
347
|
+
status: gatewayError.status
|
|
348
|
+
}
|
|
349
|
+
};
|
|
309
350
|
await llmSpan.end({
|
|
310
351
|
status: "error",
|
|
311
|
-
data:
|
|
312
|
-
error: {
|
|
313
|
-
message: gatewayError.message,
|
|
314
|
-
code: gatewayError.code,
|
|
315
|
-
status: gatewayError.status
|
|
316
|
-
}
|
|
317
|
-
}
|
|
352
|
+
data: errorEndData
|
|
318
353
|
});
|
|
319
354
|
}
|
|
320
355
|
throw gatewayError;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import OpenAIClient, { APIError, ClientOptions } from \"openai\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport {\n IModel,\n Message,\n ProviderResponse,\n StreamChunk,\n ToolDefinitionForLLM,\n GenerateOptions,\n TokenUsage,\n ContentPart,\n ConsoleLogger,\n type LogLevel,\n ExecutionContext,\n Tracing,\n} from \"@nebulaos/core\";\n\n/**\n * Custom error class for LLM Gateway errors.\n * Provides clear, actionable error messages for developers.\n */\nexport class LLMGatewayError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n public readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"LLMGatewayError\";\n }\n}\n\n/**\n * LLM Gateway Provider Configuration\n */\nexport interface LLMGatewayConfig {\n /** API Key from NebulaOS */\n apiKey: string;\n /** Base URL of the NebulaOS LLM Gateway */\n baseUrl?: string;\n /** Route alias (e.g., \"assistente\", \"code-review\") */\n model: string;\n /** Logger verbosity for gateway calls */\n logLevel?: LogLevel;\n /** Optional OpenAI client options */\n clientOptions?: ClientOptions;\n /**\n * Default model options passed to every generate() call.\n * Supports provider-specific params like reasoning_effort, temperature, topK, etc.\n * These can be overridden by options passed directly to generate().\n */\n options?: Omit<GenerateOptions, \"responseFormat\">;\n}\n\n/**\n * NebulaOS LLM Gateway Provider\n *\n * Provides access to NebulaOS LLM Gateway routes through an OpenAI-compatible interface.\n * Routes are pre-configured in NebulaOS and provide automatic fallback, cost tracking,\n * and access control.\n */\nexport class LLMGateway implements IModel {\n providerName = \"llm-gateway\";\n modelName: string;\n private client: OpenAIClient;\n private baseUrl: string;\n private logger: ConsoleLogger;\n private options?: Omit<GenerateOptions, \"responseFormat\">;\n\n capabilities = {\n inputFiles: {\n mimeTypes: [\"image/*\"],\n sources: [\"url\", \"base64\"] as const,\n },\n } as const;\n\n constructor(config: LLMGatewayConfig) {\n this.modelName = config.model;\n\n this.baseUrl = config.baseUrl || \"http://localhost:4100\";\n const baseURL = this.baseUrl.endsWith(\"/v1\") ? this.baseUrl : `${this.baseUrl}/v1`;\n this.logger = new ConsoleLogger(config.logLevel || \"info\", \"nebulaos/llm-gateway\");\n\n this.client = new OpenAIClient({\n apiKey: config.apiKey,\n baseURL,\n ...config.clientOptions,\n });\n this.options = config.options;\n }\n\n async generate(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): Promise<ProviderResponse> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n return Tracing.withSpan(\n {\n kind: \"llm\",\n name: `llm:${this.modelName}`,\n data: {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n responseFormat: mergedOptions?.responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n },\n },\n async (llmSpan) => {\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway request\", {\n model,\n baseUrl: this.baseUrl,\n stream: false,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n try {\n // Use .withResponse() to access HTTP headers for backend enrichment data\n const { data: response, response: httpResponse } = await this.client.chat.completions\n .create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n )\n .withResponse();\n\n this.logger.debug(\"LLM Gateway response\", {\n model,\n finishReason: response.choices?.[0]?.finish_reason,\n hasUsage: Boolean(response.usage),\n });\n\n const choice = response.choices[0];\n const message = choice.message;\n const usage = this.mapUsage(response.usage);\n const finishReason = this.mapFinishReason(choice.finish_reason);\n\n // Read enrichment headers from backend (model actual, cost, usage, fallback)\n const enrichment = this.extractEnrichmentFromHeaders(httpResponse.headers);\n\n await llmSpan.end({\n status: \"success\",\n data: {\n usage: enrichment.usage ?? usage,\n finishReason,\n toolCallsCount: message.tool_calls?.length ?? 0,\n outputPreview: message.content?.slice(0, 200),\n // Enrichment from backend gateway\n modelActual: enrichment.modelActual,\n fallbackUsed: enrichment.fallbackUsed,\n cost: enrichment.cost,\n },\n });\n\n return {\n content: message.content || \"\",\n toolCalls: message.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n finishReason,\n usage: enrichment.usage ?? usage,\n };\n } catch (error) {\n this.logger.error(\"LLM Gateway request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n await llmSpan.end({\n status: \"error\",\n data: {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n },\n });\n\n throw gatewayError;\n }\n },\n );\n }\n\n async *generateStream(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): AsyncGenerator<StreamChunk> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n // Start span manually for streaming (async generators can't use withSpan directly)\n const llmSpan = await Tracing.startSpan({\n kind: \"llm\",\n name: `llm:${this.modelName}`,\n data: {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n responseFormat: mergedOptions?.responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n },\n });\n\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway stream request\", {\n model,\n baseUrl: this.baseUrl,\n stream: true,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n let stream;\n try {\n stream = await this.client.chat.completions.create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n stream: true,\n stream_options: { include_usage: true },\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n );\n } catch (error) {\n this.logger.error(\"LLM Gateway stream request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n await llmSpan.end({\n status: \"error\",\n data: {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n },\n });\n }\n\n throw gatewayError;\n }\n\n let finalUsage: TokenUsage | undefined;\n let finalFinishReason: ProviderResponse[\"finishReason\"];\n let toolCallsCount = 0;\n let outputPreview = \"\";\n\n try {\n for await (const chunk of stream) {\n if (chunk.usage) {\n finalUsage = this.mapUsage(chunk.usage);\n yield {\n type: \"finish\",\n reason: \"stop\",\n usage: finalUsage,\n };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice) continue;\n\n if (choice.finish_reason) {\n finalFinishReason = this.mapFinishReason(choice.finish_reason);\n yield {\n type: \"finish\",\n reason: finalFinishReason,\n };\n }\n\n const delta = choice.delta;\n if (!delta) continue;\n\n if (delta.content) {\n if (outputPreview.length < 200) {\n outputPreview += delta.content.slice(0, 200 - outputPreview.length);\n }\n yield { type: \"content_delta\", delta: delta.content };\n }\n\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n if (tc.id && tc.function?.name) {\n toolCallsCount++;\n yield {\n type: \"tool_call_start\",\n index: tc.index,\n id: tc.id,\n name: tc.function.name,\n };\n }\n\n if (tc.function?.arguments) {\n yield {\n type: \"tool_call_delta\",\n index: tc.index,\n args: tc.function.arguments,\n };\n }\n }\n }\n }\n\n // End span with success\n if (llmSpan) {\n await llmSpan.end({\n status: \"success\",\n data: {\n usage: finalUsage,\n finishReason: finalFinishReason,\n toolCallsCount,\n outputPreview,\n },\n });\n }\n } catch (error) {\n this.logger.error(\"LLM Gateway stream failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n await llmSpan.end({\n status: \"error\",\n data: {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n },\n });\n }\n\n throw gatewayError;\n }\n }\n\n // ==========================================================================\n // Error Handling\n // ==========================================================================\n\n /**\n * Transforms raw errors into actionable LLMGatewayError with clear messages.\n * This ensures developers get specific guidance on how to resolve issues.\n *\n * Differentiates between:\n * - Gateway errors: LLM Gateway API key issues (check NEBULAOS_API_KEY env var)\n * - Provider errors: LLM provider API key issues (check route config in dashboard)\n */\n private handleError(error: unknown): LLMGatewayError {\n // Handle OpenAI SDK APIError (includes status, message, code)\n if (error instanceof APIError) {\n const status = error.status;\n const originalMessage = error.message;\n\n // Check X-Error-Source header to differentiate gateway vs provider errors\n const errorSource = this.extractErrorSource(error);\n\n // Authentication errors (401)\n if (status === 401) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway authentication failed: Your LLM Gateway API key is invalid or expired. ` +\n `Please verify your NEBULAOS_API_KEY environment variable or check your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_AUTH_ERROR\",\n status,\n error,\n );\n } else {\n // Provider error (default for 401 without explicit gateway source)\n return new LLMGatewayError(\n `LLM Provider authentication failed: The API key configured for your LLM provider (OpenAI, Azure, etc.) is invalid or expired. ` +\n `Please verify the provider API key in your route configuration in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_AUTH_ERROR\",\n status,\n error,\n );\n }\n }\n\n // Permission denied (403)\n if (status === 403) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway access denied: Your LLM Gateway API key does not have permission to access this route. ` +\n `Please verify the route is allowed for your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_FORBIDDEN\",\n status,\n error,\n );\n } else {\n return new LLMGatewayError(\n `LLM Provider access denied: The provider API key does not have permission for this operation. ` +\n `Please verify the provider API key permissions in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_FORBIDDEN\",\n status,\n error,\n );\n }\n }\n\n // Rate limit (429)\n if (status === 429) {\n return new LLMGatewayError(\n `LLM Gateway rate limit exceeded: Too many requests to the LLM provider. ` +\n `Please wait before retrying or check your rate limit configuration. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_RATE_LIMIT\",\n status,\n error,\n );\n }\n\n // Bad request (400)\n if (status === 400) {\n return new LLMGatewayError(\n `LLM Gateway request error: Invalid request parameters. ` +\n `Please check your request configuration (model, messages, tools). ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_BAD_REQUEST\",\n status,\n error,\n );\n }\n\n // Not found (404)\n if (status === 404) {\n return new LLMGatewayError(\n `LLM Gateway route not found: The specified model or route does not exist. ` +\n `Please verify the route alias '${this.modelName}' is correct and provisioned. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_NOT_FOUND\",\n status,\n error,\n );\n }\n\n // Timeout (408, 504)\n if (status === 408 || status === 504) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The request took too long to complete. ` +\n `This may be due to high load or a complex request. Please try again. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_TIMEOUT\",\n status,\n error,\n );\n }\n\n // Server errors (5xx)\n if (status && status >= 500) {\n return new LLMGatewayError(\n `LLM Gateway server error: The LLM provider returned an error (${status}). ` +\n `This is typically a temporary issue. Please try again later. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_SERVER_ERROR\",\n status,\n error,\n );\n }\n\n // Other API errors\n return new LLMGatewayError(\n `LLM Gateway error (${status}): ${originalMessage}`,\n \"LLM_GATEWAY_ERROR\",\n status,\n error,\n );\n }\n\n // Handle standard Error objects\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n // Connection errors\n if (msg.includes(\"econnrefused\") || msg.includes(\"enotfound\") || msg.includes(\"network\")) {\n return new LLMGatewayError(\n `LLM Gateway connection failed: Unable to connect to the LLM Gateway at ${this.baseUrl}. ` +\n `Please verify the gateway is running and accessible. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_CONNECTION_ERROR\",\n undefined,\n error,\n );\n }\n\n // Timeout errors\n if (msg.includes(\"timeout\") || msg.includes(\"timed out\") || msg.includes(\"etimedout\")) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The connection timed out. ` +\n `Please check network connectivity and try again. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_TIMEOUT\",\n undefined,\n error,\n );\n }\n\n // Generic Error - preserve original message with context\n return new LLMGatewayError(\n `LLM Gateway error: ${error.message}`,\n \"LLM_GATEWAY_ERROR\",\n undefined,\n error,\n );\n }\n\n // Unknown error type\n return new LLMGatewayError(\n `LLM Gateway error: An unexpected error occurred. Details: ${String(error)}`,\n \"LLM_GATEWAY_UNKNOWN_ERROR\",\n undefined,\n error,\n );\n }\n\n /**\n * Extracts the error source from an APIError.\n * The backend sets X-Error-Source header or includes source in the error body\n * to differentiate between gateway errors (LLM Gateway API key) and provider errors.\n *\n * @returns \"gateway\" if the error is from LLM Gateway authentication,\n * \"provider\" if the error is from the upstream LLM provider,\n * undefined if the source cannot be determined.\n */\n private extractErrorSource(error: APIError): \"gateway\" | \"provider\" | undefined {\n // Try to get source from response headers\n const headers = error.headers;\n if (headers) {\n const errorSource = headers[\"x-error-source\"] || headers[\"X-Error-Source\"];\n if (errorSource === \"gateway\" || errorSource === \"provider\") {\n return errorSource;\n }\n }\n\n // Try to get source from error body\n // The backend may include { error: { source: \"gateway\" | \"provider\", ... } }\n const errorBody = error.error as Record<string, unknown> | undefined;\n if (errorBody && typeof errorBody === \"object\") {\n // Check for nested error object (OpenAI style)\n const nestedError = errorBody.error as Record<string, unknown> | undefined;\n if (nestedError && typeof nestedError === \"object\" && nestedError.source) {\n const source = nestedError.source;\n if (source === \"gateway\" || source === \"provider\") {\n return source;\n }\n }\n // Check for direct source field\n if (errorBody.source === \"gateway\" || errorBody.source === \"provider\") {\n return errorBody.source;\n }\n }\n\n // Check error message for gateway-specific patterns\n const msg = error.message.toLowerCase();\n if (msg.includes(\"llm gateway api key\") || msg.includes(\"llm gateway\")) {\n return \"gateway\";\n }\n\n return undefined;\n }\n\n // ==========================================================================\n // Helpers (copied from OpenAI provider)\n // ==========================================================================\n\n private extractExtraOptions(options?: GenerateOptions): Record<string, any> {\n if (!options) return {};\n const { responseFormat, ...rest } = options;\n return rest;\n }\n\n private buildGatewayHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"x-request-id\": randomUUID(),\n };\n\n const exec = ExecutionContext.getOrUndefined();\n if (exec?.executionId) {\n headers[\"x-execution-id\"] = exec.executionId;\n }\n\n const ctx = Tracing.getContext();\n if (ctx) {\n // IDs já estão em formato W3C (hex)\n headers.traceparent = `00-${ctx.traceId}-${ctx.spanId}-01`;\n } else {\n // Still emit a root trace context so the gateway can correlate requests by traceId.\n const traceId = randomBytes(16).toString(\"hex\");\n const spanId = randomBytes(8).toString(\"hex\");\n headers.traceparent = `00-${traceId}-${spanId}-01`;\n }\n\n return headers;\n }\n\n /**\n * Extracts enrichment data from backend HTTP headers.\n * Backend returns this data so SDK can enrich its own span (avoiding duplicate spans).\n */\n private extractEnrichmentFromHeaders(headers: Headers): {\n modelActual?: string;\n fallbackUsed?: boolean;\n usage?: TokenUsage;\n cost?: { amountUsd: string; available: boolean };\n } {\n const result: ReturnType<typeof this.extractEnrichmentFromHeaders> = {};\n\n const modelActual = headers.get(\"x-llm-model-actual\");\n if (modelActual) {\n result.modelActual = modelActual;\n }\n\n const fallbackUsed = headers.get(\"x-llm-fallback-used\");\n if (fallbackUsed) {\n result.fallbackUsed = fallbackUsed === \"true\";\n }\n\n const usageRaw = headers.get(\"x-llm-usage\");\n if (usageRaw) {\n try {\n const usage = JSON.parse(usageRaw);\n result.usage = {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens,\n // Preserve any additional token fields from provider\n ...usage,\n };\n } catch {\n this.logger.warn(\"Failed to parse x-llm-usage header\", { usageRaw });\n }\n }\n\n const cost = headers.get(\"x-llm-cost\");\n const costAvailable = headers.get(\"x-llm-cost-available\");\n if (cost) {\n result.cost = {\n amountUsd: cost,\n available: costAvailable === \"true\",\n };\n }\n\n return result;\n }\n\n protected convertMessages(messages: Message[]): OpenAIClient.Chat.ChatCompletionMessageParam[] {\n // Ensure tools have a preceding assistant message with tool_calls\n const allowedToolCallIds = new Set<string>();\n\n return messages.flatMap((m) => {\n if (m.role === \"tool\") {\n // Skip orphan tool messages (no preceding tool_calls)\n if (!m.tool_call_id || !allowedToolCallIds.has(m.tool_call_id)) {\n return [];\n }\n\n return {\n role: \"tool\",\n tool_call_id: m.tool_call_id!,\n content: typeof m.content === \"string\" ? m.content : JSON.stringify(m.content), // Tool output usually string\n };\n }\n\n if (m.role === \"assistant\") {\n // OpenAI rules:\n // - content is required (string | null)\n // - if tool_calls is present, content can be null\n // - if tool_calls is NOT present, content must be string (cannot be null/empty if strict, but usually empty string is fine)\n\n let assistantContent: string | null = null;\n\n if (typeof m.content === \"string\") {\n assistantContent = m.content;\n }\n\n // If content is null/empty AND no tool_calls, force empty string to avoid API error\n if (!assistantContent && (!m.tool_calls || m.tool_calls.length === 0)) {\n assistantContent = \"\";\n }\n\n if (m.tool_calls) {\n m.tool_calls.forEach((tc) => allowedToolCallIds.add(tc.id));\n }\n\n return {\n role: \"assistant\",\n content: assistantContent,\n tool_calls: m.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n };\n }\n\n // User / System with potential multimodal content\n const content: OpenAIClient.Chat.ChatCompletionContentPart[] | string = Array.isArray(\n m.content,\n )\n ? m.content.map((part) => this.convertContentPart(part))\n : m.content || \"\";\n\n return {\n role: m.role as \"system\" | \"user\",\n content,\n name: m.name,\n } as any;\n });\n }\n\n private convertTools(\n tools?: ToolDefinitionForLLM[],\n ): OpenAIClient.Chat.ChatCompletionTool[] | undefined {\n if (!tools || tools.length === 0) return undefined;\n return tools.map((t) => ({\n type: \"function\",\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n private mapUsage(usage?: OpenAIClient.CompletionUsage): TokenUsage | undefined {\n if (!usage) return undefined;\n return {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: (usage as any).completion_tokens_details?.reasoning_tokens,\n };\n }\n\n private mapFinishReason(reason: string | null): ProviderResponse[\"finishReason\"] {\n switch (reason) {\n case \"stop\":\n return \"stop\";\n case \"length\":\n return \"length\";\n case \"tool_calls\":\n return \"tool_calls\";\n case \"content_filter\":\n return \"content_filter\";\n default:\n return undefined;\n }\n }\n\n private convertContentPart(part: ContentPart): OpenAIClient.Chat.ChatCompletionContentPart {\n if (part.type === \"text\") return { type: \"text\", text: part.text };\n\n if (part.type === \"file\") {\n const { mimeType, source } = part.file;\n if (!mimeType.startsWith(\"image/\")) {\n throw new Error(`LLM Gateway: file mimeType '${mimeType}' is not supported yet`);\n }\n\n const url = source.type === \"url\" ? source.url : `data:${mimeType};base64,${source.base64}`;\n\n return { type: \"image_url\", image_url: { url } };\n }\n\n if (part.type === \"image_url\") {\n return { type: \"image_url\", image_url: { url: part.image_url.url } };\n }\n\n // Exhaustive check - should never reach here with proper ContentPart\n const _exhaustive: never = part;\n throw new Error(`Unsupported content type: ${(_exhaustive as any).type}`);\n }\n}\n"],"mappings":"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","names":["OpenAIClient"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import OpenAIClient, { APIError, ClientOptions } from \"openai\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport {\n IModel,\n Message,\n ProviderResponse,\n StreamChunk,\n ToolDefinitionForLLM,\n GenerateOptions,\n TokenUsage,\n ContentPart,\n ConsoleLogger,\n type LogLevel,\n ExecutionContext,\n Tracing,\n} from \"@nebulaos/core\";\nimport { SpanType, type LLMSpanStartData, type LLMSpanEndData } from \"@nebulaos/types\";\n\n/**\n * Custom error class for LLM Gateway errors.\n * Provides clear, actionable error messages for developers.\n */\nexport class LLMGatewayError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n public readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"LLMGatewayError\";\n }\n}\n\n/**\n * LLM Gateway Provider Configuration\n */\nexport interface LLMGatewayConfig {\n /** API Key from NebulaOS */\n apiKey: string;\n /** Base URL of the NebulaOS LLM Gateway */\n baseUrl?: string;\n /** Route alias (e.g., \"assistente\", \"code-review\") */\n model: string;\n /** Logger verbosity for gateway calls */\n logLevel?: LogLevel;\n /** Optional OpenAI client options */\n clientOptions?: ClientOptions;\n /**\n * Default model options passed to every generate() call.\n * Supports provider-specific params like reasoning_effort, temperature, topK, etc.\n * These can be overridden by options passed directly to generate().\n */\n options?: Omit<GenerateOptions, \"responseFormat\">;\n}\n\n/**\n * NebulaOS LLM Gateway Provider\n *\n * Provides access to NebulaOS LLM Gateway routes through an OpenAI-compatible interface.\n * Routes are pre-configured in NebulaOS and provide automatic fallback, cost tracking,\n * and access control.\n */\nexport class LLMGateway implements IModel {\n providerName = \"llm-gateway\";\n modelName: string;\n private client: OpenAIClient;\n private baseUrl: string;\n private logger: ConsoleLogger;\n private options?: Omit<GenerateOptions, \"responseFormat\">;\n\n capabilities = {\n inputFiles: {\n mimeTypes: [\"image/*\"],\n sources: [\"url\", \"base64\"] as const,\n },\n } as const;\n\n constructor(config: LLMGatewayConfig) {\n this.modelName = config.model;\n\n this.baseUrl = config.baseUrl || \"http://localhost:4100\";\n const baseURL = this.baseUrl.endsWith(\"/v1\") ? this.baseUrl : `${this.baseUrl}/v1`;\n this.logger = new ConsoleLogger(config.logLevel || \"info\", \"nebulaos/llm-gateway\");\n\n this.client = new OpenAIClient({\n apiKey: config.apiKey,\n baseURL,\n ...config.clientOptions,\n });\n this.options = config.options;\n }\n\n async generate(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): Promise<ProviderResponse> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n // Extract LLM config params (temperature, thinkingLevel, topP, maxTokens, etc)\n const { responseFormat, ...llmConfig } = mergedOptions ?? {};\n\n // Typed span start data for LLM calls\n const startData: LLMSpanStartData = {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : undefined,\n responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n };\n\n return Tracing.withSpan(\n {\n kind: SpanType.llm,\n name: `llm:${this.modelName}`,\n data: startData,\n },\n async (llmSpan) => {\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway request\", {\n model,\n baseUrl: this.baseUrl,\n stream: false,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n try {\n // Use .withResponse() to access HTTP headers for backend enrichment data\n const { data: response, response: httpResponse } = await this.client.chat.completions\n .create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n )\n .withResponse();\n\n this.logger.debug(\"LLM Gateway response\", {\n model,\n finishReason: response.choices?.[0]?.finish_reason,\n hasUsage: Boolean(response.usage),\n });\n\n const choice = response.choices[0];\n const message = choice.message;\n const usage = this.mapUsage(response.usage);\n const finishReason = this.mapFinishReason(choice.finish_reason);\n\n // Read enrichment headers from backend (model actual, cost, usage, fallback)\n const enrichment = this.extractEnrichmentFromHeaders(httpResponse.headers);\n\n // Typed span end data for LLM calls\n const endData: LLMSpanEndData = {\n usage: enrichment.usage ?? usage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n finishReason: finishReason ?? \"stop\",\n toolCallsCount: message.tool_calls?.length ?? 0,\n choices: response.choices,\n model: enrichment.modelActual,\n fallbackUsed: enrichment.fallbackUsed,\n cost: enrichment.cost ? parseFloat(enrichment.cost.amountUsd) : undefined,\n };\n\n await llmSpan.end({\n status: \"success\",\n data: endData,\n });\n\n return {\n content: message.content || \"\",\n toolCalls: message.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n finishReason,\n usage: enrichment.usage ?? usage,\n };\n } catch (error) {\n this.logger.error(\"LLM Gateway request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n // Typed span end data for error case\n const errorEndData: LLMSpanEndData = {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n };\n\n await llmSpan.end({\n status: \"error\",\n data: errorEndData,\n });\n\n throw gatewayError;\n }\n },\n );\n }\n\n async *generateStream(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): AsyncGenerator<StreamChunk> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n // Extract LLM config params (temperature, thinkingLevel, topP, maxTokens, etc)\n const { responseFormat, ...llmConfig } = mergedOptions ?? {};\n\n // Typed span start data for LLM calls\n const startData: LLMSpanStartData = {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : undefined,\n responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n };\n\n // Start span manually for streaming (async generators can't use withSpan directly)\n const llmSpan = await Tracing.startSpan({\n kind: SpanType.llm,\n name: `llm:${this.modelName}`,\n data: startData,\n });\n\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway stream request\", {\n model,\n baseUrl: this.baseUrl,\n stream: true,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n let stream;\n try {\n stream = await this.client.chat.completions.create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n stream: true,\n stream_options: { include_usage: true },\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n );\n } catch (error) {\n this.logger.error(\"LLM Gateway stream request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n // Typed span end data for error case\n const errorEndData: LLMSpanEndData = {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n };\n\n await llmSpan.end({\n status: \"error\",\n data: errorEndData,\n });\n }\n\n throw gatewayError;\n }\n\n let finalUsage: TokenUsage | undefined;\n let finalFinishReason: ProviderResponse[\"finishReason\"];\n let toolCallsCount = 0;\n let outputPreview = \"\";\n let finalContent = \"\";\n const toolCallsAccumulator: Map<number, { id: string; name: string; arguments: string }> = new Map();\n\n try {\n for await (const chunk of stream) {\n if (chunk.usage) {\n finalUsage = this.mapUsage(chunk.usage);\n yield {\n type: \"finish\",\n reason: \"stop\",\n usage: finalUsage,\n };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice) continue;\n\n if (choice.finish_reason) {\n finalFinishReason = this.mapFinishReason(choice.finish_reason);\n yield {\n type: \"finish\",\n reason: finalFinishReason,\n };\n }\n\n const delta = choice.delta;\n if (!delta) continue;\n\n if (delta.content) {\n finalContent += delta.content;\n if (outputPreview.length < 200) {\n outputPreview += delta.content.slice(0, 200 - outputPreview.length);\n }\n yield { type: \"content_delta\", delta: delta.content };\n }\n\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index;\n if (tc.id && tc.function?.name) {\n toolCallsCount++;\n toolCallsAccumulator.set(idx, { id: tc.id, name: tc.function.name, arguments: \"\" });\n yield {\n type: \"tool_call_start\",\n index: idx,\n id: tc.id,\n name: tc.function.name,\n };\n }\n\n if (tc.function?.arguments) {\n const existing = toolCallsAccumulator.get(idx);\n if (existing) {\n existing.arguments += tc.function.arguments;\n }\n yield {\n type: \"tool_call_delta\",\n index: idx,\n args: tc.function.arguments,\n };\n }\n }\n }\n }\n\n // Build choices for observability\n const toolCalls = Array.from(toolCallsAccumulator.values()).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n }));\n\n const choices = [{\n index: 0,\n message: {\n role: \"assistant\" as const,\n content: finalContent || null,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n },\n finish_reason: finalFinishReason,\n }];\n\n // Typed span end data for success case\n if (llmSpan) {\n const endData: LLMSpanEndData = {\n usage: finalUsage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n finishReason: finalFinishReason ?? \"stop\",\n toolCallsCount,\n outputPreview,\n choices,\n };\n\n await llmSpan.end({\n status: \"success\",\n data: endData,\n });\n }\n } catch (error) {\n this.logger.error(\"LLM Gateway stream failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n // Typed span end data for error case\n const errorEndData: LLMSpanEndData = {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n };\n\n await llmSpan.end({\n status: \"error\",\n data: errorEndData,\n });\n }\n\n throw gatewayError;\n }\n }\n\n // ==========================================================================\n // Error Handling\n // ==========================================================================\n\n /**\n * Transforms raw errors into actionable LLMGatewayError with clear messages.\n * This ensures developers get specific guidance on how to resolve issues.\n *\n * Differentiates between:\n * - Gateway errors: LLM Gateway API key issues (check NEBULAOS_API_KEY env var)\n * - Provider errors: LLM provider API key issues (check route config in dashboard)\n */\n private handleError(error: unknown): LLMGatewayError {\n // Handle OpenAI SDK APIError (includes status, message, code)\n if (error instanceof APIError) {\n const status = error.status;\n const originalMessage = error.message;\n\n // Check X-Error-Source header to differentiate gateway vs provider errors\n const errorSource = this.extractErrorSource(error);\n\n // Authentication errors (401)\n if (status === 401) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway authentication failed: Your LLM Gateway API key is invalid or expired. ` +\n `Please verify your NEBULAOS_API_KEY environment variable or check your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_AUTH_ERROR\",\n status,\n error,\n );\n } else {\n // Provider error (default for 401 without explicit gateway source)\n return new LLMGatewayError(\n `LLM Provider authentication failed: The API key configured for your LLM provider (OpenAI, Azure, etc.) is invalid or expired. ` +\n `Please verify the provider API key in your route configuration in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_AUTH_ERROR\",\n status,\n error,\n );\n }\n }\n\n // Permission denied (403)\n if (status === 403) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway access denied: Your LLM Gateway API key does not have permission to access this route. ` +\n `Please verify the route is allowed for your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_FORBIDDEN\",\n status,\n error,\n );\n } else {\n return new LLMGatewayError(\n `LLM Provider access denied: The provider API key does not have permission for this operation. ` +\n `Please verify the provider API key permissions in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_FORBIDDEN\",\n status,\n error,\n );\n }\n }\n\n // Rate limit (429)\n if (status === 429) {\n return new LLMGatewayError(\n `LLM Gateway rate limit exceeded: Too many requests to the LLM provider. ` +\n `Please wait before retrying or check your rate limit configuration. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_RATE_LIMIT\",\n status,\n error,\n );\n }\n\n // Bad request (400)\n if (status === 400) {\n return new LLMGatewayError(\n `LLM Gateway request error: Invalid request parameters. ` +\n `Please check your request configuration (model, messages, tools). ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_BAD_REQUEST\",\n status,\n error,\n );\n }\n\n // Not found (404)\n if (status === 404) {\n return new LLMGatewayError(\n `LLM Gateway route not found: The specified model or route does not exist. ` +\n `Please verify the route alias '${this.modelName}' is correct and provisioned. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_NOT_FOUND\",\n status,\n error,\n );\n }\n\n // Timeout (408, 504)\n if (status === 408 || status === 504) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The request took too long to complete. ` +\n `This may be due to high load or a complex request. Please try again. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_TIMEOUT\",\n status,\n error,\n );\n }\n\n // Server errors (5xx)\n if (status && status >= 500) {\n return new LLMGatewayError(\n `LLM Gateway server error: The LLM provider returned an error (${status}). ` +\n `This is typically a temporary issue. Please try again later. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_SERVER_ERROR\",\n status,\n error,\n );\n }\n\n // Other API errors\n return new LLMGatewayError(\n `LLM Gateway error (${status}): ${originalMessage}`,\n \"LLM_GATEWAY_ERROR\",\n status,\n error,\n );\n }\n\n // Handle standard Error objects\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n // Connection errors\n if (msg.includes(\"econnrefused\") || msg.includes(\"enotfound\") || msg.includes(\"network\")) {\n return new LLMGatewayError(\n `LLM Gateway connection failed: Unable to connect to the LLM Gateway at ${this.baseUrl}. ` +\n `Please verify the gateway is running and accessible. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_CONNECTION_ERROR\",\n undefined,\n error,\n );\n }\n\n // Timeout errors\n if (msg.includes(\"timeout\") || msg.includes(\"timed out\") || msg.includes(\"etimedout\")) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The connection timed out. ` +\n `Please check network connectivity and try again. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_TIMEOUT\",\n undefined,\n error,\n );\n }\n\n // Generic Error - preserve original message with context\n return new LLMGatewayError(\n `LLM Gateway error: ${error.message}`,\n \"LLM_GATEWAY_ERROR\",\n undefined,\n error,\n );\n }\n\n // Unknown error type\n return new LLMGatewayError(\n `LLM Gateway error: An unexpected error occurred. Details: ${String(error)}`,\n \"LLM_GATEWAY_UNKNOWN_ERROR\",\n undefined,\n error,\n );\n }\n\n /**\n * Extracts the error source from an APIError.\n * The backend sets X-Error-Source header or includes source in the error body\n * to differentiate between gateway errors (LLM Gateway API key) and provider errors.\n *\n * @returns \"gateway\" if the error is from LLM Gateway authentication,\n * \"provider\" if the error is from the upstream LLM provider,\n * undefined if the source cannot be determined.\n */\n private extractErrorSource(error: APIError): \"gateway\" | \"provider\" | undefined {\n // Try to get source from response headers\n const headers = error.headers;\n if (headers) {\n const errorSource = headers[\"x-error-source\"] || headers[\"X-Error-Source\"];\n if (errorSource === \"gateway\" || errorSource === \"provider\") {\n return errorSource;\n }\n }\n\n // Try to get source from error body\n // The backend may include { error: { source: \"gateway\" | \"provider\", ... } }\n const errorBody = error.error as Record<string, unknown> | undefined;\n if (errorBody && typeof errorBody === \"object\") {\n // Check for nested error object (OpenAI style)\n const nestedError = errorBody.error as Record<string, unknown> | undefined;\n if (nestedError && typeof nestedError === \"object\" && nestedError.source) {\n const source = nestedError.source;\n if (source === \"gateway\" || source === \"provider\") {\n return source;\n }\n }\n // Check for direct source field\n if (errorBody.source === \"gateway\" || errorBody.source === \"provider\") {\n return errorBody.source;\n }\n }\n\n // Check error message for gateway-specific patterns\n const msg = error.message.toLowerCase();\n if (msg.includes(\"llm gateway api key\") || msg.includes(\"llm gateway\")) {\n return \"gateway\";\n }\n\n return undefined;\n }\n\n // ==========================================================================\n // Helpers (copied from OpenAI provider)\n // ==========================================================================\n\n private extractExtraOptions(options?: GenerateOptions): Record<string, any> {\n if (!options) return {};\n const { responseFormat, ...rest } = options;\n return rest;\n }\n\n private buildGatewayHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"x-request-id\": randomUUID(),\n };\n\n const exec = ExecutionContext.getOrUndefined();\n if (exec?.executionId) {\n headers[\"x-execution-id\"] = exec.executionId;\n }\n\n const ctx = Tracing.getContext();\n if (ctx) {\n // IDs já estão em formato W3C (hex)\n headers.traceparent = `00-${ctx.traceId}-${ctx.spanId}-01`;\n } else {\n // Still emit a root trace context so the gateway can correlate requests by traceId.\n const traceId = randomBytes(16).toString(\"hex\");\n const spanId = randomBytes(8).toString(\"hex\");\n headers.traceparent = `00-${traceId}-${spanId}-01`;\n }\n\n return headers;\n }\n\n /**\n * Extracts enrichment data from backend HTTP headers.\n * Backend returns this data so SDK can enrich its own span (avoiding duplicate spans).\n */\n private extractEnrichmentFromHeaders(headers: Headers): {\n modelActual?: string;\n fallbackUsed?: boolean;\n usage?: TokenUsage;\n cost?: { amountUsd: string; available: boolean };\n } {\n const result: ReturnType<typeof this.extractEnrichmentFromHeaders> = {};\n\n const modelActual = headers.get(\"x-llm-model-actual\");\n if (modelActual) {\n result.modelActual = modelActual;\n }\n\n const fallbackUsed = headers.get(\"x-llm-fallback-used\");\n if (fallbackUsed) {\n result.fallbackUsed = fallbackUsed === \"true\";\n }\n\n const usageRaw = headers.get(\"x-llm-usage\");\n if (usageRaw) {\n try {\n const usage = JSON.parse(usageRaw);\n result.usage = {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens,\n // Preserve any additional token fields from provider\n ...usage,\n };\n } catch {\n this.logger.warn(\"Failed to parse x-llm-usage header\", { usageRaw });\n }\n }\n\n const cost = headers.get(\"x-llm-cost\");\n const costAvailable = headers.get(\"x-llm-cost-available\");\n if (cost) {\n result.cost = {\n amountUsd: cost,\n available: costAvailable === \"true\",\n };\n }\n\n return result;\n }\n\n protected convertMessages(messages: Message[]): OpenAIClient.Chat.ChatCompletionMessageParam[] {\n // Ensure tools have a preceding assistant message with tool_calls\n const allowedToolCallIds = new Set<string>();\n\n return messages.flatMap((m) => {\n if (m.role === \"tool\") {\n // Skip orphan tool messages (no preceding tool_calls)\n if (!m.tool_call_id || !allowedToolCallIds.has(m.tool_call_id)) {\n return [];\n }\n\n return {\n role: \"tool\",\n tool_call_id: m.tool_call_id!,\n content: typeof m.content === \"string\" ? m.content : JSON.stringify(m.content), // Tool output usually string\n };\n }\n\n if (m.role === \"assistant\") {\n // OpenAI rules:\n // - content is required (string | null)\n // - if tool_calls is present, content can be null\n // - if tool_calls is NOT present, content must be string (cannot be null/empty if strict, but usually empty string is fine)\n\n let assistantContent: string | null = null;\n\n if (typeof m.content === \"string\") {\n assistantContent = m.content;\n }\n\n // If content is null/empty AND no tool_calls, force empty string to avoid API error\n if (!assistantContent && (!m.tool_calls || m.tool_calls.length === 0)) {\n assistantContent = \"\";\n }\n\n if (m.tool_calls) {\n m.tool_calls.forEach((tc) => allowedToolCallIds.add(tc.id));\n }\n\n return {\n role: \"assistant\",\n content: assistantContent,\n tool_calls: m.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n };\n }\n\n // User / System with potential multimodal content\n const content: OpenAIClient.Chat.ChatCompletionContentPart[] | string = Array.isArray(\n m.content,\n )\n ? m.content.map((part) => this.convertContentPart(part))\n : m.content || \"\";\n\n return {\n role: m.role as \"system\" | \"user\",\n content,\n name: m.name,\n } as any;\n });\n }\n\n private convertTools(\n tools?: ToolDefinitionForLLM[],\n ): OpenAIClient.Chat.ChatCompletionTool[] | undefined {\n if (!tools || tools.length === 0) return undefined;\n return tools.map((t) => ({\n type: \"function\",\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n private mapUsage(usage?: OpenAIClient.CompletionUsage): TokenUsage | undefined {\n if (!usage) return undefined;\n return {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: (usage as any).completion_tokens_details?.reasoning_tokens,\n };\n }\n\n private mapFinishReason(reason: string | null): ProviderResponse[\"finishReason\"] {\n switch (reason) {\n case \"stop\":\n return \"stop\";\n case \"length\":\n return \"length\";\n case \"tool_calls\":\n return \"tool_calls\";\n case \"content_filter\":\n return \"content_filter\";\n default:\n return undefined;\n }\n }\n\n private convertContentPart(part: ContentPart): OpenAIClient.Chat.ChatCompletionContentPart {\n if (part.type === \"text\") return { type: \"text\", text: part.text };\n\n if (part.type === \"file\") {\n const { mimeType, source } = part.file;\n if (!mimeType.startsWith(\"image/\")) {\n throw new Error(`LLM Gateway: file mimeType '${mimeType}' is not supported yet`);\n }\n\n const url = source.type === \"url\" ? source.url : `data:${mimeType};base64,${source.base64}`;\n\n return { type: \"image_url\", image_url: { url } };\n }\n\n if (part.type === \"image_url\") {\n return { type: \"image_url\", image_url: { url: part.image_url.url } };\n }\n\n // Exhaustive check - should never reach here with proper ContentPart\n const _exhaustive: never = part;\n throw new Error(`Unsupported content type: ${(_exhaustive as any).type}`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAsD;AACtD,yBAAwC;AACxC,kBAaO;AACP,mBAAqE;AAM9D,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzC,YACE,SACgB,MACA,QACA,OAChB;AACA,UAAM,OAAO;AAJG;AACA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AA+BO,IAAM,aAAN,MAAmC;AAAA,EACxC,eAAe;AAAA,EACf;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,eAAe;AAAA,IACb,YAAY;AAAA,MACV,WAAW,CAAC,SAAS;AAAA,MACrB,SAAS,CAAC,OAAO,QAAQ;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,YAAY,QAA0B;AACpC,SAAK,YAAY,OAAO;AAExB,SAAK,UAAU,OAAO,WAAW;AACjC,UAAM,UAAU,KAAK,QAAQ,SAAS,KAAK,IAAI,KAAK,UAAU,GAAG,KAAK,OAAO;AAC7E,SAAK,SAAS,IAAI,0BAAc,OAAO,YAAY,QAAQ,sBAAsB;AAEjF,SAAK,SAAS,IAAI,cAAAA,QAAa;AAAA,MAC7B,QAAQ,OAAO;AAAA,MACf;AAAA,MACA,GAAG,OAAO;AAAA,IACZ,CAAC;AACD,SAAK,UAAU,OAAO;AAAA,EACxB;AAAA,EAEA,MAAM,SACJ,UACA,OACA,SAC2B;AAC3B,UAAM,gBAAgB,EAAE,GAAG,KAAK,SAAS,GAAG,QAAQ;AACpD,UAAM,QAAQ,SAAS,KAAK,SAAS;AAGrC,UAAM,kBAAkB,SAAS,IAAI,CAAC,OAAO;AAAA,MAC3C,MAAM,EAAE;AAAA,MACR,SAAS,OAAO,EAAE,YAAY,WAC1B,EAAE,QAAQ,SAAS,MAAM,EAAE,QAAQ,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE,UAC7D,MAAM,QAAQ,EAAE,OAAO,IAAI,IAAI,EAAE,QAAQ,MAAM,YAAY,OAAO,EAAE,OAAO;AAAA,IACjF,EAAE;AACF,UAAM,eAAe,OAAO,IAAI,CAAC,OAAO;AAAA,MACtC,MAAM,EAAE,SAAS;AAAA,MACjB,aAAa,EAAE,SAAS,aAAa,MAAM,GAAG,GAAG;AAAA,IACnD,EAAE;AAGF,UAAM,EAAE,gBAAgB,GAAG,UAAU,IAAI,iBAAiB,CAAC;AAG3D,UAAM,YAA8B;AAAA,MAClC,UAAU,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACZ,eAAe,SAAS;AAAA,MACxB,YAAY,OAAO,UAAU;AAAA,MAC7B,WAAW,OAAO,KAAK,SAAS,EAAE,SAAS,IAAI,YAAY;AAAA,MAC3D;AAAA,MACA,UAAU;AAAA,MACV,OAAO;AAAA,IACT;AAEA,WAAO,oBAAQ;AAAA,MACb;AAAA,QACE,MAAM,sBAAS;AAAA,QACf,MAAM,OAAO,KAAK,SAAS;AAAA,QAC3B,MAAM;AAAA,MACR;AAAA,MACA,OAAO,YAAY;AACjB,cAAM,UAAU,KAAK,oBAAoB;AACzC,aAAK,OAAO,MAAM,uBAAuB;AAAA,UACvC;AAAA,UACA,SAAS,KAAK;AAAA,UACd,QAAQ;AAAA,UACR,cAAc,SAAS;AAAA,UACvB,WAAW,OAAO,UAAU;AAAA,QAC9B,CAAC;AAED,YAAI;AAEF,gBAAM,EAAE,MAAM,UAAU,UAAU,aAAa,IAAI,MAAM,KAAK,OAAO,KAAK,YACvE;AAAA,YACC;AAAA,cACE;AAAA,cACA,UAAU,KAAK,gBAAgB,QAAQ;AAAA,cACvC,OAAO,KAAK,aAAa,KAAK;AAAA,cAC9B,iBACE,eAAe,gBAAgB,SAAS,SACpC,cAAc,eAAe,SAC3B;AAAA,gBACE,MAAM;AAAA,gBACN,aAAa,EAAE,MAAM,YAAY,QAAQ,cAAc,eAAe,OAAc;AAAA,cACtF,IACA,EAAE,MAAM,cAAc,IACxB;AAAA,cACN,GAAG,KAAK,oBAAoB,aAAa;AAAA,YAC3C;AAAA,YACA,EAAE,QAAQ;AAAA,UACZ,EACC,aAAa;AAEhB,eAAK,OAAO,MAAM,wBAAwB;AAAA,YACxC;AAAA,YACA,cAAc,SAAS,UAAU,CAAC,GAAG;AAAA,YACrC,UAAU,QAAQ,SAAS,KAAK;AAAA,UAClC,CAAC;AAED,gBAAM,SAAS,SAAS,QAAQ,CAAC;AACjC,gBAAM,UAAU,OAAO;AACvB,gBAAM,QAAQ,KAAK,SAAS,SAAS,KAAK;AAC1C,gBAAM,eAAe,KAAK,gBAAgB,OAAO,aAAa;AAG9D,gBAAM,aAAa,KAAK,6BAA6B,aAAa,OAAO;AAGzE,gBAAM,UAA0B;AAAA,YAC9B,OAAO,WAAW,SAAS,SAAS,EAAE,cAAc,GAAG,kBAAkB,GAAG,aAAa,EAAE;AAAA,YAC3F,cAAc,gBAAgB;AAAA,YAC9B,gBAAgB,QAAQ,YAAY,UAAU;AAAA,YAC9C,SAAS,SAAS;AAAA,YAClB,OAAO,WAAW;AAAA,YAClB,cAAc,WAAW;AAAA,YACzB,MAAM,WAAW,OAAO,WAAW,WAAW,KAAK,SAAS,IAAI;AAAA,UAClE;AAEA,gBAAM,QAAQ,IAAI;AAAA,YAChB,QAAQ;AAAA,YACR,MAAM;AAAA,UACR,CAAC;AAED,iBAAO;AAAA,YACL,SAAS,QAAQ,WAAW;AAAA,YAC5B,WAAW,QAAQ,YAAY,IAAI,CAAC,QAAQ;AAAA,cAC1C,IAAI,GAAG;AAAA,cACP,MAAM;AAAA,cACN,UAAU;AAAA,gBACR,MAAM,GAAG,SAAS;AAAA,gBAClB,WAAW,GAAG,SAAS;AAAA,cACzB;AAAA,YACF,EAAE;AAAA,YACF;AAAA,YACA,OAAO,WAAW,SAAS;AAAA,UAC7B;AAAA,QACF,SAAS,OAAO;AACd,eAAK,OAAO,MAAM,8BAA8B,OAAO,QAAW,MAAS;AAC3E,gBAAM,eAAe,KAAK,YAAY,KAAK;AAG3C,gBAAM,eAA+B;AAAA,YACnC,OAAO;AAAA,cACL,SAAS,aAAa;AAAA,cACtB,MAAM,aAAa;AAAA,cACnB,QAAQ,aAAa;AAAA,YACvB;AAAA,UACF;AAEA,gBAAM,QAAQ,IAAI;AAAA,YAChB,QAAQ;AAAA,YACR,MAAM;AAAA,UACR,CAAC;AAED,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,eACL,UACA,OACA,SAC6B;AAC7B,UAAM,gBAAgB,EAAE,GAAG,KAAK,SAAS,GAAG,QAAQ;AACpD,UAAM,QAAQ,SAAS,KAAK,SAAS;AAGrC,UAAM,kBAAkB,SAAS,IAAI,CAAC,OAAO;AAAA,MAC3C,MAAM,EAAE;AAAA,MACR,SAAS,OAAO,EAAE,YAAY,WAC1B,EAAE,QAAQ,SAAS,MAAM,EAAE,QAAQ,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE,UAC7D,MAAM,QAAQ,EAAE,OAAO,IAAI,IAAI,EAAE,QAAQ,MAAM,YAAY,OAAO,EAAE,OAAO;AAAA,IACjF,EAAE;AACF,UAAM,eAAe,OAAO,IAAI,CAAC,OAAO;AAAA,MACtC,MAAM,EAAE,SAAS;AAAA,MACjB,aAAa,EAAE,SAAS,aAAa,MAAM,GAAG,GAAG;AAAA,IACnD,EAAE;AAGF,UAAM,EAAE,gBAAgB,GAAG,UAAU,IAAI,iBAAiB,CAAC;AAG3D,UAAM,YAA8B;AAAA,MAClC,UAAU,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACZ,eAAe,SAAS;AAAA,MACxB,YAAY,OAAO,UAAU;AAAA,MAC7B,WAAW,OAAO,KAAK,SAAS,EAAE,SAAS,IAAI,YAAY;AAAA,MAC3D;AAAA,MACA,UAAU;AAAA,MACV,OAAO;AAAA,IACT;AAGA,UAAM,UAAU,MAAM,oBAAQ,UAAU;AAAA,MACtC,MAAM,sBAAS;AAAA,MACf,MAAM,OAAO,KAAK,SAAS;AAAA,MAC3B,MAAM;AAAA,IACR,CAAC;AAED,UAAM,UAAU,KAAK,oBAAoB;AACzC,SAAK,OAAO,MAAM,8BAA8B;AAAA,MAC9C;AAAA,MACA,SAAS,KAAK;AAAA,MACd,QAAQ;AAAA,MACR,cAAc,SAAS;AAAA,MACvB,WAAW,OAAO,UAAU;AAAA,IAC9B,CAAC;AAED,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,QAC1C;AAAA,UACE;AAAA,UACA,UAAU,KAAK,gBAAgB,QAAQ;AAAA,UACvC,OAAO,KAAK,aAAa,KAAK;AAAA,UAC9B,QAAQ;AAAA,UACR,gBAAgB,EAAE,eAAe,KAAK;AAAA,UACtC,iBACE,eAAe,gBAAgB,SAAS,SACpC,cAAc,eAAe,SAC3B;AAAA,YACE,MAAM;AAAA,YACN,aAAa,EAAE,MAAM,YAAY,QAAQ,cAAc,eAAe,OAAc;AAAA,UACtF,IACA,EAAE,MAAM,cAAc,IACxB;AAAA,UACN,GAAG,KAAK,oBAAoB,aAAa;AAAA,QAC3C;AAAA,QACA,EAAE,QAAQ;AAAA,MACZ;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,qCAAqC,OAAO,QAAW,MAAS;AAClF,YAAM,eAAe,KAAK,YAAY,KAAK;AAE3C,UAAI,SAAS;AAEX,cAAM,eAA+B;AAAA,UACnC,OAAO;AAAA,YACL,SAAS,aAAa;AAAA,YACtB,MAAM,aAAa;AAAA,YACnB,QAAQ,aAAa;AAAA,UACvB;AAAA,QACF;AAEA,cAAM,QAAQ,IAAI;AAAA,UAChB,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAEA,YAAM;AAAA,IACR;AAEA,QAAI;AACJ,QAAI;AACJ,QAAI,iBAAiB;AACrB,QAAI,gBAAgB;AACpB,QAAI,eAAe;AACnB,UAAM,uBAAqF,oBAAI,IAAI;AAEnG,QAAI;AACF,uBAAiB,SAAS,QAAQ;AAChC,YAAI,MAAM,OAAO;AACf,uBAAa,KAAK,SAAS,MAAM,KAAK;AACtC,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,OAAO;AAAA,UACT;AAAA,QACF;AAEA,cAAM,SAAS,MAAM,UAAU,CAAC;AAChC,YAAI,CAAC,OAAQ;AAEb,YAAI,OAAO,eAAe;AACxB,8BAAoB,KAAK,gBAAgB,OAAO,aAAa;AAC7D,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,QAAQ;AAAA,UACV;AAAA,QACF;AAEA,cAAM,QAAQ,OAAO;AACrB,YAAI,CAAC,MAAO;AAEZ,YAAI,MAAM,SAAS;AACjB,0BAAgB,MAAM;AACtB,cAAI,cAAc,SAAS,KAAK;AAC9B,6BAAiB,MAAM,QAAQ,MAAM,GAAG,MAAM,cAAc,MAAM;AAAA,UACpE;AACA,gBAAM,EAAE,MAAM,iBAAiB,OAAO,MAAM,QAAQ;AAAA,QACtD;AAEA,YAAI,MAAM,YAAY;AACpB,qBAAW,MAAM,MAAM,YAAY;AACjC,kBAAM,MAAM,GAAG;AACf,gBAAI,GAAG,MAAM,GAAG,UAAU,MAAM;AAC9B;AACA,mCAAqB,IAAI,KAAK,EAAE,IAAI,GAAG,IAAI,MAAM,GAAG,SAAS,MAAM,WAAW,GAAG,CAAC;AAClF,oBAAM;AAAA,gBACJ,MAAM;AAAA,gBACN,OAAO;AAAA,gBACP,IAAI,GAAG;AAAA,gBACP,MAAM,GAAG,SAAS;AAAA,cACpB;AAAA,YACF;AAEA,gBAAI,GAAG,UAAU,WAAW;AAC1B,oBAAM,WAAW,qBAAqB,IAAI,GAAG;AAC7C,kBAAI,UAAU;AACZ,yBAAS,aAAa,GAAG,SAAS;AAAA,cACpC;AACA,oBAAM;AAAA,gBACJ,MAAM;AAAA,gBACN,OAAO;AAAA,gBACP,MAAM,GAAG,SAAS;AAAA,cACpB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,YAAM,YAAY,MAAM,KAAK,qBAAqB,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ;AAAA,QACvE,IAAI,GAAG;AAAA,QACP,MAAM;AAAA,QACN,UAAU,EAAE,MAAM,GAAG,MAAM,WAAW,GAAG,UAAU;AAAA,MACrD,EAAE;AAEF,YAAM,UAAU,CAAC;AAAA,QACf,OAAO;AAAA,QACP,SAAS;AAAA,UACP,MAAM;AAAA,UACN,SAAS,gBAAgB;AAAA,UACzB,YAAY,UAAU,SAAS,IAAI,YAAY;AAAA,QACjD;AAAA,QACA,eAAe;AAAA,MACjB,CAAC;AAGD,UAAI,SAAS;AACX,cAAM,UAA0B;AAAA,UAC9B,OAAO,cAAc,EAAE,cAAc,GAAG,kBAAkB,GAAG,aAAa,EAAE;AAAA,UAC5E,cAAc,qBAAqB;AAAA,UACnC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,cAAM,QAAQ,IAAI;AAAA,UAChB,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,6BAA6B,OAAO,QAAW,MAAS;AAC1E,YAAM,eAAe,KAAK,YAAY,KAAK;AAE3C,UAAI,SAAS;AAEX,cAAM,eAA+B;AAAA,UACnC,OAAO;AAAA,YACL,SAAS,aAAa;AAAA,YACtB,MAAM,aAAa;AAAA,YACnB,QAAQ,aAAa;AAAA,UACvB;AAAA,QACF;AAEA,cAAM,QAAQ,IAAI;AAAA,UAChB,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcQ,YAAY,OAAiC;AAEnD,QAAI,iBAAiB,wBAAU;AAC7B,YAAM,SAAS,MAAM;AACrB,YAAM,kBAAkB,MAAM;AAG9B,YAAM,cAAc,KAAK,mBAAmB,KAAK;AAGjD,UAAI,WAAW,KAAK;AAClB,YAAI,gBAAgB,WAAW;AAC7B,iBAAO,IAAI;AAAA,YACT,4NAEqB,eAAe;AAAA,YACpC;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,OAAO;AAEL,iBAAO,IAAI;AAAA,YACT,2OAEqB,eAAe;AAAA,YACpC;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,UAAI,WAAW,KAAK;AAClB,YAAI,gBAAgB,WAAW;AAC7B,iBAAO,IAAI;AAAA,YACT,iNAEqB,eAAe;AAAA,YACpC;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,OAAO;AACL,iBAAO,IAAI;AAAA,YACT,2LAEqB,eAAe;AAAA,YACpC;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,UAAI,WAAW,KAAK;AAClB,eAAO,IAAI;AAAA,UACT,+JAEqB,eAAe;AAAA,UACpC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,UAAI,WAAW,KAAK;AAClB,eAAO,IAAI;AAAA,UACT,4IAEqB,eAAe;AAAA,UACpC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,UAAI,WAAW,KAAK;AAClB,eAAO,IAAI;AAAA,UACT,4GACoC,KAAK,SAAS,iDAC7B,eAAe;AAAA,UACpC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,UAAI,WAAW,OAAO,WAAW,KAAK;AACpC,eAAO,IAAI;AAAA,UACT,oJAEqB,eAAe;AAAA,UACpC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,UAAI,UAAU,UAAU,KAAK;AAC3B,eAAO,IAAI;AAAA,UACT,iEAAiE,MAAM,mFAElD,eAAe;AAAA,UACpC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,aAAO,IAAI;AAAA,QACT,sBAAsB,MAAM,MAAM,eAAe;AAAA,QACjD;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,iBAAiB,OAAO;AAC1B,YAAM,MAAM,MAAM,QAAQ,YAAY;AAGtC,UAAI,IAAI,SAAS,cAAc,KAAK,IAAI,SAAS,WAAW,KAAK,IAAI,SAAS,SAAS,GAAG;AACxF,eAAO,IAAI;AAAA,UACT,0EAA0E,KAAK,OAAO,0EAEjE,MAAM,OAAO;AAAA,UAClC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,UAAI,IAAI,SAAS,SAAS,KAAK,IAAI,SAAS,WAAW,KAAK,IAAI,SAAS,WAAW,GAAG;AACrF,eAAO,IAAI;AAAA,UACT,mHAEqB,MAAM,OAAO;AAAA,UAClC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,aAAO,IAAI;AAAA,QACT,sBAAsB,MAAM,OAAO;AAAA,QACnC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,WAAO,IAAI;AAAA,MACT,6DAA6D,OAAO,KAAK,CAAC;AAAA,MAC1E;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,mBAAmB,OAAqD;AAE9E,UAAM,UAAU,MAAM;AACtB,QAAI,SAAS;AACX,YAAM,cAAc,QAAQ,gBAAgB,KAAK,QAAQ,gBAAgB;AACzE,UAAI,gBAAgB,aAAa,gBAAgB,YAAY;AAC3D,eAAO;AAAA,MACT;AAAA,IACF;AAIA,UAAM,YAAY,MAAM;AACxB,QAAI,aAAa,OAAO,cAAc,UAAU;AAE9C,YAAM,cAAc,UAAU;AAC9B,UAAI,eAAe,OAAO,gBAAgB,YAAY,YAAY,QAAQ;AACxE,cAAM,SAAS,YAAY;AAC3B,YAAI,WAAW,aAAa,WAAW,YAAY;AACjD,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,UAAI,UAAU,WAAW,aAAa,UAAU,WAAW,YAAY;AACrE,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAGA,UAAM,MAAM,MAAM,QAAQ,YAAY;AACtC,QAAI,IAAI,SAAS,qBAAqB,KAAK,IAAI,SAAS,aAAa,GAAG;AACtE,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMQ,oBAAoB,SAAgD;AAC1E,QAAI,CAAC,QAAS,QAAO,CAAC;AACtB,UAAM,EAAE,gBAAgB,GAAG,KAAK,IAAI;AACpC,WAAO;AAAA,EACT;AAAA,EAEQ,sBAA8C;AACpD,UAAM,UAAkC;AAAA,MACtC,oBAAgB,+BAAW;AAAA,IAC7B;AAEA,UAAM,OAAO,6BAAiB,eAAe;AAC7C,QAAI,MAAM,aAAa;AACrB,cAAQ,gBAAgB,IAAI,KAAK;AAAA,IACnC;AAEA,UAAM,MAAM,oBAAQ,WAAW;AAC/B,QAAI,KAAK;AAEP,cAAQ,cAAc,MAAM,IAAI,OAAO,IAAI,IAAI,MAAM;AAAA,IACvD,OAAO;AAEL,YAAM,cAAU,gCAAY,EAAE,EAAE,SAAS,KAAK;AAC9C,YAAM,aAAS,gCAAY,CAAC,EAAE,SAAS,KAAK;AAC5C,cAAQ,cAAc,MAAM,OAAO,IAAI,MAAM;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,6BAA6B,SAKnC;AACA,UAAM,SAA+D,CAAC;AAEtE,UAAM,cAAc,QAAQ,IAAI,oBAAoB;AACpD,QAAI,aAAa;AACf,aAAO,cAAc;AAAA,IACvB;AAEA,UAAM,eAAe,QAAQ,IAAI,qBAAqB;AACtD,QAAI,cAAc;AAChB,aAAO,eAAe,iBAAiB;AAAA,IACzC;AAEA,UAAM,WAAW,QAAQ,IAAI,aAAa;AAC1C,QAAI,UAAU;AACZ,UAAI;AACF,cAAM,QAAQ,KAAK,MAAM,QAAQ;AACjC,eAAO,QAAQ;AAAA,UACb,cAAc,MAAM;AAAA,UACpB,kBAAkB,MAAM;AAAA,UACxB,aAAa,MAAM;AAAA,UACnB,iBAAiB,MAAM,2BAA2B;AAAA;AAAA,UAElD,GAAG;AAAA,QACL;AAAA,MACF,QAAQ;AACN,aAAK,OAAO,KAAK,sCAAsC,EAAE,SAAS,CAAC;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,OAAO,QAAQ,IAAI,YAAY;AACrC,UAAM,gBAAgB,QAAQ,IAAI,sBAAsB;AACxD,QAAI,MAAM;AACR,aAAO,OAAO;AAAA,QACZ,WAAW;AAAA,QACX,WAAW,kBAAkB;AAAA,MAC/B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEU,gBAAgB,UAAqE;AAE7F,UAAM,qBAAqB,oBAAI,IAAY;AAE3C,WAAO,SAAS,QAAQ,CAAC,MAAM;AAC7B,UAAI,EAAE,SAAS,QAAQ;AAErB,YAAI,CAAC,EAAE,gBAAgB,CAAC,mBAAmB,IAAI,EAAE,YAAY,GAAG;AAC9D,iBAAO,CAAC;AAAA,QACV;AAEA,eAAO;AAAA,UACL,MAAM;AAAA,UACN,cAAc,EAAE;AAAA,UAChB,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,KAAK,UAAU,EAAE,OAAO;AAAA;AAAA,QAC/E;AAAA,MACF;AAEA,UAAI,EAAE,SAAS,aAAa;AAM1B,YAAI,mBAAkC;AAEtC,YAAI,OAAO,EAAE,YAAY,UAAU;AACjC,6BAAmB,EAAE;AAAA,QACvB;AAGA,YAAI,CAAC,qBAAqB,CAAC,EAAE,cAAc,EAAE,WAAW,WAAW,IAAI;AACrE,6BAAmB;AAAA,QACrB;AAEA,YAAI,EAAE,YAAY;AAChB,YAAE,WAAW,QAAQ,CAAC,OAAO,mBAAmB,IAAI,GAAG,EAAE,CAAC;AAAA,QAC5D;AAEA,eAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS;AAAA,UACT,YAAY,EAAE,YAAY,IAAI,CAAC,QAAQ;AAAA,YACrC,IAAI,GAAG;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,cACR,MAAM,GAAG,SAAS;AAAA,cAClB,WAAW,GAAG,SAAS;AAAA,YACzB;AAAA,UACF,EAAE;AAAA,QACJ;AAAA,MACF;AAGA,YAAM,UAAkE,MAAM;AAAA,QAC5E,EAAE;AAAA,MACJ,IACI,EAAE,QAAQ,IAAI,CAAC,SAAS,KAAK,mBAAmB,IAAI,CAAC,IACrD,EAAE,WAAW;AAEjB,aAAO;AAAA,QACL,MAAM,EAAE;AAAA,QACR;AAAA,QACA,MAAM,EAAE;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,aACN,OACoD;AACpD,QAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,WAAO,MAAM,IAAI,CAAC,OAAO;AAAA,MACvB,MAAM;AAAA,MACN,UAAU;AAAA,QACR,MAAM,EAAE,SAAS;AAAA,QACjB,aAAa,EAAE,SAAS;AAAA,QACxB,YAAY,EAAE,SAAS;AAAA,MACzB;AAAA,IACF,EAAE;AAAA,EACJ;AAAA,EAEQ,SAAS,OAA8D;AAC7E,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO;AAAA,MACL,cAAc,MAAM;AAAA,MACpB,kBAAkB,MAAM;AAAA,MACxB,aAAa,MAAM;AAAA,MACnB,iBAAkB,MAAc,2BAA2B;AAAA,IAC7D;AAAA,EACF;AAAA,EAEQ,gBAAgB,QAAyD;AAC/E,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEQ,mBAAmB,MAAgE;AACzF,QAAI,KAAK,SAAS,OAAQ,QAAO,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK;AAEjE,QAAI,KAAK,SAAS,QAAQ;AACxB,YAAM,EAAE,UAAU,OAAO,IAAI,KAAK;AAClC,UAAI,CAAC,SAAS,WAAW,QAAQ,GAAG;AAClC,cAAM,IAAI,MAAM,+BAA+B,QAAQ,wBAAwB;AAAA,MACjF;AAEA,YAAM,MAAM,OAAO,SAAS,QAAQ,OAAO,MAAM,QAAQ,QAAQ,WAAW,OAAO,MAAM;AAEzF,aAAO,EAAE,MAAM,aAAa,WAAW,EAAE,IAAI,EAAE;AAAA,IACjD;AAEA,QAAI,KAAK,SAAS,aAAa;AAC7B,aAAO,EAAE,MAAM,aAAa,WAAW,EAAE,KAAK,KAAK,UAAU,IAAI,EAAE;AAAA,IACrE;AAGA,UAAM,cAAqB;AAC3B,UAAM,IAAI,MAAM,6BAA8B,YAAoB,IAAI,EAAE;AAAA,EAC1E;AACF;","names":["OpenAIClient"]}
|
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ExecutionContext,
|
|
7
7
|
Tracing
|
|
8
8
|
} from "@nebulaos/core";
|
|
9
|
+
import { SpanType } from "@nebulaos/types";
|
|
9
10
|
var LLMGatewayError = class extends Error {
|
|
10
11
|
constructor(message, code, status, cause) {
|
|
11
12
|
super(message);
|
|
@@ -51,19 +52,22 @@ var LLMGateway = class {
|
|
|
51
52
|
name: t.function.name,
|
|
52
53
|
description: t.function.description?.slice(0, 200)
|
|
53
54
|
}));
|
|
55
|
+
const { responseFormat, ...llmConfig } = mergedOptions ?? {};
|
|
56
|
+
const startData = {
|
|
57
|
+
provider: this.providerName,
|
|
58
|
+
model: this.modelName,
|
|
59
|
+
messagesCount: messages.length,
|
|
60
|
+
toolsCount: tools?.length ?? 0,
|
|
61
|
+
llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : void 0,
|
|
62
|
+
responseFormat,
|
|
63
|
+
messages: messagesPreview,
|
|
64
|
+
tools: toolsPreview
|
|
65
|
+
};
|
|
54
66
|
return Tracing.withSpan(
|
|
55
67
|
{
|
|
56
|
-
kind:
|
|
68
|
+
kind: SpanType.llm,
|
|
57
69
|
name: `llm:${this.modelName}`,
|
|
58
|
-
data:
|
|
59
|
-
provider: this.providerName,
|
|
60
|
-
model: this.modelName,
|
|
61
|
-
messagesCount: messages.length,
|
|
62
|
-
toolsCount: tools?.length ?? 0,
|
|
63
|
-
responseFormat: mergedOptions?.responseFormat,
|
|
64
|
-
messages: messagesPreview,
|
|
65
|
-
tools: toolsPreview
|
|
66
|
-
}
|
|
70
|
+
data: startData
|
|
67
71
|
},
|
|
68
72
|
async (llmSpan) => {
|
|
69
73
|
const headers = this.buildGatewayHeaders();
|
|
@@ -98,18 +102,18 @@ var LLMGateway = class {
|
|
|
98
102
|
const usage = this.mapUsage(response.usage);
|
|
99
103
|
const finishReason = this.mapFinishReason(choice.finish_reason);
|
|
100
104
|
const enrichment = this.extractEnrichmentFromHeaders(httpResponse.headers);
|
|
105
|
+
const endData = {
|
|
106
|
+
usage: enrichment.usage ?? usage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
107
|
+
finishReason: finishReason ?? "stop",
|
|
108
|
+
toolCallsCount: message.tool_calls?.length ?? 0,
|
|
109
|
+
choices: response.choices,
|
|
110
|
+
model: enrichment.modelActual,
|
|
111
|
+
fallbackUsed: enrichment.fallbackUsed,
|
|
112
|
+
cost: enrichment.cost ? parseFloat(enrichment.cost.amountUsd) : void 0
|
|
113
|
+
};
|
|
101
114
|
await llmSpan.end({
|
|
102
115
|
status: "success",
|
|
103
|
-
data:
|
|
104
|
-
usage: enrichment.usage ?? usage,
|
|
105
|
-
finishReason,
|
|
106
|
-
toolCallsCount: message.tool_calls?.length ?? 0,
|
|
107
|
-
outputPreview: message.content?.slice(0, 200),
|
|
108
|
-
// Enrichment from backend gateway
|
|
109
|
-
modelActual: enrichment.modelActual,
|
|
110
|
-
fallbackUsed: enrichment.fallbackUsed,
|
|
111
|
-
cost: enrichment.cost
|
|
112
|
-
}
|
|
116
|
+
data: endData
|
|
113
117
|
});
|
|
114
118
|
return {
|
|
115
119
|
content: message.content || "",
|
|
@@ -127,15 +131,16 @@ var LLMGateway = class {
|
|
|
127
131
|
} catch (error) {
|
|
128
132
|
this.logger.error("LLM Gateway request failed", error, void 0, void 0);
|
|
129
133
|
const gatewayError = this.handleError(error);
|
|
134
|
+
const errorEndData = {
|
|
135
|
+
error: {
|
|
136
|
+
message: gatewayError.message,
|
|
137
|
+
code: gatewayError.code,
|
|
138
|
+
status: gatewayError.status
|
|
139
|
+
}
|
|
140
|
+
};
|
|
130
141
|
await llmSpan.end({
|
|
131
142
|
status: "error",
|
|
132
|
-
data:
|
|
133
|
-
error: {
|
|
134
|
-
message: gatewayError.message,
|
|
135
|
-
code: gatewayError.code,
|
|
136
|
-
status: gatewayError.status
|
|
137
|
-
}
|
|
138
|
-
}
|
|
143
|
+
data: errorEndData
|
|
139
144
|
});
|
|
140
145
|
throw gatewayError;
|
|
141
146
|
}
|
|
@@ -153,18 +158,21 @@ var LLMGateway = class {
|
|
|
153
158
|
name: t.function.name,
|
|
154
159
|
description: t.function.description?.slice(0, 200)
|
|
155
160
|
}));
|
|
161
|
+
const { responseFormat, ...llmConfig } = mergedOptions ?? {};
|
|
162
|
+
const startData = {
|
|
163
|
+
provider: this.providerName,
|
|
164
|
+
model: this.modelName,
|
|
165
|
+
messagesCount: messages.length,
|
|
166
|
+
toolsCount: tools?.length ?? 0,
|
|
167
|
+
llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : void 0,
|
|
168
|
+
responseFormat,
|
|
169
|
+
messages: messagesPreview,
|
|
170
|
+
tools: toolsPreview
|
|
171
|
+
};
|
|
156
172
|
const llmSpan = await Tracing.startSpan({
|
|
157
|
-
kind:
|
|
173
|
+
kind: SpanType.llm,
|
|
158
174
|
name: `llm:${this.modelName}`,
|
|
159
|
-
data:
|
|
160
|
-
provider: this.providerName,
|
|
161
|
-
model: this.modelName,
|
|
162
|
-
messagesCount: messages.length,
|
|
163
|
-
toolsCount: tools?.length ?? 0,
|
|
164
|
-
responseFormat: mergedOptions?.responseFormat,
|
|
165
|
-
messages: messagesPreview,
|
|
166
|
-
tools: toolsPreview
|
|
167
|
-
}
|
|
175
|
+
data: startData
|
|
168
176
|
});
|
|
169
177
|
const headers = this.buildGatewayHeaders();
|
|
170
178
|
this.logger.debug("LLM Gateway stream request", {
|
|
@@ -195,15 +203,16 @@ var LLMGateway = class {
|
|
|
195
203
|
this.logger.error("LLM Gateway stream request failed", error, void 0, void 0);
|
|
196
204
|
const gatewayError = this.handleError(error);
|
|
197
205
|
if (llmSpan) {
|
|
206
|
+
const errorEndData = {
|
|
207
|
+
error: {
|
|
208
|
+
message: gatewayError.message,
|
|
209
|
+
code: gatewayError.code,
|
|
210
|
+
status: gatewayError.status
|
|
211
|
+
}
|
|
212
|
+
};
|
|
198
213
|
await llmSpan.end({
|
|
199
214
|
status: "error",
|
|
200
|
-
data:
|
|
201
|
-
error: {
|
|
202
|
-
message: gatewayError.message,
|
|
203
|
-
code: gatewayError.code,
|
|
204
|
-
status: gatewayError.status
|
|
205
|
-
}
|
|
206
|
-
}
|
|
215
|
+
data: errorEndData
|
|
207
216
|
});
|
|
208
217
|
}
|
|
209
218
|
throw gatewayError;
|
|
@@ -212,6 +221,8 @@ var LLMGateway = class {
|
|
|
212
221
|
let finalFinishReason;
|
|
213
222
|
let toolCallsCount = 0;
|
|
214
223
|
let outputPreview = "";
|
|
224
|
+
let finalContent = "";
|
|
225
|
+
const toolCallsAccumulator = /* @__PURE__ */ new Map();
|
|
215
226
|
try {
|
|
216
227
|
for await (const chunk of stream) {
|
|
217
228
|
if (chunk.usage) {
|
|
@@ -234,6 +245,7 @@ var LLMGateway = class {
|
|
|
234
245
|
const delta = choice.delta;
|
|
235
246
|
if (!delta) continue;
|
|
236
247
|
if (delta.content) {
|
|
248
|
+
finalContent += delta.content;
|
|
237
249
|
if (outputPreview.length < 200) {
|
|
238
250
|
outputPreview += delta.content.slice(0, 200 - outputPreview.length);
|
|
239
251
|
}
|
|
@@ -241,49 +253,72 @@ var LLMGateway = class {
|
|
|
241
253
|
}
|
|
242
254
|
if (delta.tool_calls) {
|
|
243
255
|
for (const tc of delta.tool_calls) {
|
|
256
|
+
const idx = tc.index;
|
|
244
257
|
if (tc.id && tc.function?.name) {
|
|
245
258
|
toolCallsCount++;
|
|
259
|
+
toolCallsAccumulator.set(idx, { id: tc.id, name: tc.function.name, arguments: "" });
|
|
246
260
|
yield {
|
|
247
261
|
type: "tool_call_start",
|
|
248
|
-
index:
|
|
262
|
+
index: idx,
|
|
249
263
|
id: tc.id,
|
|
250
264
|
name: tc.function.name
|
|
251
265
|
};
|
|
252
266
|
}
|
|
253
267
|
if (tc.function?.arguments) {
|
|
268
|
+
const existing = toolCallsAccumulator.get(idx);
|
|
269
|
+
if (existing) {
|
|
270
|
+
existing.arguments += tc.function.arguments;
|
|
271
|
+
}
|
|
254
272
|
yield {
|
|
255
273
|
type: "tool_call_delta",
|
|
256
|
-
index:
|
|
274
|
+
index: idx,
|
|
257
275
|
args: tc.function.arguments
|
|
258
276
|
};
|
|
259
277
|
}
|
|
260
278
|
}
|
|
261
279
|
}
|
|
262
280
|
}
|
|
281
|
+
const toolCalls = Array.from(toolCallsAccumulator.values()).map((tc) => ({
|
|
282
|
+
id: tc.id,
|
|
283
|
+
type: "function",
|
|
284
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
285
|
+
}));
|
|
286
|
+
const choices = [{
|
|
287
|
+
index: 0,
|
|
288
|
+
message: {
|
|
289
|
+
role: "assistant",
|
|
290
|
+
content: finalContent || null,
|
|
291
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
292
|
+
},
|
|
293
|
+
finish_reason: finalFinishReason
|
|
294
|
+
}];
|
|
263
295
|
if (llmSpan) {
|
|
296
|
+
const endData = {
|
|
297
|
+
usage: finalUsage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
298
|
+
finishReason: finalFinishReason ?? "stop",
|
|
299
|
+
toolCallsCount,
|
|
300
|
+
outputPreview,
|
|
301
|
+
choices
|
|
302
|
+
};
|
|
264
303
|
await llmSpan.end({
|
|
265
304
|
status: "success",
|
|
266
|
-
data:
|
|
267
|
-
usage: finalUsage,
|
|
268
|
-
finishReason: finalFinishReason,
|
|
269
|
-
toolCallsCount,
|
|
270
|
-
outputPreview
|
|
271
|
-
}
|
|
305
|
+
data: endData
|
|
272
306
|
});
|
|
273
307
|
}
|
|
274
308
|
} catch (error) {
|
|
275
309
|
this.logger.error("LLM Gateway stream failed", error, void 0, void 0);
|
|
276
310
|
const gatewayError = this.handleError(error);
|
|
277
311
|
if (llmSpan) {
|
|
312
|
+
const errorEndData = {
|
|
313
|
+
error: {
|
|
314
|
+
message: gatewayError.message,
|
|
315
|
+
code: gatewayError.code,
|
|
316
|
+
status: gatewayError.status
|
|
317
|
+
}
|
|
318
|
+
};
|
|
278
319
|
await llmSpan.end({
|
|
279
320
|
status: "error",
|
|
280
|
-
data:
|
|
281
|
-
error: {
|
|
282
|
-
message: gatewayError.message,
|
|
283
|
-
code: gatewayError.code,
|
|
284
|
-
status: gatewayError.status
|
|
285
|
-
}
|
|
286
|
-
}
|
|
321
|
+
data: errorEndData
|
|
287
322
|
});
|
|
288
323
|
}
|
|
289
324
|
throw gatewayError;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import OpenAIClient, { APIError, ClientOptions } from \"openai\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport {\n IModel,\n Message,\n ProviderResponse,\n StreamChunk,\n ToolDefinitionForLLM,\n GenerateOptions,\n TokenUsage,\n ContentPart,\n ConsoleLogger,\n type LogLevel,\n ExecutionContext,\n Tracing,\n} from \"@nebulaos/core\";\n\n/**\n * Custom error class for LLM Gateway errors.\n * Provides clear, actionable error messages for developers.\n */\nexport class LLMGatewayError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n public readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"LLMGatewayError\";\n }\n}\n\n/**\n * LLM Gateway Provider Configuration\n */\nexport interface LLMGatewayConfig {\n /** API Key from NebulaOS */\n apiKey: string;\n /** Base URL of the NebulaOS LLM Gateway */\n baseUrl?: string;\n /** Route alias (e.g., \"assistente\", \"code-review\") */\n model: string;\n /** Logger verbosity for gateway calls */\n logLevel?: LogLevel;\n /** Optional OpenAI client options */\n clientOptions?: ClientOptions;\n /**\n * Default model options passed to every generate() call.\n * Supports provider-specific params like reasoning_effort, temperature, topK, etc.\n * These can be overridden by options passed directly to generate().\n */\n options?: Omit<GenerateOptions, \"responseFormat\">;\n}\n\n/**\n * NebulaOS LLM Gateway Provider\n *\n * Provides access to NebulaOS LLM Gateway routes through an OpenAI-compatible interface.\n * Routes are pre-configured in NebulaOS and provide automatic fallback, cost tracking,\n * and access control.\n */\nexport class LLMGateway implements IModel {\n providerName = \"llm-gateway\";\n modelName: string;\n private client: OpenAIClient;\n private baseUrl: string;\n private logger: ConsoleLogger;\n private options?: Omit<GenerateOptions, \"responseFormat\">;\n\n capabilities = {\n inputFiles: {\n mimeTypes: [\"image/*\"],\n sources: [\"url\", \"base64\"] as const,\n },\n } as const;\n\n constructor(config: LLMGatewayConfig) {\n this.modelName = config.model;\n\n this.baseUrl = config.baseUrl || \"http://localhost:4100\";\n const baseURL = this.baseUrl.endsWith(\"/v1\") ? this.baseUrl : `${this.baseUrl}/v1`;\n this.logger = new ConsoleLogger(config.logLevel || \"info\", \"nebulaos/llm-gateway\");\n\n this.client = new OpenAIClient({\n apiKey: config.apiKey,\n baseURL,\n ...config.clientOptions,\n });\n this.options = config.options;\n }\n\n async generate(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): Promise<ProviderResponse> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n return Tracing.withSpan(\n {\n kind: \"llm\",\n name: `llm:${this.modelName}`,\n data: {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n responseFormat: mergedOptions?.responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n },\n },\n async (llmSpan) => {\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway request\", {\n model,\n baseUrl: this.baseUrl,\n stream: false,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n try {\n // Use .withResponse() to access HTTP headers for backend enrichment data\n const { data: response, response: httpResponse } = await this.client.chat.completions\n .create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n )\n .withResponse();\n\n this.logger.debug(\"LLM Gateway response\", {\n model,\n finishReason: response.choices?.[0]?.finish_reason,\n hasUsage: Boolean(response.usage),\n });\n\n const choice = response.choices[0];\n const message = choice.message;\n const usage = this.mapUsage(response.usage);\n const finishReason = this.mapFinishReason(choice.finish_reason);\n\n // Read enrichment headers from backend (model actual, cost, usage, fallback)\n const enrichment = this.extractEnrichmentFromHeaders(httpResponse.headers);\n\n await llmSpan.end({\n status: \"success\",\n data: {\n usage: enrichment.usage ?? usage,\n finishReason,\n toolCallsCount: message.tool_calls?.length ?? 0,\n outputPreview: message.content?.slice(0, 200),\n // Enrichment from backend gateway\n modelActual: enrichment.modelActual,\n fallbackUsed: enrichment.fallbackUsed,\n cost: enrichment.cost,\n },\n });\n\n return {\n content: message.content || \"\",\n toolCalls: message.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n finishReason,\n usage: enrichment.usage ?? usage,\n };\n } catch (error) {\n this.logger.error(\"LLM Gateway request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n await llmSpan.end({\n status: \"error\",\n data: {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n },\n });\n\n throw gatewayError;\n }\n },\n );\n }\n\n async *generateStream(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): AsyncGenerator<StreamChunk> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n // Start span manually for streaming (async generators can't use withSpan directly)\n const llmSpan = await Tracing.startSpan({\n kind: \"llm\",\n name: `llm:${this.modelName}`,\n data: {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n responseFormat: mergedOptions?.responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n },\n });\n\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway stream request\", {\n model,\n baseUrl: this.baseUrl,\n stream: true,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n let stream;\n try {\n stream = await this.client.chat.completions.create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n stream: true,\n stream_options: { include_usage: true },\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n );\n } catch (error) {\n this.logger.error(\"LLM Gateway stream request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n await llmSpan.end({\n status: \"error\",\n data: {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n },\n });\n }\n\n throw gatewayError;\n }\n\n let finalUsage: TokenUsage | undefined;\n let finalFinishReason: ProviderResponse[\"finishReason\"];\n let toolCallsCount = 0;\n let outputPreview = \"\";\n\n try {\n for await (const chunk of stream) {\n if (chunk.usage) {\n finalUsage = this.mapUsage(chunk.usage);\n yield {\n type: \"finish\",\n reason: \"stop\",\n usage: finalUsage,\n };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice) continue;\n\n if (choice.finish_reason) {\n finalFinishReason = this.mapFinishReason(choice.finish_reason);\n yield {\n type: \"finish\",\n reason: finalFinishReason,\n };\n }\n\n const delta = choice.delta;\n if (!delta) continue;\n\n if (delta.content) {\n if (outputPreview.length < 200) {\n outputPreview += delta.content.slice(0, 200 - outputPreview.length);\n }\n yield { type: \"content_delta\", delta: delta.content };\n }\n\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n if (tc.id && tc.function?.name) {\n toolCallsCount++;\n yield {\n type: \"tool_call_start\",\n index: tc.index,\n id: tc.id,\n name: tc.function.name,\n };\n }\n\n if (tc.function?.arguments) {\n yield {\n type: \"tool_call_delta\",\n index: tc.index,\n args: tc.function.arguments,\n };\n }\n }\n }\n }\n\n // End span with success\n if (llmSpan) {\n await llmSpan.end({\n status: \"success\",\n data: {\n usage: finalUsage,\n finishReason: finalFinishReason,\n toolCallsCount,\n outputPreview,\n },\n });\n }\n } catch (error) {\n this.logger.error(\"LLM Gateway stream failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n await llmSpan.end({\n status: \"error\",\n data: {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n },\n });\n }\n\n throw gatewayError;\n }\n }\n\n // ==========================================================================\n // Error Handling\n // ==========================================================================\n\n /**\n * Transforms raw errors into actionable LLMGatewayError with clear messages.\n * This ensures developers get specific guidance on how to resolve issues.\n *\n * Differentiates between:\n * - Gateway errors: LLM Gateway API key issues (check NEBULAOS_API_KEY env var)\n * - Provider errors: LLM provider API key issues (check route config in dashboard)\n */\n private handleError(error: unknown): LLMGatewayError {\n // Handle OpenAI SDK APIError (includes status, message, code)\n if (error instanceof APIError) {\n const status = error.status;\n const originalMessage = error.message;\n\n // Check X-Error-Source header to differentiate gateway vs provider errors\n const errorSource = this.extractErrorSource(error);\n\n // Authentication errors (401)\n if (status === 401) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway authentication failed: Your LLM Gateway API key is invalid or expired. ` +\n `Please verify your NEBULAOS_API_KEY environment variable or check your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_AUTH_ERROR\",\n status,\n error,\n );\n } else {\n // Provider error (default for 401 without explicit gateway source)\n return new LLMGatewayError(\n `LLM Provider authentication failed: The API key configured for your LLM provider (OpenAI, Azure, etc.) is invalid or expired. ` +\n `Please verify the provider API key in your route configuration in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_AUTH_ERROR\",\n status,\n error,\n );\n }\n }\n\n // Permission denied (403)\n if (status === 403) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway access denied: Your LLM Gateway API key does not have permission to access this route. ` +\n `Please verify the route is allowed for your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_FORBIDDEN\",\n status,\n error,\n );\n } else {\n return new LLMGatewayError(\n `LLM Provider access denied: The provider API key does not have permission for this operation. ` +\n `Please verify the provider API key permissions in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_FORBIDDEN\",\n status,\n error,\n );\n }\n }\n\n // Rate limit (429)\n if (status === 429) {\n return new LLMGatewayError(\n `LLM Gateway rate limit exceeded: Too many requests to the LLM provider. ` +\n `Please wait before retrying or check your rate limit configuration. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_RATE_LIMIT\",\n status,\n error,\n );\n }\n\n // Bad request (400)\n if (status === 400) {\n return new LLMGatewayError(\n `LLM Gateway request error: Invalid request parameters. ` +\n `Please check your request configuration (model, messages, tools). ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_BAD_REQUEST\",\n status,\n error,\n );\n }\n\n // Not found (404)\n if (status === 404) {\n return new LLMGatewayError(\n `LLM Gateway route not found: The specified model or route does not exist. ` +\n `Please verify the route alias '${this.modelName}' is correct and provisioned. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_NOT_FOUND\",\n status,\n error,\n );\n }\n\n // Timeout (408, 504)\n if (status === 408 || status === 504) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The request took too long to complete. ` +\n `This may be due to high load or a complex request. Please try again. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_TIMEOUT\",\n status,\n error,\n );\n }\n\n // Server errors (5xx)\n if (status && status >= 500) {\n return new LLMGatewayError(\n `LLM Gateway server error: The LLM provider returned an error (${status}). ` +\n `This is typically a temporary issue. Please try again later. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_SERVER_ERROR\",\n status,\n error,\n );\n }\n\n // Other API errors\n return new LLMGatewayError(\n `LLM Gateway error (${status}): ${originalMessage}`,\n \"LLM_GATEWAY_ERROR\",\n status,\n error,\n );\n }\n\n // Handle standard Error objects\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n // Connection errors\n if (msg.includes(\"econnrefused\") || msg.includes(\"enotfound\") || msg.includes(\"network\")) {\n return new LLMGatewayError(\n `LLM Gateway connection failed: Unable to connect to the LLM Gateway at ${this.baseUrl}. ` +\n `Please verify the gateway is running and accessible. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_CONNECTION_ERROR\",\n undefined,\n error,\n );\n }\n\n // Timeout errors\n if (msg.includes(\"timeout\") || msg.includes(\"timed out\") || msg.includes(\"etimedout\")) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The connection timed out. ` +\n `Please check network connectivity and try again. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_TIMEOUT\",\n undefined,\n error,\n );\n }\n\n // Generic Error - preserve original message with context\n return new LLMGatewayError(\n `LLM Gateway error: ${error.message}`,\n \"LLM_GATEWAY_ERROR\",\n undefined,\n error,\n );\n }\n\n // Unknown error type\n return new LLMGatewayError(\n `LLM Gateway error: An unexpected error occurred. Details: ${String(error)}`,\n \"LLM_GATEWAY_UNKNOWN_ERROR\",\n undefined,\n error,\n );\n }\n\n /**\n * Extracts the error source from an APIError.\n * The backend sets X-Error-Source header or includes source in the error body\n * to differentiate between gateway errors (LLM Gateway API key) and provider errors.\n *\n * @returns \"gateway\" if the error is from LLM Gateway authentication,\n * \"provider\" if the error is from the upstream LLM provider,\n * undefined if the source cannot be determined.\n */\n private extractErrorSource(error: APIError): \"gateway\" | \"provider\" | undefined {\n // Try to get source from response headers\n const headers = error.headers;\n if (headers) {\n const errorSource = headers[\"x-error-source\"] || headers[\"X-Error-Source\"];\n if (errorSource === \"gateway\" || errorSource === \"provider\") {\n return errorSource;\n }\n }\n\n // Try to get source from error body\n // The backend may include { error: { source: \"gateway\" | \"provider\", ... } }\n const errorBody = error.error as Record<string, unknown> | undefined;\n if (errorBody && typeof errorBody === \"object\") {\n // Check for nested error object (OpenAI style)\n const nestedError = errorBody.error as Record<string, unknown> | undefined;\n if (nestedError && typeof nestedError === \"object\" && nestedError.source) {\n const source = nestedError.source;\n if (source === \"gateway\" || source === \"provider\") {\n return source;\n }\n }\n // Check for direct source field\n if (errorBody.source === \"gateway\" || errorBody.source === \"provider\") {\n return errorBody.source;\n }\n }\n\n // Check error message for gateway-specific patterns\n const msg = error.message.toLowerCase();\n if (msg.includes(\"llm gateway api key\") || msg.includes(\"llm gateway\")) {\n return \"gateway\";\n }\n\n return undefined;\n }\n\n // ==========================================================================\n // Helpers (copied from OpenAI provider)\n // ==========================================================================\n\n private extractExtraOptions(options?: GenerateOptions): Record<string, any> {\n if (!options) return {};\n const { responseFormat, ...rest } = options;\n return rest;\n }\n\n private buildGatewayHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"x-request-id\": randomUUID(),\n };\n\n const exec = ExecutionContext.getOrUndefined();\n if (exec?.executionId) {\n headers[\"x-execution-id\"] = exec.executionId;\n }\n\n const ctx = Tracing.getContext();\n if (ctx) {\n // IDs já estão em formato W3C (hex)\n headers.traceparent = `00-${ctx.traceId}-${ctx.spanId}-01`;\n } else {\n // Still emit a root trace context so the gateway can correlate requests by traceId.\n const traceId = randomBytes(16).toString(\"hex\");\n const spanId = randomBytes(8).toString(\"hex\");\n headers.traceparent = `00-${traceId}-${spanId}-01`;\n }\n\n return headers;\n }\n\n /**\n * Extracts enrichment data from backend HTTP headers.\n * Backend returns this data so SDK can enrich its own span (avoiding duplicate spans).\n */\n private extractEnrichmentFromHeaders(headers: Headers): {\n modelActual?: string;\n fallbackUsed?: boolean;\n usage?: TokenUsage;\n cost?: { amountUsd: string; available: boolean };\n } {\n const result: ReturnType<typeof this.extractEnrichmentFromHeaders> = {};\n\n const modelActual = headers.get(\"x-llm-model-actual\");\n if (modelActual) {\n result.modelActual = modelActual;\n }\n\n const fallbackUsed = headers.get(\"x-llm-fallback-used\");\n if (fallbackUsed) {\n result.fallbackUsed = fallbackUsed === \"true\";\n }\n\n const usageRaw = headers.get(\"x-llm-usage\");\n if (usageRaw) {\n try {\n const usage = JSON.parse(usageRaw);\n result.usage = {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens,\n // Preserve any additional token fields from provider\n ...usage,\n };\n } catch {\n this.logger.warn(\"Failed to parse x-llm-usage header\", { usageRaw });\n }\n }\n\n const cost = headers.get(\"x-llm-cost\");\n const costAvailable = headers.get(\"x-llm-cost-available\");\n if (cost) {\n result.cost = {\n amountUsd: cost,\n available: costAvailable === \"true\",\n };\n }\n\n return result;\n }\n\n protected convertMessages(messages: Message[]): OpenAIClient.Chat.ChatCompletionMessageParam[] {\n // Ensure tools have a preceding assistant message with tool_calls\n const allowedToolCallIds = new Set<string>();\n\n return messages.flatMap((m) => {\n if (m.role === \"tool\") {\n // Skip orphan tool messages (no preceding tool_calls)\n if (!m.tool_call_id || !allowedToolCallIds.has(m.tool_call_id)) {\n return [];\n }\n\n return {\n role: \"tool\",\n tool_call_id: m.tool_call_id!,\n content: typeof m.content === \"string\" ? m.content : JSON.stringify(m.content), // Tool output usually string\n };\n }\n\n if (m.role === \"assistant\") {\n // OpenAI rules:\n // - content is required (string | null)\n // - if tool_calls is present, content can be null\n // - if tool_calls is NOT present, content must be string (cannot be null/empty if strict, but usually empty string is fine)\n\n let assistantContent: string | null = null;\n\n if (typeof m.content === \"string\") {\n assistantContent = m.content;\n }\n\n // If content is null/empty AND no tool_calls, force empty string to avoid API error\n if (!assistantContent && (!m.tool_calls || m.tool_calls.length === 0)) {\n assistantContent = \"\";\n }\n\n if (m.tool_calls) {\n m.tool_calls.forEach((tc) => allowedToolCallIds.add(tc.id));\n }\n\n return {\n role: \"assistant\",\n content: assistantContent,\n tool_calls: m.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n };\n }\n\n // User / System with potential multimodal content\n const content: OpenAIClient.Chat.ChatCompletionContentPart[] | string = Array.isArray(\n m.content,\n )\n ? m.content.map((part) => this.convertContentPart(part))\n : m.content || \"\";\n\n return {\n role: m.role as \"system\" | \"user\",\n content,\n name: m.name,\n } as any;\n });\n }\n\n private convertTools(\n tools?: ToolDefinitionForLLM[],\n ): OpenAIClient.Chat.ChatCompletionTool[] | undefined {\n if (!tools || tools.length === 0) return undefined;\n return tools.map((t) => ({\n type: \"function\",\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n private mapUsage(usage?: OpenAIClient.CompletionUsage): TokenUsage | undefined {\n if (!usage) return undefined;\n return {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: (usage as any).completion_tokens_details?.reasoning_tokens,\n };\n }\n\n private mapFinishReason(reason: string | null): ProviderResponse[\"finishReason\"] {\n switch (reason) {\n case \"stop\":\n return \"stop\";\n case \"length\":\n return \"length\";\n case \"tool_calls\":\n return \"tool_calls\";\n case \"content_filter\":\n return \"content_filter\";\n default:\n return undefined;\n }\n }\n\n private convertContentPart(part: ContentPart): OpenAIClient.Chat.ChatCompletionContentPart {\n if (part.type === \"text\") return { type: \"text\", text: part.text };\n\n if (part.type === \"file\") {\n const { mimeType, source } = part.file;\n if (!mimeType.startsWith(\"image/\")) {\n throw new Error(`LLM Gateway: file mimeType '${mimeType}' is not supported yet`);\n }\n\n const url = source.type === \"url\" ? source.url : `data:${mimeType};base64,${source.base64}`;\n\n return { type: \"image_url\", image_url: { url } };\n }\n\n if (part.type === \"image_url\") {\n return { type: \"image_url\", image_url: { url: part.image_url.url } };\n }\n\n // Exhaustive check - should never reach here with proper ContentPart\n const _exhaustive: never = part;\n throw new Error(`Unsupported content type: ${(_exhaustive as any).type}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import OpenAIClient, { APIError, ClientOptions } from \"openai\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport {\n IModel,\n Message,\n ProviderResponse,\n StreamChunk,\n ToolDefinitionForLLM,\n GenerateOptions,\n TokenUsage,\n ContentPart,\n ConsoleLogger,\n type LogLevel,\n ExecutionContext,\n Tracing,\n} from \"@nebulaos/core\";\nimport { SpanType, type LLMSpanStartData, type LLMSpanEndData } from \"@nebulaos/types\";\n\n/**\n * Custom error class for LLM Gateway errors.\n * Provides clear, actionable error messages for developers.\n */\nexport class LLMGatewayError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n public readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"LLMGatewayError\";\n }\n}\n\n/**\n * LLM Gateway Provider Configuration\n */\nexport interface LLMGatewayConfig {\n /** API Key from NebulaOS */\n apiKey: string;\n /** Base URL of the NebulaOS LLM Gateway */\n baseUrl?: string;\n /** Route alias (e.g., \"assistente\", \"code-review\") */\n model: string;\n /** Logger verbosity for gateway calls */\n logLevel?: LogLevel;\n /** Optional OpenAI client options */\n clientOptions?: ClientOptions;\n /**\n * Default model options passed to every generate() call.\n * Supports provider-specific params like reasoning_effort, temperature, topK, etc.\n * These can be overridden by options passed directly to generate().\n */\n options?: Omit<GenerateOptions, \"responseFormat\">;\n}\n\n/**\n * NebulaOS LLM Gateway Provider\n *\n * Provides access to NebulaOS LLM Gateway routes through an OpenAI-compatible interface.\n * Routes are pre-configured in NebulaOS and provide automatic fallback, cost tracking,\n * and access control.\n */\nexport class LLMGateway implements IModel {\n providerName = \"llm-gateway\";\n modelName: string;\n private client: OpenAIClient;\n private baseUrl: string;\n private logger: ConsoleLogger;\n private options?: Omit<GenerateOptions, \"responseFormat\">;\n\n capabilities = {\n inputFiles: {\n mimeTypes: [\"image/*\"],\n sources: [\"url\", \"base64\"] as const,\n },\n } as const;\n\n constructor(config: LLMGatewayConfig) {\n this.modelName = config.model;\n\n this.baseUrl = config.baseUrl || \"http://localhost:4100\";\n const baseURL = this.baseUrl.endsWith(\"/v1\") ? this.baseUrl : `${this.baseUrl}/v1`;\n this.logger = new ConsoleLogger(config.logLevel || \"info\", \"nebulaos/llm-gateway\");\n\n this.client = new OpenAIClient({\n apiKey: config.apiKey,\n baseURL,\n ...config.clientOptions,\n });\n this.options = config.options;\n }\n\n async generate(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): Promise<ProviderResponse> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n // Extract LLM config params (temperature, thinkingLevel, topP, maxTokens, etc)\n const { responseFormat, ...llmConfig } = mergedOptions ?? {};\n\n // Typed span start data for LLM calls\n const startData: LLMSpanStartData = {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : undefined,\n responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n };\n\n return Tracing.withSpan(\n {\n kind: SpanType.llm,\n name: `llm:${this.modelName}`,\n data: startData,\n },\n async (llmSpan) => {\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway request\", {\n model,\n baseUrl: this.baseUrl,\n stream: false,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n try {\n // Use .withResponse() to access HTTP headers for backend enrichment data\n const { data: response, response: httpResponse } = await this.client.chat.completions\n .create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n )\n .withResponse();\n\n this.logger.debug(\"LLM Gateway response\", {\n model,\n finishReason: response.choices?.[0]?.finish_reason,\n hasUsage: Boolean(response.usage),\n });\n\n const choice = response.choices[0];\n const message = choice.message;\n const usage = this.mapUsage(response.usage);\n const finishReason = this.mapFinishReason(choice.finish_reason);\n\n // Read enrichment headers from backend (model actual, cost, usage, fallback)\n const enrichment = this.extractEnrichmentFromHeaders(httpResponse.headers);\n\n // Typed span end data for LLM calls\n const endData: LLMSpanEndData = {\n usage: enrichment.usage ?? usage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n finishReason: finishReason ?? \"stop\",\n toolCallsCount: message.tool_calls?.length ?? 0,\n choices: response.choices,\n model: enrichment.modelActual,\n fallbackUsed: enrichment.fallbackUsed,\n cost: enrichment.cost ? parseFloat(enrichment.cost.amountUsd) : undefined,\n };\n\n await llmSpan.end({\n status: \"success\",\n data: endData,\n });\n\n return {\n content: message.content || \"\",\n toolCalls: message.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n finishReason,\n usage: enrichment.usage ?? usage,\n };\n } catch (error) {\n this.logger.error(\"LLM Gateway request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n // Typed span end data for error case\n const errorEndData: LLMSpanEndData = {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n };\n\n await llmSpan.end({\n status: \"error\",\n data: errorEndData,\n });\n\n throw gatewayError;\n }\n },\n );\n }\n\n async *generateStream(\n messages: Message[],\n tools?: ToolDefinitionForLLM[],\n options?: GenerateOptions,\n ): AsyncGenerator<StreamChunk> {\n const mergedOptions = { ...this.options, ...options };\n const model = `route:${this.modelName}`;\n\n // Prepare context data for tracing (useful for debugging, especially on errors)\n const messagesPreview = messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\"\n ? m.content.length > 500 ? m.content.slice(0, 500) + \"...\" : m.content\n : Array.isArray(m.content) ? `[${m.content.length} parts]` : String(m.content),\n }));\n const toolsPreview = tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description?.slice(0, 200),\n }));\n\n // Extract LLM config params (temperature, thinkingLevel, topP, maxTokens, etc)\n const { responseFormat, ...llmConfig } = mergedOptions ?? {};\n\n // Typed span start data for LLM calls\n const startData: LLMSpanStartData = {\n provider: this.providerName,\n model: this.modelName,\n messagesCount: messages.length,\n toolsCount: tools?.length ?? 0,\n llmConfig: Object.keys(llmConfig).length > 0 ? llmConfig : undefined,\n responseFormat,\n messages: messagesPreview,\n tools: toolsPreview,\n };\n\n // Start span manually for streaming (async generators can't use withSpan directly)\n const llmSpan = await Tracing.startSpan({\n kind: SpanType.llm,\n name: `llm:${this.modelName}`,\n data: startData,\n });\n\n const headers = this.buildGatewayHeaders();\n this.logger.debug(\"LLM Gateway stream request\", {\n model,\n baseUrl: this.baseUrl,\n stream: true,\n messageCount: messages.length,\n toolCount: tools?.length ?? 0,\n });\n\n let stream;\n try {\n stream = await this.client.chat.completions.create(\n {\n model,\n messages: this.convertMessages(messages),\n tools: this.convertTools(tools),\n stream: true,\n stream_options: { include_usage: true },\n response_format:\n mergedOptions?.responseFormat?.type === \"json\"\n ? mergedOptions.responseFormat.schema\n ? {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: mergedOptions.responseFormat.schema as any },\n }\n : { type: \"json_object\" }\n : undefined,\n ...this.extractExtraOptions(mergedOptions),\n },\n { headers },\n );\n } catch (error) {\n this.logger.error(\"LLM Gateway stream request failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n // Typed span end data for error case\n const errorEndData: LLMSpanEndData = {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n };\n\n await llmSpan.end({\n status: \"error\",\n data: errorEndData,\n });\n }\n\n throw gatewayError;\n }\n\n let finalUsage: TokenUsage | undefined;\n let finalFinishReason: ProviderResponse[\"finishReason\"];\n let toolCallsCount = 0;\n let outputPreview = \"\";\n let finalContent = \"\";\n const toolCallsAccumulator: Map<number, { id: string; name: string; arguments: string }> = new Map();\n\n try {\n for await (const chunk of stream) {\n if (chunk.usage) {\n finalUsage = this.mapUsage(chunk.usage);\n yield {\n type: \"finish\",\n reason: \"stop\",\n usage: finalUsage,\n };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice) continue;\n\n if (choice.finish_reason) {\n finalFinishReason = this.mapFinishReason(choice.finish_reason);\n yield {\n type: \"finish\",\n reason: finalFinishReason,\n };\n }\n\n const delta = choice.delta;\n if (!delta) continue;\n\n if (delta.content) {\n finalContent += delta.content;\n if (outputPreview.length < 200) {\n outputPreview += delta.content.slice(0, 200 - outputPreview.length);\n }\n yield { type: \"content_delta\", delta: delta.content };\n }\n\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index;\n if (tc.id && tc.function?.name) {\n toolCallsCount++;\n toolCallsAccumulator.set(idx, { id: tc.id, name: tc.function.name, arguments: \"\" });\n yield {\n type: \"tool_call_start\",\n index: idx,\n id: tc.id,\n name: tc.function.name,\n };\n }\n\n if (tc.function?.arguments) {\n const existing = toolCallsAccumulator.get(idx);\n if (existing) {\n existing.arguments += tc.function.arguments;\n }\n yield {\n type: \"tool_call_delta\",\n index: idx,\n args: tc.function.arguments,\n };\n }\n }\n }\n }\n\n // Build choices for observability\n const toolCalls = Array.from(toolCallsAccumulator.values()).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n }));\n\n const choices = [{\n index: 0,\n message: {\n role: \"assistant\" as const,\n content: finalContent || null,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n },\n finish_reason: finalFinishReason,\n }];\n\n // Typed span end data for success case\n if (llmSpan) {\n const endData: LLMSpanEndData = {\n usage: finalUsage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n finishReason: finalFinishReason ?? \"stop\",\n toolCallsCount,\n outputPreview,\n choices,\n };\n\n await llmSpan.end({\n status: \"success\",\n data: endData,\n });\n }\n } catch (error) {\n this.logger.error(\"LLM Gateway stream failed\", error, undefined, undefined);\n const gatewayError = this.handleError(error);\n\n if (llmSpan) {\n // Typed span end data for error case\n const errorEndData: LLMSpanEndData = {\n error: {\n message: gatewayError.message,\n code: gatewayError.code,\n status: gatewayError.status,\n },\n };\n\n await llmSpan.end({\n status: \"error\",\n data: errorEndData,\n });\n }\n\n throw gatewayError;\n }\n }\n\n // ==========================================================================\n // Error Handling\n // ==========================================================================\n\n /**\n * Transforms raw errors into actionable LLMGatewayError with clear messages.\n * This ensures developers get specific guidance on how to resolve issues.\n *\n * Differentiates between:\n * - Gateway errors: LLM Gateway API key issues (check NEBULAOS_API_KEY env var)\n * - Provider errors: LLM provider API key issues (check route config in dashboard)\n */\n private handleError(error: unknown): LLMGatewayError {\n // Handle OpenAI SDK APIError (includes status, message, code)\n if (error instanceof APIError) {\n const status = error.status;\n const originalMessage = error.message;\n\n // Check X-Error-Source header to differentiate gateway vs provider errors\n const errorSource = this.extractErrorSource(error);\n\n // Authentication errors (401)\n if (status === 401) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway authentication failed: Your LLM Gateway API key is invalid or expired. ` +\n `Please verify your NEBULAOS_API_KEY environment variable or check your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_AUTH_ERROR\",\n status,\n error,\n );\n } else {\n // Provider error (default for 401 without explicit gateway source)\n return new LLMGatewayError(\n `LLM Provider authentication failed: The API key configured for your LLM provider (OpenAI, Azure, etc.) is invalid or expired. ` +\n `Please verify the provider API key in your route configuration in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_AUTH_ERROR\",\n status,\n error,\n );\n }\n }\n\n // Permission denied (403)\n if (status === 403) {\n if (errorSource === \"gateway\") {\n return new LLMGatewayError(\n `LLM Gateway access denied: Your LLM Gateway API key does not have permission to access this route. ` +\n `Please verify the route is allowed for your LLM Gateway API key in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"GATEWAY_FORBIDDEN\",\n status,\n error,\n );\n } else {\n return new LLMGatewayError(\n `LLM Provider access denied: The provider API key does not have permission for this operation. ` +\n `Please verify the provider API key permissions in the NebulaOS dashboard. ` +\n `Original error: ${originalMessage}`,\n \"PROVIDER_FORBIDDEN\",\n status,\n error,\n );\n }\n }\n\n // Rate limit (429)\n if (status === 429) {\n return new LLMGatewayError(\n `LLM Gateway rate limit exceeded: Too many requests to the LLM provider. ` +\n `Please wait before retrying or check your rate limit configuration. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_RATE_LIMIT\",\n status,\n error,\n );\n }\n\n // Bad request (400)\n if (status === 400) {\n return new LLMGatewayError(\n `LLM Gateway request error: Invalid request parameters. ` +\n `Please check your request configuration (model, messages, tools). ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_BAD_REQUEST\",\n status,\n error,\n );\n }\n\n // Not found (404)\n if (status === 404) {\n return new LLMGatewayError(\n `LLM Gateway route not found: The specified model or route does not exist. ` +\n `Please verify the route alias '${this.modelName}' is correct and provisioned. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_NOT_FOUND\",\n status,\n error,\n );\n }\n\n // Timeout (408, 504)\n if (status === 408 || status === 504) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The request took too long to complete. ` +\n `This may be due to high load or a complex request. Please try again. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_TIMEOUT\",\n status,\n error,\n );\n }\n\n // Server errors (5xx)\n if (status && status >= 500) {\n return new LLMGatewayError(\n `LLM Gateway server error: The LLM provider returned an error (${status}). ` +\n `This is typically a temporary issue. Please try again later. ` +\n `Original error: ${originalMessage}`,\n \"LLM_GATEWAY_SERVER_ERROR\",\n status,\n error,\n );\n }\n\n // Other API errors\n return new LLMGatewayError(\n `LLM Gateway error (${status}): ${originalMessage}`,\n \"LLM_GATEWAY_ERROR\",\n status,\n error,\n );\n }\n\n // Handle standard Error objects\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n // Connection errors\n if (msg.includes(\"econnrefused\") || msg.includes(\"enotfound\") || msg.includes(\"network\")) {\n return new LLMGatewayError(\n `LLM Gateway connection failed: Unable to connect to the LLM Gateway at ${this.baseUrl}. ` +\n `Please verify the gateway is running and accessible. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_CONNECTION_ERROR\",\n undefined,\n error,\n );\n }\n\n // Timeout errors\n if (msg.includes(\"timeout\") || msg.includes(\"timed out\") || msg.includes(\"etimedout\")) {\n return new LLMGatewayError(\n `LLM Gateway timeout: The connection timed out. ` +\n `Please check network connectivity and try again. ` +\n `Original error: ${error.message}`,\n \"LLM_GATEWAY_TIMEOUT\",\n undefined,\n error,\n );\n }\n\n // Generic Error - preserve original message with context\n return new LLMGatewayError(\n `LLM Gateway error: ${error.message}`,\n \"LLM_GATEWAY_ERROR\",\n undefined,\n error,\n );\n }\n\n // Unknown error type\n return new LLMGatewayError(\n `LLM Gateway error: An unexpected error occurred. Details: ${String(error)}`,\n \"LLM_GATEWAY_UNKNOWN_ERROR\",\n undefined,\n error,\n );\n }\n\n /**\n * Extracts the error source from an APIError.\n * The backend sets X-Error-Source header or includes source in the error body\n * to differentiate between gateway errors (LLM Gateway API key) and provider errors.\n *\n * @returns \"gateway\" if the error is from LLM Gateway authentication,\n * \"provider\" if the error is from the upstream LLM provider,\n * undefined if the source cannot be determined.\n */\n private extractErrorSource(error: APIError): \"gateway\" | \"provider\" | undefined {\n // Try to get source from response headers\n const headers = error.headers;\n if (headers) {\n const errorSource = headers[\"x-error-source\"] || headers[\"X-Error-Source\"];\n if (errorSource === \"gateway\" || errorSource === \"provider\") {\n return errorSource;\n }\n }\n\n // Try to get source from error body\n // The backend may include { error: { source: \"gateway\" | \"provider\", ... } }\n const errorBody = error.error as Record<string, unknown> | undefined;\n if (errorBody && typeof errorBody === \"object\") {\n // Check for nested error object (OpenAI style)\n const nestedError = errorBody.error as Record<string, unknown> | undefined;\n if (nestedError && typeof nestedError === \"object\" && nestedError.source) {\n const source = nestedError.source;\n if (source === \"gateway\" || source === \"provider\") {\n return source;\n }\n }\n // Check for direct source field\n if (errorBody.source === \"gateway\" || errorBody.source === \"provider\") {\n return errorBody.source;\n }\n }\n\n // Check error message for gateway-specific patterns\n const msg = error.message.toLowerCase();\n if (msg.includes(\"llm gateway api key\") || msg.includes(\"llm gateway\")) {\n return \"gateway\";\n }\n\n return undefined;\n }\n\n // ==========================================================================\n // Helpers (copied from OpenAI provider)\n // ==========================================================================\n\n private extractExtraOptions(options?: GenerateOptions): Record<string, any> {\n if (!options) return {};\n const { responseFormat, ...rest } = options;\n return rest;\n }\n\n private buildGatewayHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"x-request-id\": randomUUID(),\n };\n\n const exec = ExecutionContext.getOrUndefined();\n if (exec?.executionId) {\n headers[\"x-execution-id\"] = exec.executionId;\n }\n\n const ctx = Tracing.getContext();\n if (ctx) {\n // IDs já estão em formato W3C (hex)\n headers.traceparent = `00-${ctx.traceId}-${ctx.spanId}-01`;\n } else {\n // Still emit a root trace context so the gateway can correlate requests by traceId.\n const traceId = randomBytes(16).toString(\"hex\");\n const spanId = randomBytes(8).toString(\"hex\");\n headers.traceparent = `00-${traceId}-${spanId}-01`;\n }\n\n return headers;\n }\n\n /**\n * Extracts enrichment data from backend HTTP headers.\n * Backend returns this data so SDK can enrich its own span (avoiding duplicate spans).\n */\n private extractEnrichmentFromHeaders(headers: Headers): {\n modelActual?: string;\n fallbackUsed?: boolean;\n usage?: TokenUsage;\n cost?: { amountUsd: string; available: boolean };\n } {\n const result: ReturnType<typeof this.extractEnrichmentFromHeaders> = {};\n\n const modelActual = headers.get(\"x-llm-model-actual\");\n if (modelActual) {\n result.modelActual = modelActual;\n }\n\n const fallbackUsed = headers.get(\"x-llm-fallback-used\");\n if (fallbackUsed) {\n result.fallbackUsed = fallbackUsed === \"true\";\n }\n\n const usageRaw = headers.get(\"x-llm-usage\");\n if (usageRaw) {\n try {\n const usage = JSON.parse(usageRaw);\n result.usage = {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: usage.completion_tokens_details?.reasoning_tokens,\n // Preserve any additional token fields from provider\n ...usage,\n };\n } catch {\n this.logger.warn(\"Failed to parse x-llm-usage header\", { usageRaw });\n }\n }\n\n const cost = headers.get(\"x-llm-cost\");\n const costAvailable = headers.get(\"x-llm-cost-available\");\n if (cost) {\n result.cost = {\n amountUsd: cost,\n available: costAvailable === \"true\",\n };\n }\n\n return result;\n }\n\n protected convertMessages(messages: Message[]): OpenAIClient.Chat.ChatCompletionMessageParam[] {\n // Ensure tools have a preceding assistant message with tool_calls\n const allowedToolCallIds = new Set<string>();\n\n return messages.flatMap((m) => {\n if (m.role === \"tool\") {\n // Skip orphan tool messages (no preceding tool_calls)\n if (!m.tool_call_id || !allowedToolCallIds.has(m.tool_call_id)) {\n return [];\n }\n\n return {\n role: \"tool\",\n tool_call_id: m.tool_call_id!,\n content: typeof m.content === \"string\" ? m.content : JSON.stringify(m.content), // Tool output usually string\n };\n }\n\n if (m.role === \"assistant\") {\n // OpenAI rules:\n // - content is required (string | null)\n // - if tool_calls is present, content can be null\n // - if tool_calls is NOT present, content must be string (cannot be null/empty if strict, but usually empty string is fine)\n\n let assistantContent: string | null = null;\n\n if (typeof m.content === \"string\") {\n assistantContent = m.content;\n }\n\n // If content is null/empty AND no tool_calls, force empty string to avoid API error\n if (!assistantContent && (!m.tool_calls || m.tool_calls.length === 0)) {\n assistantContent = \"\";\n }\n\n if (m.tool_calls) {\n m.tool_calls.forEach((tc) => allowedToolCallIds.add(tc.id));\n }\n\n return {\n role: \"assistant\",\n content: assistantContent,\n tool_calls: m.tool_calls?.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n };\n }\n\n // User / System with potential multimodal content\n const content: OpenAIClient.Chat.ChatCompletionContentPart[] | string = Array.isArray(\n m.content,\n )\n ? m.content.map((part) => this.convertContentPart(part))\n : m.content || \"\";\n\n return {\n role: m.role as \"system\" | \"user\",\n content,\n name: m.name,\n } as any;\n });\n }\n\n private convertTools(\n tools?: ToolDefinitionForLLM[],\n ): OpenAIClient.Chat.ChatCompletionTool[] | undefined {\n if (!tools || tools.length === 0) return undefined;\n return tools.map((t) => ({\n type: \"function\",\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n private mapUsage(usage?: OpenAIClient.CompletionUsage): TokenUsage | undefined {\n if (!usage) return undefined;\n return {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n reasoningTokens: (usage as any).completion_tokens_details?.reasoning_tokens,\n };\n }\n\n private mapFinishReason(reason: string | null): ProviderResponse[\"finishReason\"] {\n switch (reason) {\n case \"stop\":\n return \"stop\";\n case \"length\":\n return \"length\";\n case \"tool_calls\":\n return \"tool_calls\";\n case \"content_filter\":\n return \"content_filter\";\n default:\n return undefined;\n }\n }\n\n private convertContentPart(part: ContentPart): OpenAIClient.Chat.ChatCompletionContentPart {\n if (part.type === \"text\") return { type: \"text\", text: part.text };\n\n if (part.type === \"file\") {\n const { mimeType, source } = part.file;\n if (!mimeType.startsWith(\"image/\")) {\n throw new Error(`LLM Gateway: file mimeType '${mimeType}' is not supported yet`);\n }\n\n const url = source.type === \"url\" ? source.url : `data:${mimeType};base64,${source.base64}`;\n\n return { type: \"image_url\", image_url: { url } };\n }\n\n if (part.type === \"image_url\") {\n return { type: \"image_url\", image_url: { url: part.image_url.url } };\n }\n\n // Exhaustive check - should never reach here with proper ContentPart\n const _exhaustive: never = part;\n throw new Error(`Unsupported content type: ${(_exhaustive as any).type}`);\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebulaos/llm-gateway",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "NebulaOS LLM Gateway provider - OpenAI-compatible chat completions",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"openai": "^4.52.7",
|
|
30
|
-
"@nebulaos/core": "^0.2.
|
|
30
|
+
"@nebulaos/core": "^0.2.5",
|
|
31
|
+
"@nebulaos/types": "^0.1.4"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@types/node": "^20.0.0",
|