@nebutra/agents 1.1.0 → 1.1.2

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 (67) 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/{src/env.ts → dist/chunk-BMSL4E4A.js} +23 -47
  5. package/dist/{chunk-5JZJ5KMC.js → chunk-BZKIMAGK.js} +16 -7
  6. package/dist/{chunk-NPQECBXL.js → chunk-GRSMUTUS.js} +60 -7
  7. package/dist/{chunk-B7XWL35G.js → chunk-QYZDHC5A.js} +5 -1
  8. package/dist/{chunk-RLWM437Q.js → chunk-RNFUEMUB.js} +10 -3
  9. package/dist/chunk-RWQL4HXC.js +171 -0
  10. package/dist/{chunk-NVPE5EDI.js → chunk-V6VC2O6Q.js} +4 -3
  11. package/dist/chunk-VZPQOXWW.js +47 -0
  12. package/dist/{chunk-5LX742GP.js → chunk-XLBS3XUI.js} +4 -50
  13. package/dist/env.d.ts +42 -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 +21 -328
  20. package/dist/index.js +57 -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 +3 -0
  27. package/dist/sdk/config.js +1 -1
  28. package/dist/sdk/index.d.ts +36 -3
  29. package/dist/sdk/index.js +20 -7
  30. package/dist/sdk/models.d.ts +18 -6
  31. package/dist/sdk/models.js +1 -1
  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 -27
  37. package/.turbo/turbo-build.log +0 -40
  38. package/.turbo/turbo-test.log +0 -18
  39. package/.turbo/turbo-typecheck.log +0 -4
  40. package/AGENTS.md +0 -63
  41. package/CHANGELOG.md +0 -26
  42. package/src/__tests__/cost-observability.test.ts +0 -172
  43. package/src/__tests__/fallback-wiring.test.ts +0 -313
  44. package/src/__tests__/generation.test.ts +0 -111
  45. package/src/__tests__/public-api.test.ts +0 -114
  46. package/src/agent.ts +0 -117
  47. package/src/context.ts +0 -99
  48. package/src/fallback.ts +0 -358
  49. package/src/generation/index.ts +0 -157
  50. package/src/generation/mock-provider.ts +0 -123
  51. package/src/generation/types.ts +0 -87
  52. package/src/index.ts +0 -104
  53. package/src/memory.ts +0 -126
  54. package/src/observability.ts +0 -102
  55. package/src/orchestrator.ts +0 -147
  56. package/src/providers/langchain.ts +0 -28
  57. package/src/providers/vercel-ai.ts +0 -114
  58. package/src/router.ts +0 -158
  59. package/src/sdk/config.ts +0 -73
  60. package/src/sdk/index.ts +0 -214
  61. package/src/sdk/models.ts +0 -57
  62. package/src/sdk/provider.ts +0 -80
  63. package/src/tenant.ts +0 -52
  64. package/src/tools.ts +0 -65
  65. package/src/types.ts +0 -114
  66. package/tsconfig.json +0 -12
  67. package/tsup.config.ts +0 -21
package/dist/index.js CHANGED
@@ -1,50 +1,67 @@
1
+ import {
2
+ configure,
3
+ embed,
4
+ embedMany,
5
+ generateStructured,
6
+ generateText,
7
+ getConfig,
8
+ streamText,
9
+ validateStructured
10
+ } from "./chunk-GRSMUTUS.js";
11
+ import {
12
+ createEmbeddingModel,
13
+ createModel
14
+ } from "./chunk-RNFUEMUB.js";
15
+ import {
16
+ NebutraAIConfigSchema
17
+ } from "./chunk-V6VC2O6Q.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-RWQL4HXC.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-QYZDHC5A.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";
37
- import {
38
- createEmbeddingModel,
39
- createModel
40
- } from "./chunk-RLWM437Q.js";
41
- import {
42
- NebutraAIConfigSchema
43
- } from "./chunk-NVPE5EDI.js";
49
+ } from "./chunk-XLBS3XUI.js";
44
50
  import {
45
51
  models,
46
52
  resolveModel
47
- } from "./chunk-5JZJ5KMC.js";
53
+ } from "./chunk-BZKIMAGK.js";
54
+ import {
55
+ AgentsEnvSchema,
56
+ getAgentsEnv,
57
+ isLangfuseConfigured
58
+ } from "./chunk-BMSL4E4A.js";
59
+ import {
60
+ BaseAgent,
61
+ clearMemory,
62
+ getMemory,
63
+ saveMemory
64
+ } from "./chunk-RDOFKRI6.js";
48
65
 
