opentool 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +84 -22
  2. package/dist/ai/index.d.ts +237 -0
  3. package/dist/ai/index.js +759 -0
  4. package/dist/ai/index.js.map +1 -0
  5. package/dist/cli/index.d.ts +38 -5
  6. package/dist/cli/index.js +2218 -67
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/index-D3DaM5Rs.d.ts +1693 -0
  9. package/dist/index.d.ts +33 -5
  10. package/dist/index.js +3258 -25
  11. package/dist/index.js.map +1 -1
  12. package/dist/payment/index.d.ts +2 -0
  13. package/dist/payment/index.js +969 -0
  14. package/dist/payment/index.js.map +1 -0
  15. package/dist/{types/metadata.d.ts → validate-DiIOFUU5.d.ts} +262 -415
  16. package/dist/wallets/index.d.ts +117 -0
  17. package/dist/wallets/index.js +337 -0
  18. package/dist/wallets/index.js.map +1 -0
  19. package/package.json +35 -4
  20. package/dist/cli/build.d.ts +0 -23
  21. package/dist/cli/build.d.ts.map +0 -1
  22. package/dist/cli/build.js +0 -223
  23. package/dist/cli/build.js.map +0 -1
  24. package/dist/cli/dev.d.ts +0 -6
  25. package/dist/cli/dev.d.ts.map +0 -1
  26. package/dist/cli/dev.js +0 -123
  27. package/dist/cli/dev.js.map +0 -1
  28. package/dist/cli/generate-metadata.d.ts +0 -15
  29. package/dist/cli/generate-metadata.d.ts.map +0 -1
  30. package/dist/cli/generate-metadata.js +0 -90
  31. package/dist/cli/generate-metadata.js.map +0 -1
  32. package/dist/cli/index.d.ts.map +0 -1
  33. package/dist/cli/shared/metadata.d.ts +0 -19
  34. package/dist/cli/shared/metadata.d.ts.map +0 -1
  35. package/dist/cli/shared/metadata.js +0 -283
  36. package/dist/cli/shared/metadata.js.map +0 -1
  37. package/dist/cli/validate.d.ts +0 -12
  38. package/dist/cli/validate.d.ts.map +0 -1
  39. package/dist/cli/validate.js +0 -237
  40. package/dist/cli/validate.js.map +0 -1
  41. package/dist/index.d.ts.map +0 -1
  42. package/dist/runtime/index.d.ts +0 -12
  43. package/dist/runtime/index.d.ts.map +0 -1
  44. package/dist/runtime/index.js +0 -241
  45. package/dist/runtime/index.js.map +0 -1
  46. package/dist/types/index.d.ts +0 -33
  47. package/dist/types/index.d.ts.map +0 -1
  48. package/dist/types/index.js +0 -3
  49. package/dist/types/index.js.map +0 -1
  50. package/dist/types/metadata.d.ts.map +0 -1
  51. package/dist/types/metadata.js +0 -108
  52. package/dist/types/metadata.js.map +0 -1
  53. package/dist/utils/esbuild.d.ts +0 -13
  54. package/dist/utils/esbuild.d.ts.map +0 -1
  55. package/dist/utils/esbuild.js +0 -95
  56. package/dist/utils/esbuild.js.map +0 -1
  57. package/dist/utils/module-loader.d.ts +0 -3
  58. package/dist/utils/module-loader.d.ts.map +0 -1
  59. package/dist/utils/module-loader.js +0 -49
  60. package/dist/utils/module-loader.js.map +0 -1
