@nebutra/agents 1.1.1 → 2.0.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 (70) hide show
  1. package/LICENSE +21 -676
  2. package/README.md +7 -3
  3. package/dist/{agent-CahnMASx.d.ts → agent-DDGWuUpe.d.ts} +1 -1
  4. package/dist/{chunk-B7XWL35G.js → chunk-7VL333VJ.js} +5 -1
  5. package/dist/{chunk-5LX742GP.js → chunk-C4CC5UEF.js} +25 -53
  6. package/{src/env.ts → dist/chunk-FCXWOXII.js} +23 -47
  7. package/dist/chunk-HVLFZW6E.js +71 -0
  8. package/dist/chunk-HZQXXUKB.js +171 -0
  9. package/dist/{chunk-RLWM437Q.js → chunk-KC6SOI5Z.js} +19 -4
  10. package/dist/{chunk-NPQECBXL.js → chunk-S5N743EP.js} +60 -7
  11. package/dist/chunk-VZPQOXWW.js +47 -0
  12. package/dist/{chunk-NVPE5EDI.js → chunk-YBCIJKC7.js} +20 -3
  13. package/dist/env.d.ts +45 -0
  14. package/dist/env.js +12 -0
  15. package/dist/fallback.d.ts +100 -0
  16. package/dist/fallback.js +18 -0
  17. package/dist/generation/index.d.ts +122 -0
  18. package/dist/generation/index.js +19 -0
  19. package/dist/index.d.ts +22 -329
  20. package/dist/index.js +59 -245
  21. package/dist/observability.d.ts +46 -0
  22. package/dist/observability.js +13 -0
  23. package/dist/providers/langchain.d.ts +2 -2
  24. package/dist/providers/vercel-ai.d.ts +2 -2
  25. package/dist/providers/vercel-ai.js +15 -7
  26. package/dist/sdk/config.d.ts +6 -0
  27. package/dist/sdk/config.js +2 -1
  28. package/dist/sdk/index.d.ts +37 -4
  29. package/dist/sdk/index.js +23 -8
  30. package/dist/sdk/models.d.ts +20 -27
  31. package/dist/sdk/models.js +5 -3
  32. package/dist/sdk/provider.d.ts +1 -0
  33. package/dist/sdk/provider.js +3 -3
  34. package/dist/tools.d.ts +1 -1
  35. package/dist/{types-NtgB3pch.d.ts → types-BytC-HfQ.d.ts} +1 -5
  36. package/package.json +73 -19
  37. package/.turbo/turbo-build.log +0 -40
  38. package/.turbo/turbo-test.log +0 -19
  39. package/.turbo/turbo-typecheck.log +0 -4
  40. package/AGENTS.md +0 -63
  41. package/CHANGELOG.md +0 -36
  42. package/dist/chunk-5JZJ5KMC.js +0 -37
  43. package/src/__tests__/cost-observability.test.ts +0 -172
  44. package/src/__tests__/fallback-wiring.test.ts +0 -313
  45. package/src/__tests__/generation.test.ts +0 -111
  46. package/src/__tests__/public-api.test.ts +0 -114
  47. package/src/__tests__/runtime-gateway.test.ts +0 -108
  48. package/src/agent.ts +0 -117
  49. package/src/context.ts +0 -99
  50. package/src/fallback.ts +0 -358
  51. package/src/gateway.ts +0 -234
  52. package/src/generation/index.ts +0 -157
  53. package/src/generation/mock-provider.ts +0 -123
  54. package/src/generation/types.ts +0 -87
  55. package/src/index.ts +0 -104
  56. package/src/memory.ts +0 -126
  57. package/src/observability.ts +0 -102
  58. package/src/orchestrator.ts +0 -147
  59. package/src/providers/langchain.ts +0 -28
  60. package/src/providers/vercel-ai.ts +0 -114
  61. package/src/router.ts +0 -158
  62. package/src/sdk/config.ts +0 -73
  63. package/src/sdk/index.ts +0 -214
  64. package/src/sdk/models.ts +0 -57
  65. package/src/sdk/provider.ts +0 -80
  66. package/src/tenant.ts +0 -52
  67. package/src/tools.ts +0 -65
  68. package/src/types.ts +0 -114
  69. package/tsconfig.json +0 -12
  70. package/tsup.config.ts +0 -21