49
66
  // src/context.ts
50
67
  var MAX_BIO_CHARS = 2e3;
@@ -94,170 +111,8 @@ function buildPersonalizedSystemPrompt(basePrompt, context) {
94
111
  ${basePrompt}`;
95
112
  }
96
113
 
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
114
  // src/router.ts
260
- import { logger as logger2 } from "@nebutra/logger";
115
+ import { logger } from "@nebutra/logger";
261
116
  var AgentRouter = class {
262
117
  config;
263
118
  constructor(config) {
@@ -309,7 +164,7 @@ var AgentRouter = class {
309
164
  bestId = agent.id;
310
165
  }
311
166
  }
312
- logger2.info("Router: keyword match", {
167
+ logger.info("Router: keyword match", {
313
168
  selectedAgentId: bestId,
314
169
  score: bestScore
315
170
  });
@@ -340,14 +195,14 @@ ${agentList}`,
340
195
  const selectedId = result.text.trim();
341
196
  const validAgent = agents.find((a) => a.id === selectedId);
342
197
  if (validAgent) {
343
- logger2.info("Router: LLM classification", {
198
+ logger.info("Router: LLM classification", {
344
199
  selectedAgentId: validAgent.id
345
200
  });
346
201
  return validAgent.id;
347
202
  }
348
203
  return defaultAgentId ?? agents[0]?.id;
349
204
  } catch (error) {
350
- logger2.warn("Router: LLM routing failed, falling back to keyword", {
205
+ logger.warn("Router: LLM routing failed, falling back to keyword", {
351
206
  error
352
207
  });
353
208
  return this.routeByKeyword(message, agents, defaultAgentId);
@@ -365,7 +220,7 @@ ${agentList}`,
365
220
  try {
366
221
  return await this.config.customRouter(message, context);
367
222
  } catch (error) {
368
- logger2.warn("Router: custom router failed", { error });
223
+ logger.warn("Router: custom router failed", { error });
369
224
  if (defaultAgentId) return defaultAgentId;
370
225
  throw error;
371
226
  }
@@ -414,15 +269,11 @@ var AgentOrchestrator = class {
414
269
  registerAgent(agent) {
415
270
  this.agents.set(agent.config.id, agent);
416
271
  }
417
- /**
418
- * Get a registered agent by ID.
419
- */
272
+ /** Get a registered agent by ID. */
420
273
  getAgent(agentId) {
421
274
  return this.agents.get(agentId);
422
275
  }
423
- /**
424
- * Route a message to the best agent and execute.
425
- */
276
+ /** Route a message to the best agent and execute. */
426
277
  async chat(message, context) {
427
278
  await this.assertQuota(context.tenantId);
428
279
  const agentConfigs = [...this.agents.values()].map((a) => a.config);
@@ -434,51 +285,7 @@ var AgentOrchestrator = class {
434
285
  const messages = [{ role: "user", content: message, timestamp: /* @__PURE__ */ new Date() }];
435
286
  return agent.run(messages, context);
436
287
  }
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
- */
288
+ /** Check tenant quota before execution. */
482
289
  async assertQuota(tenantId) {
483
290
  const { allowed } = await checkAgentQuota(tenantId);
484
291
  if (!allowed) {
@@ -493,7 +300,9 @@ export {
493
300
  BUILT_IN_TOOLS,
494
301
  BaseAgent,
495
302
  NebutraAIConfigSchema,
303
+ OPENAI_MAX_PROPERTY_NAME_LENGTH,
496
304
  _resetGenerationRegistry,
305
+ assertSafeOpenAIJsonPayload,
497
306
  buildPersonalizedSystemPrompt,
498
307
  buildSystemWithCache,
499
308
  buildTelemetryConfig,
@@ -507,8 +316,10 @@ export {
507
316
  embed,
508
317
  embedMany,
509
318
  filterAvailableProviders,
319
+ findOversizedPropertyName,
510
320
  flushTelemetry,
511
321
  generateImage,
322
+ generateStructured,
512
323
  generateText,
513
324
  generateVideo,
514
325
  getAgentsEnv,
@@ -528,6 +339,7 @@ export {
528
339
  runWithFallback,
529
340
  saveMemory,
530
341
  streamText,
342
+ validateStructured,
531
343
  webSearchTool,
532
344
  withAnthropicCacheControl
533
345
  };
@@ -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-QYZDHC5A.js";
7
+ import "./chunk-BMSL4E4A.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-QYZDHC5A.js";
7
7
  import {
8
8
  runWithFallback,
9
9
  withAnthropicCacheControl
10
- } from "../chunk-5LX742GP.js";
11
- import "../chunk-5JZJ5KMC.js";
10
+ } from "../chunk-XLBS3XUI.js";
11
+ import "../chunk-BZKIMAGK.js";
12
+ import "../chunk-BMSL4E4A.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,14 @@ 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`)
18
19
  * - gateway: Vercel AI Gateway with OIDC auth (for Vercel-deployed apps)
19
20
  */
20
21
  declare const ProviderType: z.ZodEnum<{
21
22
  openrouter: "openrouter";
22
23
  openai: "openai";
23
24
  siliconflow: "siliconflow";
25
+ sensenova: "sensenova";
24
26
  gateway: "gateway";
25
27
  }>;
26
28
  type ProviderType = z.infer<typeof ProviderType>;
@@ -29,6 +31,7 @@ declare const NebutraAIConfigSchema: z.ZodObject<{
29
31
  openrouter: "openrouter";
30
32
  openai: "openai";
31
33
  siliconflow: "siliconflow";
34
+ sensenova: "sensenova";
32
35
  gateway: "gateway";
33
36
  }>>;
34
37
  apiKey: z.ZodOptional<z.ZodString>;
@@ -2,7 +2,7 @@ import {
2
2
  NebutraAIConfigSchema,
3
3
  ProviderType,
4
4
  resolveApiKey
5
- } from "../chunk-NVPE5EDI.js";
5
+ } from "../chunk-V6VC2O6Q.js";
6
6
  export {
7
7
  NebutraAIConfigSchema,
8
8
  ProviderType,
@@ -1,5 +1,5 @@
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';
@@ -7,6 +7,39 @@ export { ModelPreset, models, resolveModel } 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,45 @@ 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-GRSMUTUS.js";
10
11
  import {
11
12
  createEmbeddingModel,
12
13
  createModel
13
- } from "../chunk-RLWM437Q.js";
14
+ } from "../chunk-RNFUEMUB.js";
14
15
  import {
15
16
  NebutraAIConfigSchema
16
- } from "../chunk-NVPE5EDI.js";
17
+ } from "../chunk-V6VC2O6Q.js";
18
+ import {
19
+ OPENAI_MAX_PROPERTY_NAME_LENGTH,
20
+ assertSafeOpenAIJsonPayload,
21
+ findOversizedPropertyName
22
+ } from "../chunk-VZPQOXWW.js";
23
+ import "../chunk-XLBS3XUI.js";
17
24
  import {
18
25
  models,
19
26
  resolveModel
20
- } from "../chunk-5JZJ5KMC.js";
27
+ } from "../chunk-BZKIMAGK.js";
28
+ import "../chunk-BMSL4E4A.js";
21
29
  export {
22
30
  NebutraAIConfigSchema,
31
+ OPENAI_MAX_PROPERTY_NAME_LENGTH,
32
+ assertSafeOpenAIJsonPayload,
23
33
  configure,
24
34
  createEmbeddingModel,
25
35
  createModel,
26
36
  embed,
27
37
  embedMany,
38
+ findOversizedPropertyName,
39
+ generateStructured,
28
40
  generateText,
29
41
  getConfig,
30
42
  models,
31
43
  resolveModel,
32
- streamText
44
+ streamText,
45
+ validateStructured
33
46
  };