@@ -0,0 +1,759 @@
1
+ // src/ai/errors.ts
2
+ var AIError = class extends Error {
3
+ constructor(message, options) {
4
+ super(message);
5
+ this.name = "AIError";
6
+ if (options && "cause" in options) {
7
+ this.cause = options.cause;
8
+ }
9
+ }
10
+ };
11
+ var AIFetchError = class extends AIError {
12
+ constructor(message, options) {
13
+ super(message, options);
14
+ this.name = "AIFetchError";
15
+ }
16
+ };
17
+ var AIResponseError = class extends AIError {
18
+ constructor(details, message) {
19
+ super(message ?? `AI response error: ${details.status} ${details.statusText}`);
20
+ this.name = "AIResponseError";
21
+ this.status = details.status;
22
+ this.statusText = details.statusText;
23
+ this.body = details.body;
24
+ this.headers = details.headers ?? {};
25
+ }
26
+ };
27
+ var AIAbortError = class extends AIError {
28
+ constructor(message = "AI request aborted") {
29
+ super(message);
30
+ this.name = "AIAbortError";
31
+ }
32
+ };
33
+
34
+ // src/ai/config.ts
35
+ var DEFAULT_BASE_URL = "https://gateway.openpond.dev";
36
+ var DEFAULT_TIMEOUT_MS = 6e4;
37
+ var DEFAULT_MODEL = "openai/gpt-5-mini";
38
+ function assertFetchAvailable(fetchImplementation) {
39
+ if (!fetchImplementation) {
40
+ throw new Error(
41
+ "No fetch implementation available. Provide one via AIClientConfig.fetchImplementation."
42
+ );
43
+ }
44
+ }
45
+ function resolveConfig(config = {}) {
46
+ const fetchImplementation = config.fetchImplementation ?? globalThis.fetch;
47
+ assertFetchAvailable(fetchImplementation);
48
+ const resolved = {
49
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
50
+ defaultModel: config.defaultModel ?? DEFAULT_MODEL,
51
+ defaultHeaders: {
52
+ "Content-Type": "application/json",
53
+ ...config.defaultHeaders
54
+ },
55
+ fetchImplementation,
56
+ timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS
57
+ };
58
+ if (config.apiKey !== void 0) {
59
+ resolved.apiKey = config.apiKey;
60
+ }
61
+ return resolved;
62
+ }
63
+ function mergeHeaders(base, overrides) {
64
+ if (!overrides) {
65
+ return { ...base };
66
+ }
67
+ const merged = { ...base };
68
+ for (const [key, value] of Object.entries(overrides)) {
69
+ if (value === void 0) {
70
+ continue;
71
+ }
72
+ merged[key] = value;
73
+ }
74
+ return merged;
75
+ }
76
+
77
+ // src/ai/models.ts
78
+ var MODEL_REGISTRY = [
79
+ {
80
+ name: "openai/gpt-5-mini",
81
+ label: "OpenAI GPT-5 Mini",
82
+ provider: "openai",
83
+ supportsStreaming: true,
84
+ supportsTools: true,
85
+ reasoning: true,
86
+ aliases: ["gpt-5-mini", "gpt5-mini", "gpt-5.0-mini"],
87
+ default: true
88
+ },
89
+ {
90
+ name: "anthropic/claude-4-sonnet-20250514",
91
+ label: "Claude 4 Sonnet (20250514)",
92
+ provider: "anthropic",
93
+ supportsStreaming: true,
94
+ supportsTools: true,
95
+ aliases: ["claude-4-sonnet", "claude-sonnet"]
96
+ },
97
+ {
98
+ name: "google/gemini-2.0-flash-001",
99
+ label: "Gemini 2.0 Flash",
100
+ provider: "google",
101
+ supportsStreaming: true,
102
+ supportsTools: true,
103
+ aliases: ["gemini-2.0-flash", "gemini-flash"]
104
+ },
105
+ {
106
+ name: "deepseek/deepseek-chat",
107
+ label: "DeepSeek Chat",
108
+ provider: "deepseek",
109
+ supportsStreaming: true,
110
+ supportsTools: true,
111
+ aliases: ["deepseek-chat", "deepseek"]
112
+ }
113
+ ];
114
+ var ALIAS_LOOKUP = MODEL_REGISTRY.reduce(
115
+ (accumulator, model) => {
116
+ accumulator[model.name.toLowerCase()] = model.name;
117
+ if (model.aliases) {
118
+ for (const alias of model.aliases) {
119
+ accumulator[alias.toLowerCase()] = model.name;
120
+ }
121
+ }
122
+ return accumulator;
123
+ },
124
+ {}
125
+ );
126
+ var DEFAULT_MODEL_NAME = MODEL_REGISTRY.find((model) => model.default)?.name ?? MODEL_REGISTRY[0].name;
127
+ function listModels() {
128
+ return [...MODEL_REGISTRY];
129
+ }
130
+ function getModelConfig(modelName) {
131
+ if (!modelName) {
132
+ return MODEL_REGISTRY.find((model) => model.default) ?? MODEL_REGISTRY[0];
133
+ }
134
+ const normalized = normalizeModelName(modelName);
135
+ return MODEL_REGISTRY.find((model) => model.name === normalized);
136
+ }
137
+ function normalizeModelName(modelName) {
138
+ if (!modelName) {
139
+ return DEFAULT_MODEL_NAME;
140
+ }
141
+ const trimmed = modelName.trim();
142
+ if (!trimmed) {
143
+ return DEFAULT_MODEL_NAME;
144
+ }
145
+ const directMatch = ALIAS_LOOKUP[trimmed.toLowerCase()];
146
+ if (directMatch) {
147
+ return directMatch;
148
+ }
149
+ if (trimmed.includes("/")) {
150
+ return trimmed;
151
+ }
152
+ return `openai/${trimmed}`;
153
+ }
154
+ function isStreamingSupported(modelName) {
155
+ const config = getModelConfig(modelName);
156
+ return config ? config.supportsStreaming : true;
157
+ }
158
+ function isToolCallingSupported(modelName) {
159
+ const config = getModelConfig(modelName);
160
+ return config ? config.supportsTools : true;
161
+ }
162
+
163
+ // src/ai/tools.ts
164
+ var WEBSEARCH_TOOL_NAME = "websearch";
165
+ var WEBSEARCH_TOOL_DEFINITION = {
166
+ type: "function",
167
+ function: {
168
+ name: WEBSEARCH_TOOL_NAME,
169
+ description: "Search the web using the OpenPond search engine. Returns relevant results with titles, URLs, and text content.",
170
+ parameters: {
171
+ type: "object",
172
+ properties: {
173
+ query: {
174
+ type: "string",
175
+ description: "The search query"
176
+ },
177
+ limit: {
178
+ type: "number",
179
+ description: "Maximum number of results to return (default: 5)"
180
+ }
181
+ },
182
+ required: ["query"]
183
+ }
184
+ }
185
+ };
186
+ function resolveToolset(tools, policy) {
187
+ if (!policy) {
188
+ return tools;
189
+ }
190
+ const resolved = tools ? [...tools] : [];
191
+ if (policy.webSearch) {
192
+ const alreadyIncluded = resolved.some(
193
+ (tool) => tool.type === "function" && tool.function?.name === WEBSEARCH_TOOL_NAME
194
+ );
195
+ if (!alreadyIncluded) {
196
+ resolved.push(materializeWebSearchTool(policy.webSearch));
197
+ }
198
+ }
199
+ return resolved.length > 0 ? resolved : void 0;
200
+ }
201
+ function materializeWebSearchTool(options) {
202
+ if (!options || Object.keys(options).length === 0) {
203
+ return WEBSEARCH_TOOL_DEFINITION;
204
+ }
205
+ const baseParameters = WEBSEARCH_TOOL_DEFINITION.function.parameters ?? {};
206
+ const baseProperties = baseParameters.properties ?? {};
207
+ const properties = { ...baseProperties };
208
+ if (options.limit !== void 0) {
209
+ const existingLimit = baseProperties["limit"];
210
+ const limitSchema = typeof existingLimit === "object" && existingLimit !== null ? { ...existingLimit } : {
211
+ type: "number",
212
+ description: "Maximum number of results to return (default: 5)"
213
+ };
214
+ limitSchema.default = options.limit;
215
+ properties.limit = limitSchema;
216
+ }
217
+ if (options.includeImages) {
218
+ properties.includeImages = {
219
+ type: "boolean",
220
+ description: "Whether to include representative images in results.",
221
+ default: true
222
+ };
223
+ }
224
+ return {
225
+ ...WEBSEARCH_TOOL_DEFINITION,
226
+ function: {
227
+ ...WEBSEARCH_TOOL_DEFINITION.function,
228
+ parameters: {
229
+ ...WEBSEARCH_TOOL_DEFINITION.function.parameters,
230
+ properties
231
+ }
232
+ }
233
+ };
234
+ }
235
+
236
+ // src/ai/messages.ts
237
+ function flattenMessageContent(content, options = {}) {
238
+ if (typeof content === "string") {
239
+ return content;
240
+ }
241
+ if (!Array.isArray(content)) {
242
+ return void 0;
243
+ }
244
+ const separator = options.separator ?? "";
245
+ const collected = [];
246
+ for (const part of content) {
247
+ const text = extractTextPart(part, options);
248
+ if (text) {
249
+ collected.push(text);
250
+ }
251
+ }
252
+ if (collected.length === 0) {
253
+ return void 0;
254
+ }
255
+ return collected.join(separator);
256
+ }
257
+ function ensureTextContent(message, options) {
258
+ const flattened = flattenMessageContent(message.content, options);
259
+ if (flattened !== void 0) {
260
+ return flattened;
261
+ }
262
+ throw new AIError(
263
+ options?.errorMessage ?? "Assistant response did not contain textual content."
264
+ );
265
+ }
266
+ function extractTextPart(part, options) {
267
+ if (!part || typeof part !== "object") {
268
+ return void 0;
269
+ }
270
+ if ("text" in part && typeof part.text === "string") {
271
+ return part.text;
272
+ }
273
+ if (options.includeUnknown) {
274
+ try {
275
+ return JSON.stringify(part);
276
+ } catch (error) {
277
+ return `[unserializable_part: ${String(error)}]`;
278
+ }
279
+ }
280
+ return void 0;
281
+ }
282
+
283
+ // src/ai/client.ts
284
+ var CHAT_COMPLETIONS_PATH = "/v1/chat/completions";
285
+ function createAIClient(config = {}) {
286
+ const resolved = resolveConfig(config);
287
+ return {
288
+ get config() {
289
+ return resolved;
290
+ },
291
+ async generateText(options) {
292
+ return generateText(options, config);
293
+ },
294
+ async streamText(options) {
295
+ return streamText(options, config);
296
+ },
297
+ listModels
298
+ };
299
+ }
300
+ async function generateText(options, clientConfig = {}) {
301
+ const resolved = resolveConfig(clientConfig);
302
+ const model = normalizeModelName(options.model ?? resolved.defaultModel);
303
+ const payload = buildRequestPayload(options, model, {
304
+ allowTools: isToolCallingSupported(model)
305
+ });
306
+ const headers = mergeHeaders(resolved.defaultHeaders, options.headers);
307
+ if (resolved.apiKey) {
308
+ headers.Authorization = `Bearer ${resolved.apiKey}`;
309
+ }
310
+ const endpoint = buildUrl(resolved.baseUrl, CHAT_COMPLETIONS_PATH);
311
+ const abortBundle = createAbortBundle(
312
+ options.abortSignal,
313
+ options.timeoutMs ?? resolved.timeoutMs
314
+ );
315
+ let response;
316
+ try {
317
+ response = await resolved.fetchImplementation(endpoint, {
318
+ method: "POST",
319
+ headers,
320
+ body: JSON.stringify(payload),
321
+ signal: abortBundle.signal
322
+ });
323
+ } catch (error) {
324
+ if (abortBundle.signal.aborted) {
325
+ throw toAbortError(abortBundle.signal.reason ?? error);
326
+ }
327
+ throw new AIFetchError("Failed to reach AI gateway", { cause: error });
328
+ } finally {
329
+ abortBundle.cleanup();
330
+ }
331
+ if (!response.ok) {
332
+ const errorBody = await safeParseJson(response);
333
+ throw new AIResponseError({
334
+ status: response.status,
335
+ statusText: response.statusText,
336
+ body: errorBody,
337
+ headers: collectHeaders(response.headers)
338
+ });
339
+ }
340
+ const data = await response.json();
341
+ const primaryChoice = data.choices.find(isPrimaryChoice);
342
+ if (!primaryChoice) {
343
+ throw new AIResponseError(
344
+ {
345
+ status: response.status,
346
+ statusText: response.statusText,
347
+ body: data
348
+ },
349
+ "Gateway response did not contain a valid choice"
350
+ );
351
+ }
352
+ const result = {
353
+ id: data.id,
354
+ model: data.model,
355
+ message: primaryChoice.message,
356
+ raw: data
357
+ };
358
+ if (primaryChoice.finish_reason !== void 0) {
359
+ result.finishReason = primaryChoice.finish_reason;
360
+ }
361
+ if (data.usage) {
362
+ result.usage = data.usage;
363
+ }
364
+ return result;
365
+ }
366
+ async function streamText(options, clientConfig = {}) {
367
+ const resolved = resolveConfig(clientConfig);
368
+ const model = normalizeModelName(options.model ?? resolved.defaultModel);
369
+ const streamExtras = buildStreamMetadataExtras(options);
370
+ const payload = buildRequestPayload(
371
+ options,
372
+ model,
373
+ {
374
+ allowTools: isToolCallingSupported(model)
375
+ },
376
+ streamExtras
377
+ );
378
+ payload.stream = true;
379
+ if (options.includeUsage) {
380
+ payload.stream_options = { include_usage: true };
381
+ }
382
+ const headers = mergeHeaders(resolved.defaultHeaders, options.headers);
383
+ if (resolved.apiKey) {
384
+ headers.Authorization = `Bearer ${resolved.apiKey}`;
385
+ }
386
+ const endpoint = buildUrl(resolved.baseUrl, CHAT_COMPLETIONS_PATH);
387
+ const abortBundle = createAbortBundle(
388
+ options.abortSignal,
389
+ options.timeoutMs ?? resolved.timeoutMs
390
+ );
391
+ let response;
392
+ try {
393
+ response = await resolved.fetchImplementation(endpoint, {
394
+ method: "POST",
395
+ headers,
396
+ body: JSON.stringify(payload),
397
+ signal: abortBundle.signal
398
+ });
399
+ } catch (error) {
400
+ if (abortBundle.signal.aborted) {
401
+ throw toAbortError(abortBundle.signal.reason ?? error);
402
+ }
403
+ throw new AIFetchError("Failed to reach AI gateway", { cause: error });
404
+ }
405
+ if (!response.ok) {
406
+ const errorBody = await safeParseJson(response);
407
+ abortBundle.cleanup();
408
+ throw new AIResponseError({
409
+ status: response.status,
410
+ statusText: response.statusText,
411
+ body: errorBody,
412
+ headers: collectHeaders(response.headers)
413
+ });
414
+ }
415
+ if (!response.body) {
416
+ abortBundle.cleanup();
417
+ throw new AIFetchError("Streaming response did not include a readable body");
418
+ }
419
+ const reader = response.body.getReader();
420
+ const decoder = new TextDecoder();
421
+ const handlers = options.handlers ?? {};
422
+ let finishedResolve;
423
+ let finishedReject;
424
+ const finished = new Promise((resolve, reject) => {
425
+ finishedResolve = resolve;
426
+ finishedReject = reject;
427
+ });
428
+ let settled = false;
429
+ const resolveStream = () => {
430
+ if (settled) {
431
+ return;
432
+ }
433
+ settled = true;
434
+ try {
435
+ handlers.onDone?.();
436
+ finishedResolve();
437
+ } catch (error) {
438
+ settled = false;
439
+ rejectStream(error);
440
+ }
441
+ };
442
+ const rejectStream = (reason) => {
443
+ if (settled) {
444
+ return;
445
+ }
446
+ settled = true;
447
+ try {
448
+ handlers.onError?.(reason);
449
+ } catch (handlerError) {
450
+ reason = handlerError;
451
+ }
452
+ finishedReject(reason);
453
+ };
454
+ const abort = () => abortBundle.abort();
455
+ (async () => {
456
+ let buffer = "";
457
+ try {
458
+ while (true) {
459
+ const { done, value } = await reader.read();
460
+ if (done) {
461
+ buffer += decoder.decode();
462
+ buffer = buffer.replace(/\r\n/g, "\n");
463
+ if (buffer.trim().length > 0) {
464
+ if (processStreamEventChunk(buffer, handlers)) {
465
+ break;
466
+ }
467
+ }
468
+ resolveStream();
469
+ break;
470
+ }
471
+ buffer += decoder.decode(value, { stream: true });
472
+ buffer = buffer.replace(/\r\n/g, "\n");
473
+ let boundaryIndex;
474
+ while ((boundaryIndex = buffer.indexOf("\n\n")) !== -1) {
475
+ const chunk = buffer.slice(0, boundaryIndex);
476
+ buffer = buffer.slice(boundaryIndex + 2);
477
+ if (!chunk) {
478
+ continue;
479
+ }
480
+ if (processStreamEventChunk(chunk, handlers)) {
481
+ await reader.cancel().catch(() => void 0);
482
+ resolveStream();
483
+ return;
484
+ }
485
+ }
486
+ }
487
+ } catch (error) {
488
+ if (abortBundle.signal.aborted) {
489
+ rejectStream(toAbortError(abortBundle.signal.reason ?? error));
490
+ } else {
491
+ rejectStream(error);
492
+ }
493
+ } finally {
494
+ try {
495
+ reader.releaseLock();
496
+ } catch (error) {
497
+ }
498
+ abortBundle.cleanup();
499
+ }
500
+ })().catch((error) => {
501
+ rejectStream(error);
502
+ });
503
+ return {
504
+ abort,
505
+ finished
506
+ };
507
+ function processStreamEventChunk(chunk, eventHandlers) {
508
+ const dataString = extractSseData(chunk);
509
+ if (dataString == null) {
510
+ return false;
511
+ }
512
+ const trimmed = dataString.trim();
513
+ if (trimmed === "[DONE]") {
514
+ return true;
515
+ }
516
+ let payload2;
517
+ try {
518
+ payload2 = JSON.parse(dataString);
519
+ } catch (error) {
520
+ rejectStream(new AIError("Failed to parse streaming payload", { cause: error }));
521
+ return true;
522
+ }
523
+ try {
524
+ handleStreamPayload(payload2, eventHandlers);
525
+ } catch (error) {
526
+ rejectStream(error);
527
+ return true;
528
+ }
529
+ return false;
530
+ }
531
+ function handleStreamPayload(payload2, eventHandlers) {
532
+ if (!payload2 || typeof payload2 !== "object") {
533
+ return;
534
+ }
535
+ if ("error" in payload2 && payload2.error) {
536
+ const message = typeof payload2.error === "string" ? payload2.error : payload2.error.message;
537
+ throw new AIError(message ?? "AI stream returned an error payload");
538
+ }
539
+ const structured = payload2;
540
+ if (Array.isArray(structured.choices)) {
541
+ for (const choice of structured.choices) {
542
+ if (!choice || typeof choice !== "object") {
543
+ continue;
544
+ }
545
+ const delta = choice.delta;
546
+ if (!delta || typeof delta !== "object") {
547
+ continue;
548
+ }
549
+ const deltaObject = delta;
550
+ const textDelta = extractDeltaText(deltaObject.content);
551
+ if (textDelta) {
552
+ eventHandlers.onTextDelta?.(textDelta);
553
+ }
554
+ const reasoningDelta = extractDeltaText(deltaObject.reasoning);
555
+ if (reasoningDelta) {
556
+ eventHandlers.onReasoningDelta?.(reasoningDelta);
557
+ }
558
+ if (deltaObject.tool_calls !== void 0) {
559
+ eventHandlers.onToolCallDelta?.(deltaObject.tool_calls);
560
+ }
561
+ }
562
+ }
563
+ if (structured.usage) {
564
+ eventHandlers.onUsage?.(structured.usage);
565
+ }
566
+ }
567
+ function extractDeltaText(value) {
568
+ if (!value) {
569
+ return void 0;
570
+ }
571
+ if (typeof value === "string") {
572
+ return value;
573
+ }
574
+ if (Array.isArray(value)) {
575
+ return flattenMessageContent(value);
576
+ }
577
+ if (typeof value === "object" && value !== null && "content" in value && Array.isArray(value.content)) {
578
+ return flattenMessageContent(
579
+ value.content ?? []
580
+ );
581
+ }
582
+ return void 0;
583
+ }
584
+ function extractSseData(chunk) {
585
+ const lines = chunk.split("\n");
586
+ const dataLines = [];
587
+ for (const rawLine of lines) {
588
+ if (!rawLine) {
589
+ continue;
590
+ }
591
+ const match = /^data:(.*)$/.exec(rawLine);
592
+ if (!match) {
593
+ continue;
594
+ }
595
+ const value = match[1];
596
+ dataLines.push(value.startsWith(" ") ? value.slice(1) : value);
597
+ }
598
+ if (dataLines.length === 0) {
599
+ return null;
600
+ }
601
+ return dataLines.join("\n");
602
+ }
603
+ }
604
+ function buildStreamMetadataExtras(options) {
605
+ const streamConfig = {};
606
+ if (options.sendReasoning !== void 0) {
607
+ streamConfig.sendReasoning = options.sendReasoning;
608
+ }
609
+ if (options.includeUsage !== void 0) {
610
+ streamConfig.includeUsage = options.includeUsage;
611
+ }
612
+ if (Object.keys(streamConfig).length === 0) {
613
+ return void 0;
614
+ }
615
+ return {
616
+ openpond: {
617
+ stream: streamConfig
618
+ }
619
+ };
620
+ }
621
+ function buildRequestPayload(options, model, capabilities, metadataExtras) {
622
+ const payload = {
623
+ model,
624
+ messages: options.messages
625
+ };
626
+ const generation = options.generation ?? {};
627
+ assignIfDefined(payload, "temperature", generation.temperature);
628
+ assignIfDefined(payload, "top_p", generation.topP);
629
+ assignIfDefined(payload, "max_tokens", generation.maxTokens);
630
+ assignIfDefined(payload, "stop", generation.stop);
631
+ assignIfDefined(
632
+ payload,
633
+ "frequency_penalty",
634
+ generation.frequencyPenalty
635
+ );
636
+ assignIfDefined(payload, "presence_penalty", generation.presencePenalty);
637
+ assignIfDefined(payload, "response_format", generation.responseFormat);
638
+ const toolExecution = options.toolExecution;
639
+ const enableTools = toolExecution?.enableTools ?? true;
640
+ if (enableTools && capabilities.allowTools) {
641
+ const resolvedTools = resolveToolset(options.tools, toolExecution);
642
+ assignIfDefined(payload, "tools", resolvedTools);
643
+ assignIfDefined(payload, "tool_choice", options.toolChoice);
644
+ } else if (options.toolChoice && options.toolChoice !== "none") {
645
+ payload.tool_choice = "none";
646
+ }
647
+ const metadataPayload = buildMetadataPayload(
648
+ options.metadata,
649
+ toolExecution,
650
+ metadataExtras
651
+ );
652
+ if (metadataPayload) {
653
+ payload.metadata = metadataPayload;
654
+ }
655
+ return payload;
656
+ }
657
+ function assignIfDefined(target, key, value) {
658
+ if (value !== void 0) {
659
+ target[key] = value;
660
+ }
661
+ }
662
+ function buildUrl(baseUrl, path) {
663
+ const sanitizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
664
+ return `${sanitizedBase}${path}`;
665
+ }
666
+ function createAbortBundle(upstreamSignal, timeoutMs) {
667
+ const controller = new AbortController();
668
+ const cleanupCallbacks = [];
669
+ if (upstreamSignal) {
670
+ if (upstreamSignal.aborted) {
671
+ controller.abort(upstreamSignal.reason);
672
+ } else {
673
+ const onAbort = () => controller.abort(upstreamSignal.reason);
674
+ upstreamSignal.addEventListener("abort", onAbort, { once: true });
675
+ cleanupCallbacks.push(
676
+ () => upstreamSignal.removeEventListener("abort", onAbort)
677
+ );
678
+ }
679
+ }
680
+ if (timeoutMs && timeoutMs > 0) {
681
+ const timeoutId = setTimeout(() => {
682
+ controller.abort(new Error("AI request timed out"));
683
+ }, timeoutMs);
684
+ cleanupCallbacks.push(() => clearTimeout(timeoutId));
685
+ }
686
+ return {
687
+ signal: controller.signal,
688
+ abort: () => controller.abort(),
689
+ cleanup: () => {
690
+ cleanupCallbacks.forEach((fn) => fn());
691
+ }
692
+ };
693
+ }
694
+ function collectHeaders(headers) {
695
+ const result = {};
696
+ headers.forEach((value, key) => {
697
+ result[key] = value;
698
+ });
699
+ return result;
700
+ }
701
+ function buildMetadataPayload(base, toolExecution, extras) {
702
+ const metadata = base ? { ...base } : {};
703
+ if (extras) {
704
+ for (const [key, value] of Object.entries(extras)) {
705
+ if (value === void 0) {
706
+ continue;
707
+ }
708
+ if (key === "openpond" && typeof value === "object" && value !== null) {
709
+ const existing = {
710
+ ...metadata.openpond ?? {}
711
+ };
712
+ metadata.openpond = {
713
+ ...existing,
714
+ ...value
715
+ };
716
+ } else {
717
+ metadata[key] = value;
718
+ }
719
+ }
720
+ }
721
+ if (toolExecution) {
722
+ const openpond = {
723
+ ...metadata.openpond ?? {},
724
+ toolExecution
725
+ };
726
+ metadata.openpond = openpond;
727
+ }
728
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
729
+ }
730
+ async function safeParseJson(response) {
731
+ const contentType = response.headers.get("content-type");
732
+ if (!contentType || !contentType.includes("application/json")) {
733
+ return void 0;
734
+ }
735
+ try {
736
+ return await response.json();
737
+ } catch (error) {
738
+ return { error: "Failed to parse error body", cause: String(error) };
739
+ }
740
+ }
741
+ function isPrimaryChoice(choice) {
742
+ return choice.index === 0 || choice.message !== void 0;
743
+ }
744
+ function toAbortError(reason) {
745
+ if (reason instanceof AIAbortError) {
746
+ return reason;
747
+ }
748
+ if (reason instanceof Error) {
749
+ if (reason.name === "AbortError") {
750
+ return new AIAbortError(reason.message || "AI request aborted");
751
+ }
752
+ return new AIAbortError(reason.message);
753
+ }
754
+ return new AIAbortError(String(reason ?? "AI request aborted"));
755
+ }
756
+
757
+ export { AIAbortError, AIError, AIFetchError, AIResponseError, DEFAULT_BASE_URL, DEFAULT_MODEL, DEFAULT_TIMEOUT_MS, WEBSEARCH_TOOL_DEFINITION, WEBSEARCH_TOOL_NAME, createAIClient, ensureTextContent, flattenMessageContent, generateText, getModelConfig, isStreamingSupported, isToolCallingSupported, listModels, normalizeModelName, resolveConfig, resolveToolset, streamText };
758
+ //# sourceMappingURL=index.js.map
759
+ //# sourceMappingURL=index.js.map