package/dist/index.js CHANGED
@@ -1,50 +1,68 @@
1
+ import {
2
+ configure,
3
+ embed,
4
+ embedMany,
5
+ generateStructured,
6
+ generateText,
7
+ getConfig,
8
+ streamText,
9
+ validateStructured
10
+ } from "./chunk-S5N743EP.js";
11
+ import {
12
+ createEmbeddingModel,
13
+ createModel
14
+ } from "./chunk-KC6SOI5Z.js";
15
+ import {
16
+ NebutraAIConfigSchema
17
+ } from "./chunk-YBCIJKC7.js";
1
18
  import {
2
19
  BUILT_IN_TOOLS,
3
20
  databaseQueryTool,
4
21
  knowledgeBaseTool,
5
22
  webSearchTool
6
23
  } from "./chunk-UVL2UVVM.js";
24
+ import {
25
+ _resetGenerationRegistry,
26
+ generateImage,
27
+ generateVideo,
28
+ listGenerationProviders,
29
+ mockGenerationProvider,
30
+ registerGenerationProvider
31
+ } from "./chunk-HZQXXUKB.js";
32
+ import {
33
+ OPENAI_MAX_PROPERTY_NAME_LENGTH,
34
+ assertSafeOpenAIJsonPayload,
35
+ findOversizedPropertyName
36
+ } from "./chunk-VZPQOXWW.js";
7
37
  import {
8
38
  buildTelemetryConfig,
9
39
  flushTelemetry,
10
40
  initLangfuse
11
- } from "./chunk-B7XWL35G.js";
12
- import {
13
- BaseAgent,
14
- clearMemory,
15
- getMemory,
16
- saveMemory
17
- } from "./chunk-RDOFKRI6.js";
41
+ } from "./chunk-7VL333VJ.js";
18
42
  import {
19
- configure,
20
- embed,
21
- embedMany,
22
- generateText,
23
- getConfig,
24
- streamText
25
- } from "./chunk-NPQECBXL.js";
26
- import {
27
- AgentsEnvSchema,
28
43
  buildSystemWithCache,
29
44
  filterAvailableProviders,
30
- getAgentsEnv,
31
- isLangfuseConfigured,
32
45
  isRetryableError,
33
46
  runEmbedWithFallback,
34
47
  runWithFallback,
35
48
  withAnthropicCacheControl
36
- } from "./chunk-5LX742GP.js";
49
+ } from "./chunk-C4CC5UEF.js";
37
50
  import {
38
- createEmbeddingModel,
39
- createModel
40
- } from "./chunk-RLWM437Q.js";
51
+ models,
52
+ resolveModel,
53
+ toAi302ModelId
54
+ } from "./chunk-HVLFZW6E.js";
41
55
  import {
42
- NebutraAIConfigSchema
43
- } from "./chunk-NVPE5EDI.js";
56
+ AgentsEnvSchema,
57
+ getAgentsEnv,
58
+ isLangfuseConfigured
59
+ } from "./chunk-FCXWOXII.js";
44
60
  import {
45
- models,
46
- resolveModel
47
- } from "./chunk-5JZJ5KMC.js";
61
+ BaseAgent,
62
+ clearMemory,
63
+ getMemory,
64
+ saveMemory
65
+ } from "./chunk-RDOFKRI6.js";
48
66
 
49
67
  // src/context.ts
50
68
  var MAX_BIO_CHARS = 2e3;
@@ -94,170 +112,8 @@ function buildPersonalizedSystemPrompt(basePrompt, context) {
94
112
  ${basePrompt}`;
95
113
  }
96
114
 
97
- // src/generation/index.ts
98
- import { logger } from "@nebutra/logger";
99
-
100
- // src/generation/mock-provider.ts
101
- function hash(input) {
102
- let h = 2166136261;
103
- for (let i = 0; i < input.length; i++) {
104
- h ^= input.charCodeAt(i);
105
- h = Math.imul(h, 16777619);
106
- }
107
- return h >>> 0;
108
- }
109
- function escapeXml(s) {
110
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
111
- }
112
- function svgDataUri(label, prompt, w, h) {
113
- const hue = hash(prompt) % 360;
114
- const hue2 = (hue + 40) % 360;
115
- const words = prompt.split(/\s+/);
116
- const lines = [];
117
- let cur = "";
118
- for (const word of words) {
119
- if ((cur + " " + word).trim().length > 32) {
120
- lines.push(cur.trim());
121
- cur = word;
122
- } else {
123
- cur = `${cur} ${word}`;
124
- }
125
- if (lines.length === 4) break;
126
- }
127
- if (cur && lines.length < 4) lines.push(cur.trim());
128
- const tspans = lines.map((ln, i) => `<tspan x="50%" dy="${i === 0 ? 0 : 26}">${escapeXml(ln)}</tspan>`).join("");
129
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
130
- <defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
131
- <stop offset="0" stop-color="hsl(${hue} 70% 55%)"/>
132
- <stop offset="1" stop-color="hsl(${hue2} 70% 45%)"/>
133
- </linearGradient></defs>
134
- <rect width="${w}" height="${h}" fill="url(#g)"/>
135
- <text x="50%" y="14%" fill="rgba(255,255,255,.7)" font-family="sans-serif" font-size="20" text-anchor="middle">${escapeXml(label)}</text>
136
- <text x="50%" y="46%" fill="#fff" font-family="sans-serif" font-size="22" font-weight="600" text-anchor="middle">${tspans}</text>
137
- </svg>`;
138
- const b64 = typeof btoa === "function" ? btoa(unescape(encodeURIComponent(svg))) : Buffer.from(svg, "utf8").toString("base64");
139
- return `data:image/svg+xml;base64,${b64}`;
140
- }
141
- var mockGenerationProvider = {
142
- name: "mock",
143
- envKey: null,
144
- capabilities: ["image", "video"],
145
- async generateImage(req, _ctx) {
146
- const width = req.width ?? 1024;
147
- const height = req.height ?? 1024;
148
- return {
149
- modality: "image",
150
- mimeType: "image/svg+xml",
151
- url: svgDataUri("mock \xB7 image", req.prompt, width, height),
152
- width,
153
- height,
154
- providerName: "mock",
155
- model: req.model ?? "mock-image-1",
156
- usage: { units: 1 }
157
- };
158
- },
159
- async generateVideo(req, _ctx) {
160
- const width = req.width ?? 1280;
161
- const height = req.height ?? 720;
162
- const seconds = req.durationSeconds ?? 5;
163
- return {
164
- modality: "video",
165
- mimeType: "image/svg+xml",
166
- url: svgDataUri(`mock \xB7 video \xB7 ${seconds}s`, req.prompt, width, height),
167
- width,
168
- height,
169
- providerName: "mock",
170
- model: req.model ?? "mock-video-1",
171
- usage: { units: seconds }
172
- };
173
- }
174
- };
175
-
176
- // src/generation/index.ts
177
- var log = logger.child({ module: "agents/generation" });
178
- var _registry = /* @__PURE__ */ new Map();
179
- function registerGenerationProvider(provider) {
180
- _registry.set(provider.name, provider);
181
- }
182
- function _resetGenerationRegistry() {
183
- _registry.clear();
184
- _registry.set(mockGenerationProvider.name, mockGenerationProvider);
185
- }
186
- _resetGenerationRegistry();
187
- function hasEnvKey(provider) {
188
- if (provider.envKey === null) return true;
189
- return Boolean(globalThis.process?.env?.[provider.envKey]);
190
- }
191
- function listGenerationProviders(modality, options = {}) {
192
- const envChain = (globalThis.process?.env?.GENERATION_FALLBACK_CHAIN ?? "").split(",").map((s) => s.trim()).filter(Boolean);
193
- const preferred = options.chain ?? (envChain.length > 0 ? envChain : []);
194
- const mockName = mockGenerationProvider.name;
195
- const all = [...preferred, ..._registry.keys()];
196
- const seen = /* @__PURE__ */ new Set();
197
- const resolved = [];
198
- for (const name of all) {
199
- if (seen.has(name) || name === mockName) continue;
200
- seen.add(name);
201
- const provider = _registry.get(name);
202
- if (!provider) continue;
203
- if (!provider.capabilities.includes(modality)) continue;
204
- if (!hasEnvKey(provider)) continue;
205
- resolved.push(name);
206
- }
207
- resolved.push(mockName);
208
- return resolved;
209
- }
210
- async function runChain(modality, options, ctx, invoke) {
211
- const chain = listGenerationProviders(modality, options);
212
- let lastErr;
213
- for (const name of chain) {
214
- const provider = _registry.get(name);
215
- if (!provider) continue;
216
- try {
217
- const result = await invoke(provider);
218
- log.debug("generation succeeded", {
219
- provider: name,
220
- modality,
221
- tenantId: ctx.tenantId
222
- });
223
- return result;
224
- } catch (err) {
225
- lastErr = err;
226
- const retryable = isRetryableError(err);
227
- log.warn("generation provider failed", {
228
- provider: name,
229
- modality,
230
- retryable,
231
- tenantId: ctx.tenantId
232
- });
233
- if (!retryable && name !== mockGenerationProvider.name) continue;
234
- if (!retryable) throw err;
235
- }
236
- }
237
- throw lastErr instanceof Error ? lastErr : new Error("[@nebutra/agents] generation chain exhausted");
238
- }
239
- async function generateImage(req, ctx, options = {}) {
240
- return runChain("image", options, ctx, (p) => {
241
- if (!p.generateImage) {
242
- throw new Error(`[@nebutra/agents] provider "${p.name}" lacks image support`);
243
- }
244
- return p.generateImage(req, ctx);
245
- });
246
- }
247
- async function generateVideo(req, ctx, options = {}) {
248
- return runChain("video", options, ctx, (p) => {
249
- if (!p.generateVideo) {
250
- throw new Error(`[@nebutra/agents] provider "${p.name}" lacks video support`);
251
- }
252
- return p.generateVideo(req, ctx);
253
- });
254
- }
255
-
256
- // src/orchestrator.ts
257
- import { logger as logger3 } from "@nebutra/logger";
258
-
259
115
  // src/router.ts
260
- import { logger as logger2 } from "@nebutra/logger";
116
+ import { logger } from "@nebutra/logger";
261
117
  var AgentRouter = class {
262
118
  config;
263
119
  constructor(config) {
@@ -309,7 +165,7 @@ var AgentRouter = class {
309
165
  bestId = agent.id;
310
166
  }
311
167
  }
312
- logger2.info("Router: keyword match", {
168
+ logger.info("Router: keyword match", {
313
169
  selectedAgentId: bestId,
314
170
  score: bestScore
315
171
  });
@@ -340,14 +196,14 @@ ${agentList}`,
340
196
  const selectedId = result.text.trim();
341
197
  const validAgent = agents.find((a) => a.id === selectedId);
342
198
  if (validAgent) {
343
- logger2.info("Router: LLM classification", {
199
+ logger.info("Router: LLM classification", {
344
200
  selectedAgentId: validAgent.id
345
201
  });
346
202
  return validAgent.id;
347
203
  }
348
204
  return defaultAgentId ?? agents[0]?.id;
349
205
  } catch (error) {
350
- logger2.warn("Router: LLM routing failed, falling back to keyword", {
206
+ logger.warn("Router: LLM routing failed, falling back to keyword", {
351
207
  error
352
208
  });
353
209
  return this.routeByKeyword(message, agents, defaultAgentId);
@@ -365,7 +221,7 @@ ${agentList}`,
365
221
  try {
366
222
  return await this.config.customRouter(message, context);
367
223
  } catch (error) {
368
- logger2.warn("Router: custom router failed", { error });
224
+ logger.warn("Router: custom router failed", { error });
369
225
  if (defaultAgentId) return defaultAgentId;
370
226
  throw error;
371
227
  }
@@ -414,15 +270,11 @@ var AgentOrchestrator = class {
414
270
  registerAgent(agent) {
415
271
  this.agents.set(agent.config.id, agent);
416
272
  }
417
- /**
418
- * Get a registered agent by ID.
419
- */
273
+ /** Get a registered agent by ID. */
420
274
  getAgent(agentId) {
421
275
  return this.agents.get(agentId);
422
276
  }
423
- /**
424
- * Route a message to the best agent and execute.
425
- */
277
+ /** Route a message to the best agent and execute. */
426
278
  async chat(message, context) {
427
279
  await this.assertQuota(context.tenantId);
428
280
  const agentConfigs = [...this.agents.values()].map((a) => a.config);
@@ -434,51 +286,7 @@ var AgentOrchestrator = class {
434
286
  const messages = [{ role: "user", content: message, timestamp: /* @__PURE__ */ new Date() }];
435
287
  return agent.run(messages, context);
436
288
  }
437
- /**
438
- * Execute a multi-agent pipeline where each step's output feeds the next.
439
- * @experimental — API may change. Use `chat()` for production workloads.
440
- */
441
- async pipeline(steps, input, context) {
442
- await this.assertQuota(context.tenantId);
443
- let currentInput = input;
444
- let lastResponse;
445
- for (const step of steps) {
446
- const agent = this.agents.get(step.agentId);
447
- if (!agent) {
448
- throw new Error(`Pipeline step references unknown agent "${step.agentId}"`);
449
- }
450
- const transformedInput = step.transformInput ? step.transformInput(currentInput) : currentInput;
451
- const messages = [
452
- { role: "user", content: transformedInput, timestamp: /* @__PURE__ */ new Date() }
453
- ];
454
- lastResponse = await agent.run(messages, context);
455
- const assistantMessages = lastResponse.messages.filter((m) => m.role === "assistant");
456
- const lastAssistant = assistantMessages[assistantMessages.length - 1];
457
- currentInput = lastAssistant?.content ?? "";
458
- logger3.info("Pipeline step completed", {
459
- agentId: step.agentId,
460
- tenantId: context.tenantId
461
- });
462
- }
463
- if (!lastResponse) {
464
- throw new Error("Pipeline produced no response (empty steps?)");
465
- }
466
- return lastResponse;
467
- }
468
- /**
469
- * Broadcast a message to ALL registered agents in parallel.
470
- * Returns an array of responses (one per agent).
471
- * @experimental — API may change. Use `chat()` for production workloads.
472
- */
473
- async broadcast(message, context) {
474
- await this.assertQuota(context.tenantId);
475
- const messages = [{ role: "user", content: message, timestamp: /* @__PURE__ */ new Date() }];
476
- const promises = [...this.agents.values()].map((agent) => agent.run(messages, context));
477
- return Promise.all(promises);
478
- }
479
- /**
480
- * Check tenant quota before execution.
481
- */
289
+ /** Check tenant quota before execution. */
482
290
  async assertQuota(tenantId) {
483
291
  const { allowed } = await checkAgentQuota(tenantId);
484
292
  if (!allowed) {
@@ -493,7 +301,9 @@ export {
493
301
  BUILT_IN_TOOLS,
494
302
  BaseAgent,
495
303
  NebutraAIConfigSchema,
304
+ OPENAI_MAX_PROPERTY_NAME_LENGTH,
496
305
  _resetGenerationRegistry,
306
+ assertSafeOpenAIJsonPayload,
497
307
  buildPersonalizedSystemPrompt,
498
308
  buildSystemWithCache,
499
309
  buildTelemetryConfig,
@@ -507,8 +317,10 @@ export {
507
317
  embed,
508
318
  embedMany,
509
319
  filterAvailableProviders,
320
+ findOversizedPropertyName,
510
321
  flushTelemetry,
511
322
  generateImage,
323
+ generateStructured,
512
324
  generateText,
513
325
  generateVideo,
514
326
  getAgentsEnv,
@@ -528,6 +340,8 @@ export {
528
340
  runWithFallback,
529
341
  saveMemory,
530
342
  streamText,
343
+ toAi302ModelId,
344
+ validateStructured,
531
345
  webSearchTool,
532
346
  withAnthropicCacheControl
533
347
  };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * LLM observability via Langfuse.
3
+ *
4
+ * - No-op when env vars are missing — the package works with zero config.
5
+ * - Exposes `experimental_telemetry` settings ready to plug into Vercel AI SDK.
6
+ * - Use `LangfuseExporter` from `langfuse-vercel` in your OTEL NodeSDK setup
7
+ * for full trace export (see README).
8
+ */
9
+ type LangfuseClient = {
10
+ trace: (input: unknown) => unknown;
11
+ flushAsync: () => Promise<void>;
12
+ shutdownAsync: () => Promise<void>;
13
+ };
14
+ /**
15
+ * Returns a configured `Langfuse` client, or `null` when env is missing.
16
+ *
17
+ * Telemetry is OPTIONAL. If LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY are
18
+ * not set, this returns null (no error). Callers must handle the null case.
19
+ */
20
+ declare function initLangfuse(): Promise<LangfuseClient | null>;
21
+ /** Test helper — clears cached client so subsequent init() re-reads env. */
22
+ declare function _resetLangfuseCache(): void;
23
+ interface TelemetryMetadata {
24
+ tenantId?: string | undefined;
25
+ userId?: string | undefined;
26
+ sessionId?: string | undefined;
27
+ agentId?: string | undefined;
28
+ [key: string]: unknown;
29
+ }
30
+ /**
31
+ * Build the `experimental_telemetry` option for AI SDK calls.
32
+ * Returns `{ isEnabled: false }` (a safe no-op) when Langfuse is not configured,
33
+ * which avoids any OTEL span creation cost.
34
+ */
35
+ declare function buildTelemetryConfig(args: {
36
+ functionId: string;
37
+ metadata?: TelemetryMetadata;
38
+ }): {
39
+ isEnabled: boolean;
40
+ functionId?: string;
41
+ metadata?: Record<string, unknown>;
42
+ };
43
+ /** Flush pending telemetry before process exit. Safe to call when disabled. */
44
+ declare function flushTelemetry(): Promise<void>;
45
+
46
+ export { type TelemetryMetadata, _resetLangfuseCache, buildTelemetryConfig, flushTelemetry, initLangfuse };
@@ -0,0 +1,13 @@
1
+ import {
2
+ _resetLangfuseCache,
3
+ buildTelemetryConfig,
4
+ flushTelemetry,
5
+ initLangfuse
6
+ } from "./chunk-7VL333VJ.js";
7
+ import "./chunk-FCXWOXII.js";
8
+ export {
9
+ _resetLangfuseCache,
10
+ buildTelemetryConfig,
11
+ flushTelemetry,
12
+ initLangfuse
13
+ };
@@ -1,5 +1,5 @@
1
- import { B as BaseAgent } from '../agent-CahnMASx.js';
2
- import { c as AgentConfig } from '../types-NtgB3pch.js';
1
+ import { B as BaseAgent } from '../agent-DDGWuUpe.js';
2
+ import { c as AgentConfig } from '../types-BytC-HfQ.js';
3
3
 
4
4
  /**
5
5
  * LangChain.js agent adapter — stub.
@@ -1,5 +1,5 @@
1
- import { B as BaseAgent } from '../agent-CahnMASx.js';
2
- import { A as AgentMessage, a as AgentContext, b as AgentResponse } from '../types-NtgB3pch.js';
1
+ import { B as BaseAgent } from '../agent-DDGWuUpe.js';
2
+ import { A as AgentMessage, a as AgentContext, b as AgentResponse } from '../types-BytC-HfQ.js';
3
3
 
4
4
  /**
5
5
  * Vercel AI SDK agent adapter.
@@ -1,14 +1,18 @@
1
1
  import {
2
- buildTelemetryConfig
3
- } from "../chunk-B7XWL35G.js";
2
+ assertSafeOpenAIJsonPayload
3
+ } from "../chunk-VZPQOXWW.js";
4
4
  import {
5
- BaseAgent
6
- } from "../chunk-RDOFKRI6.js";
5
+ buildTelemetryConfig
6
+ } from "../chunk-7VL333VJ.js";
7
7
  import {
8
8
  runWithFallback,
9
9
  withAnthropicCacheControl
10
- } from "../chunk-5LX742GP.js";
11
- import "../chunk-5JZJ5KMC.js";
10
+ } from "../chunk-C4CC5UEF.js";
11
+ import "../chunk-HVLFZW6E.js";
12
+ import "../chunk-FCXWOXII.js";
13
+ import {
14
+ BaseAgent
15
+ } from "../chunk-RDOFKRI6.js";
12
16
 
13
17
  // src/providers/vercel-ai.ts
14
18
  var VercelAIAgent = class extends BaseAgent {
@@ -19,7 +23,7 @@ var VercelAIAgent = class extends BaseAgent {
19
23
  t.name,
20
24
  dynamicTool({
21
25
  description: t.description,
22
- inputSchema: t.inputSchema,
26
+ inputSchema: assertSafeToolSchema(t.name, t.inputSchema),
23
27
  execute: async (args) => t.execute(args, context)
24
28
  })
25
29
  ])
@@ -78,6 +82,10 @@ var VercelAIAgent = class extends BaseAgent {
78
82
  };
79
83
  }
80
84
  };
85
+ function assertSafeToolSchema(name, inputSchema) {
86
+ assertSafeOpenAIJsonPayload(`OpenAI tool schema "${name}"`, inputSchema);
87
+ return inputSchema;
88
+ }
81
89
  export {
82
90
  VercelAIAgent
83
91
  };
@@ -15,12 +15,16 @@ import { z } from 'zod';
15
15
  * - openrouter: 300+ models, automatic failover, pay-as-you-go (default)
16
16
  * - openai: Direct OpenAI API access
17
17
  * - siliconflow: SiliconFlow cloud — Qwen, DeepSeek, etc. (OpenAI-compatible, China-optimized)
18
+ * - sensenova: 商汤 SenseNova — OpenAI-compatible (`compatible-mode/v1`)
19
+ * - ai302: 302.AI aggregator — OpenAI-compatible, broad model catalogue
18
20
  * - gateway: Vercel AI Gateway with OIDC auth (for Vercel-deployed apps)
19
21
  */
20
22
  declare const ProviderType: z.ZodEnum<{
21
23
  openrouter: "openrouter";
22
24
  openai: "openai";
25
+ ai302: "ai302";
23
26
  siliconflow: "siliconflow";
27
+ sensenova: "sensenova";
24
28
  gateway: "gateway";
25
29
  }>;
26
30
  type ProviderType = z.infer<typeof ProviderType>;
@@ -28,7 +32,9 @@ declare const NebutraAIConfigSchema: z.ZodObject<{
28
32
  provider: z.ZodDefault<z.ZodEnum<{
29
33
  openrouter: "openrouter";
30
34
  openai: "openai";
35
+ ai302: "ai302";
31
36
  siliconflow: "siliconflow";
37
+ sensenova: "sensenova";
32
38
  gateway: "gateway";
33
39
  }>>;
34
40
  apiKey: z.ZodOptional<z.ZodString>;
@@ -2,7 +2,8 @@ import {
2
2
  NebutraAIConfigSchema,
3
3
  ProviderType,
4
4
  resolveApiKey
5
- } from "../chunk-NVPE5EDI.js";
5
+ } from "../chunk-YBCIJKC7.js";
6
+ import "../chunk-HVLFZW6E.js";
6
7
  export {
7
8
  NebutraAIConfigSchema,
8
9
  ProviderType,
@@ -1,12 +1,45 @@
1
1
  import * as ai from 'ai';
2
- import { JSONValue, ModelMessage, GenerateTextResult, StreamTextResult } from 'ai';
2
+ import { ModelMessage, JSONValue, GenerateTextResult, StreamTextResult } from 'ai';
3
3
  export { GenerateTextResult, ModelMessage, StreamTextResult } from 'ai';
4
4
  import { NebutraAIConfig, ResolvedNebutraAIConfig } from './config.js';
5
5
  export { NebutraAIConfigSchema, ProviderType } from './config.js';
6
- export { ModelPreset, models, resolveModel } from './models.js';
6
+ export { ModelPreset, models, resolveModel, toAi302ModelId } from './models.js';
7
7
  export { createEmbeddingModel, createModel } from './provider.js';
8
8
  import 'zod';
9
9
 
10
+ declare const OPENAI_MAX_PROPERTY_NAME_LENGTH = 256;
11
+ interface OversizedPropertyNameIssue {
12
+ readonly path: string;
13
+ readonly length: number;
14
+ }
15
+ declare function findOversizedPropertyName(value: unknown, maxLength?: number): OversizedPropertyNameIssue | null;
16
+ declare function assertSafeOpenAIJsonPayload(label: string, value: unknown): void;
17
+
18
+ /**
19
+ * Structured generation — the JSON-Schema → forced-tool bridge.
20
+ *
21
+ * AI SDK v6 removed `generateObject`. To get a schema-shaped object we force a
22
+ * single `_output` tool whose `inputSchema` IS the caller's JSON Schema, then
23
+ * AJV-recheck the model's tool-call args (defense in depth — the provider's
24
+ * structured output should already match, but tenants supply the schema). The
25
+ * call goes through the same provider fallback chain as every other generation.
26
+ */
27
+
28
+ interface GenerateStructuredResult {
29
+ /** The object the model produced — already validated against the schema. */
30
+ readonly output: unknown;
31
+ readonly usage: {
32
+ readonly inputTokens: number;
33
+ readonly outputTokens: number;
34
+ readonly reasoningOutputTokens: number;
35
+ };
36
+ }
37
+ /** Validate a value against a JSON Schema. Throws with a readable message. */
38
+ declare function validateStructured(value: unknown, schema: Record<string, unknown>): void;
39
+ declare function generateStructured(messages: ModelMessage[], schema: Record<string, unknown>, options?: {
40
+ model?: string;
41
+ }): Promise<GenerateStructuredResult>;
42
+
10
43
  /**
11
44
  * Initialise the global Nebutra AI configuration.
12
45
  * Call once in your app entry point (e.g. instrumentation.ts or layout.tsx).
@@ -40,7 +73,7 @@ interface StreamFinishEvent {
40
73
  };
41
74
  }
42
75
  interface GenerateOptions {
43
- /** Model ID or preset alias (e.g. "flagship", "fast", "anthropic/claude-sonnet-4"). */
76
+ /** Model ID or preset alias (e.g. "flagship", "fast", "anthropic/claude-sonnet-4.6"). */
44
77
  model?: string;
45
78
  /** System prompt prepended to the conversation. */
46
79
  system?: string;
@@ -91,4 +124,4 @@ declare function embed(value: string, options?: EmbedOptions): Promise<ai.EmbedR
91
124
  */
92
125
  declare function embedMany(values: string[], options?: EmbedOptions): Promise<ai.EmbedManyResult>;
93
126
 
94
- export { type EmbedOptions, type GenerateOptions, NebutraAIConfig, ResolvedNebutraAIConfig, type StreamFinishEvent, type StreamOptions, configure, embed, embedMany, generateText, getConfig, streamText };
127
+ export { type EmbedOptions, type GenerateOptions, type GenerateStructuredResult, NebutraAIConfig, OPENAI_MAX_PROPERTY_NAME_LENGTH, ResolvedNebutraAIConfig, type StreamFinishEvent, type StreamOptions, assertSafeOpenAIJsonPayload, configure, embed, embedMany, findOversizedPropertyName, generateStructured, generateText, getConfig, streamText, validateStructured };
package/dist/sdk/index.js CHANGED
@@ -2,32 +2,47 @@ import {
2
2
  configure,
3
3
  embed,
4
4
  embedMany,
5
+ generateStructured,
5
6
  generateText,
6
7
  getConfig,
7
- streamText
8
- } from "../chunk-NPQECBXL.js";
9
- import "../chunk-5LX742GP.js";
8
+ streamText,
9
+ validateStructured
10
+ } from "../chunk-S5N743EP.js";
10
11
  import {
11
12
  createEmbeddingModel,
12
13
  createModel
13
- } from "../chunk-RLWM437Q.js";
14
+ } from "../chunk-KC6SOI5Z.js";
14
15
  import {
15
16
  NebutraAIConfigSchema
16
- } from "../chunk-NVPE5EDI.js";
17
+ } from "../chunk-YBCIJKC7.js";
18
+ import {
19
+ OPENAI_MAX_PROPERTY_NAME_LENGTH,
20
+ assertSafeOpenAIJsonPayload,
21
+ findOversizedPropertyName
22
+ } from "../chunk-VZPQOXWW.js";
23
+ import "../chunk-C4CC5UEF.js";
17
24
  import {
18
25
  models,
19
- resolveModel
20
- } from "../chunk-5JZJ5KMC.js";
26
+ resolveModel,
27
+ toAi302ModelId
28
+ } from "../chunk-HVLFZW6E.js";
29
+ import "../chunk-FCXWOXII.js";
21
30
  export {
22
31
  NebutraAIConfigSchema,
32
+ OPENAI_MAX_PROPERTY_NAME_LENGTH,
33
+ assertSafeOpenAIJsonPayload,
23
34
  configure,
24
35
  createEmbeddingModel,
25
36
  createModel,
26
37
  embed,
27
38
  embedMany,
39
+ findOversizedPropertyName,
40
+ generateStructured,
28
41
  generateText,
29
42
  getConfig,
30
43
  models,
31
44
  resolveModel,
32
- streamText
45
+ streamText,
46
+ toAi302ModelId,
47
+ validateStructured
33
48
  };