@nebutra/agents 1.1.2 → 3.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.
package/README.md CHANGED
@@ -67,7 +67,7 @@ orchestrator.registerAgent(
67
67
  new VercelAIAgent({ id: "assistant", name: "Assistant", description: "Helpful", model: "openai/gpt-5.5", instructions: "..." }),
68
68
  );
69
69
 
70
- const ctx = createAgentContext("org_123", "user_456");
70
+ const ctx = createAgentContext({ tenantId: "org_123", product: "app" }, "user_456");
71
71
  const response = await orchestrator.chat("Hello", ctx);
72
72
  // → response.usage tracks tokens for billing
73
73
  ```
@@ -1,4 +1,4 @@
1
- import { c as AgentConfig, A as AgentMessage, a as AgentContext, b as AgentResponse } from './types-BytC-HfQ.js';
1
+ import { c as AgentConfig, A as AgentMessage, a as AgentContext, b as AgentResponse } from './types-CRbPsTpE.js';
2
2
 
3
3
  /**
4
4
  * BaseAgent — abstract agent with memory, usage tracking, and tenant scoping.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getAgentsEnv,
3
3
  isLangfuseConfigured
4
- } from "./chunk-BMSL4E4A.js";
4
+ } from "./chunk-FCXWOXII.js";
5
5
 
6
6
  // src/observability.ts
7
7
  import { logger } from "@nebutra/logger";
@@ -1,17 +1,22 @@
1
1
  import {
2
- resolveModel
3
- } from "./chunk-BZKIMAGK.js";
2
+ resolveModel,
3
+ toAi302ModelId
4
+ } from "./chunk-HVLFZW6E.js";
4
5
  import {
5
6
  getAgentsEnv
6
- } from "./chunk-BMSL4E4A.js";
7
+ } from "./chunk-FCXWOXII.js";
7
8
 
8
9
  // src/fallback.ts
9
10
  import { logger } from "@nebutra/logger";
10
11
  var ENV_KEY_BY_PROVIDER = {
11
12
  openrouter: "OPENROUTER_API_KEY",
12
13
  anthropic: "ANTHROPIC".concat("_API_KEY"),
13
- openai: "OPENAI".concat("_API_KEY")
14
+ openai: "OPENAI".concat("_API_KEY"),
15
+ ai302: "AI302_API_KEY"
14
16
  };
17
+ function ai302BaseUrl() {
18
+ return globalThis.process?.env?.AI302_BASE_URL ?? "https://api.302.ai/v1";
19
+ }
15
20
  function hasProviderKey(provider) {
16
21
  const k = ENV_KEY_BY_PROVIDER[provider];
17
22
  return Boolean(globalThis.process?.env?.[k]);
@@ -40,6 +45,10 @@ async function buildModel(provider, modelOrPreset) {
40
45
  const openaiModelId = modelId.startsWith("openai/") ? modelId.slice("openai/".length) : modelId;
41
46
  return createOpenAI({ apiKey })(openaiModelId);
42
47
  }
48
+ case "ai302": {
49
+ const { createOpenAI } = await import("@ai-sdk/openai");
50
+ return createOpenAI({ apiKey, baseURL: ai302BaseUrl() })(toAi302ModelId(modelId));
51
+ }
43
52
  }
44
53
  }
45
54
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
@@ -102,7 +111,10 @@ function buildSystemWithCache(systemPrompt) {
102
111
  }
103
112
  var EMBEDDING_CAPABLE = /* @__PURE__ */ new Set([
104
113
  "openrouter",
105
- "openai"
114
+ "openai",
115
+ // 302.AI serves /v1/embeddings — an unauthenticated POST answers
116
+ // `Missing 302 Apikey` rather than 404, so the route exists behind auth.
117
+ "ai302"
106
118
  ]);
107
119
  async function buildEmbeddingModel(provider, modelOrPreset) {
108
120
  const modelId = resolveModel(modelOrPreset);
@@ -119,6 +131,12 @@ async function buildEmbeddingModel(provider, modelOrPreset) {
119
131
  const openaiModelId = modelId.startsWith("openai/") ? modelId.slice("openai/".length) : modelId;
120
132
  return createOpenAI({ apiKey }).textEmbeddingModel(openaiModelId);
121
133
  }
134
+ case "ai302": {
135
+ const { createOpenAI } = await import("@ai-sdk/openai");
136
+ return createOpenAI({ apiKey, baseURL: ai302BaseUrl() }).textEmbeddingModel(
137
+ toAi302ModelId(modelId)
138
+ );
139
+ }
122
140
  case "anthropic": {
123
141
  throw new Error("Anthropic does not expose embedding models");
124
142
  }
@@ -1,6 +1,6 @@
1
1
  // src/env.ts
2
2
  import { z } from "zod";
3
- var FallbackProviderName = z.enum(["openrouter", "anthropic", "openai"]);
3
+ var FallbackProviderName = z.enum(["openrouter", "anthropic", "openai", "ai302"]);
4
4
  var FallbackChain = z.string().transform(
5
5
  (raw) => raw.split(",").map((s) => s.trim()).filter(Boolean)
6
6
  ).pipe(z.array(FallbackProviderName).min(1));
@@ -107,6 +107,7 @@ var BaseAgent = class {
107
107
  const durationMs = Date.now() - startTime;
108
108
  const usage = {
109
109
  tenantId: context.tenantId,
110
+ product: context.product,
110
111
  userId: context.userId,
111
112
  agentId: this.config.id,
112
113
  model: this.config.model,
@@ -145,6 +146,7 @@ var BaseAgent = class {
145
146
  const creditCost = Math.max(1, Math.ceil(totalTokens / 1e4));
146
147
  await deductCredits({
147
148
  organizationId: event.tenantId,
149
+ product: event.product,
148
150
  amount: creditCost,
149
151
  description: `Agent execution: ${event.model}`
150
152
  });
@@ -0,0 +1,71 @@
1
+ // src/sdk/frontier-fallback.generated.ts
2
+ var FRONTIER_FALLBACK = {
3
+ reasoning: "anthropic/claude-opus-5",
4
+ flagship: "anthropic/claude-sonnet-5",
5
+ fast: "anthropic/claude-haiku-4.5",
6
+ "openai-flagship": "openai/gpt-5.6-sol",
7
+ "google-flagship": "google/gemini-3.1-pro-preview",
8
+ "google-fast": "google/gemini-3.7-flash"
9
+ };
10
+ var AI302_ALIASES = {
11
+ fast: "claude-haiku-4-5-20251001"
12
+ };
13
+ var AI302_OPEN_MODELS = {
14
+ "302-deepseek": "deepseek-v4-pro",
15
+ "302-deepseek-fast": "deepseek-v4-flash",
16
+ "302-qwen": "qwen3.8-max",
17
+ "302-glm": "glm-5.3",
18
+ "302-kimi": "kimi-k3",
19
+ "302-minimax": "MiniMax-M3"
20
+ };
21
+
22
+ // src/sdk/models.ts
23
+ var models = {
24
+ // ── Generated frontier tiers — edit via `pnpm gen:frontier-models` ──────────
25
+ ...FRONTIER_FALLBACK,
26
+ /** Embedding model */
27
+ embedding: "openai/text-embedding-3-small",
28
+ /** Embedding model (high-dimensional) */
29
+ "embedding-large": "openai/text-embedding-3-large",
30
+ // --- Open-weight families via 302.AI (use with provider: "ai302") ---
31
+ // Generated with the tiers above. These replaced three SiliconFlow presets
32
+ // naming Qwen2.5-72B, DeepSeek-R1 and DeepSeek-V3 — every one superseded,
33
+ // none with a caller anywhere in the repo, and none checkable without a
34
+ // SiliconFlow key. 302 serves the same families and lists its catalogue,
35
+ // so these are resolved rather than remembered.
36
+ ...AI302_OPEN_MODELS,
37
+ // --- SenseNova Token Plan presets (use with provider: "sensenova") ---
38
+ // Base URL: https://token.sensenova.cn/v1
39
+ // Docs: https://github.com/OpenSenseNova/SenseNova6.7/blob/main/API_CN.md
40
+ /** SenseNova 6.7 Flash-Lite — small multimodal agent (default for i18n CI) */
41
+ "sn-flash-lite": "sensenova-6.7-flash-lite",
42
+ /** DeepSeek V4 Flash via SenseNova Token Plan — long context, cheap */
43
+ "sn-deepseek-flash": "deepseek-v4-flash",
44
+ /** Alias: prefer flash-lite for bulk translation */
45
+ "sn-translate": "sensenova-6.7-flash-lite"
46
+ };
47
+ function resolveModel(modelOrPreset) {
48
+ if (modelOrPreset in models) {
49
+ return models[modelOrPreset];
50
+ }
51
+ return modelOrPreset;
52
+ }
53
+ var AI302_BY_GATEWAY_ID = Object.fromEntries(
54
+ Object.entries(AI302_ALIASES).map(([tier, id]) => [
55
+ FRONTIER_FALLBACK[tier],
56
+ id
57
+ ])
58
+ );
59
+ function toAi302ModelId(modelOrPreset) {
60
+ const modelId = resolveModel(modelOrPreset);
61
+ const alias = AI302_BY_GATEWAY_ID[modelId];
62
+ if (alias) return alias;
63
+ const slash = modelId.indexOf("/");
64
+ return slash === -1 ? modelId : modelId.slice(slash + 1);
65
+ }
66
+
67
+ export {
68
+ models,
69
+ resolveModel,
70
+ toAi302ModelId
71
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isRetryableError
3
- } from "./chunk-XLBS3XUI.js";
3
+ } from "./chunk-C4CC5UEF.js";
4
4
 
5
5
  // src/generation/index.ts
6
6
  import { logger } from "@nebutra/logger";
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  resolveApiKey
3
- } from "./chunk-V6VC2O6Q.js";
3
+ } from "./chunk-YBCIJKC7.js";
4
4
  import {
5
- resolveModel
6
- } from "./chunk-BZKIMAGK.js";
5
+ resolveModel,
6
+ toAi302ModelId
7
+ } from "./chunk-HVLFZW6E.js";
7
8
 
8
9
  // src/sdk/provider.ts
9
10
  import { createOpenAI } from "@ai-sdk/openai";
@@ -38,6 +39,13 @@ function createModel(modelOrPreset, config) {
38
39
  });
39
40
  return provider(modelId);
40
41
  }
42
+ case "ai302": {
43
+ const provider = createOpenAI({
44
+ apiKey,
45
+ baseURL: process.env.AI302_BASE_URL ?? "https://api.302.ai/v1"
46
+ });
47
+ return provider(toAi302ModelId(modelId));
48
+ }
41
49
  case "gateway": {
42
50
  const provider = createOpenAI({
43
51
  apiKey,
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  createModel
3
- } from "./chunk-RNFUEMUB.js";
3
+ } from "./chunk-KC6SOI5Z.js";
4
4
  import {
5
5
  NebutraAIConfigSchema
6
- } from "./chunk-V6VC2O6Q.js";
6
+ } from "./chunk-YBCIJKC7.js";
7
7
  import {
8
8
  assertSafeOpenAIJsonPayload
9
9
  } from "./chunk-VZPQOXWW.js";
10
10
  import {
11
11
  runEmbedWithFallback,
12
12
  runWithFallback
13
- } from "./chunk-XLBS3XUI.js";
13
+ } from "./chunk-C4CC5UEF.js";
14
14
 
15
15
  // src/sdk/index.ts
16
16
  import {
@@ -1,13 +1,28 @@
1
+ import {
2
+ models
3
+ } from "./chunk-HVLFZW6E.js";
4
+
1
5
  // src/sdk/config.ts
2
6
  import { z } from "zod";
3
- var ProviderType = z.enum(["openrouter", "openai", "siliconflow", "sensenova", "gateway"]);
7
+ var ProviderType = z.enum([
8
+ "openrouter",
9
+ "openai",
10
+ "siliconflow",
11
+ "sensenova",
12
+ "ai302",
13
+ "gateway"
14
+ ]);
4
15
  var NebutraAIConfigSchema = z.object({
5
16
  /** Which provider backend to use. Defaults to "openrouter". */
6
17
  provider: ProviderType.default("openrouter"),
7
18
  /** API key override. Falls back to env vars per provider. */
8
19
  apiKey: z.string().optional(),
9
- /** Default model id — OpenRouter / models.dev frontier (not Claude 3.x / GPT-4 era). */
10
- defaultModel: z.string().default("anthropic/claude-sonnet-4.6"),
20
+ /**
21
+ * Default model id. Reads the generated frontier flagship rather than naming
22
+ * a version, so `pnpm gen:frontier-models` moves it and it cannot go stale
23
+ * here independently of everywhere else.
24
+ */
25
+ defaultModel: z.string().default(models.flagship),
11
26
  /** Default temperature for generations. */
12
27
  temperature: z.number().min(0).max(2).default(0.7),
13
28
  /** Default max tokens for output. */
@@ -24,6 +39,7 @@ function resolveApiKey(config) {
24
39
  openai: "OPENAI_API_KEY",
25
40
  siliconflow: "SILICONFLOW_API_KEY",
26
41
  sensenova: "SENSENOVA_API_KEY",
42
+ ai302: "AI302_API_KEY",
27
43
  gateway: "VERCEL_OIDC_TOKEN"
28
44
  };
29
45
  const envVar = envMap[config.provider];
package/dist/env.d.ts CHANGED
@@ -13,6 +13,7 @@ declare const FallbackProviderName: z.ZodEnum<{
13
13
  openrouter: "openrouter";
14
14
  anthropic: "anthropic";
15
15
  openai: "openai";
16
+ ai302: "ai302";
16
17
  }>;
17
18
  type FallbackProviderName = z.infer<typeof FallbackProviderName>;
18
19
  declare const AgentsEnvSchema: z.ZodObject<{
@@ -24,11 +25,13 @@ declare const AgentsEnvSchema: z.ZodObject<{
24
25
  openrouter: "openrouter";
25
26
  anthropic: "anthropic";
26
27
  openai: "openai";
28
+ ai302: "ai302";
27
29
  }>>>>;
28
30
  LLM_EMBEDDING_FALLBACK_CHAIN: z.ZodDefault<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string[], string>>, z.ZodArray<z.ZodEnum<{
29
31
  openrouter: "openrouter";
30
32
  anthropic: "anthropic";
31
33
  openai: "openai";
34
+ ai302: "ai302";
32
35
  }>>>>;
33
36
  }, z.core.$strip>;
34
37
  type AgentsEnv = z.infer<typeof AgentsEnvSchema>;
package/dist/env.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  _resetAgentsEnvCache,
4
4
  getAgentsEnv,
5
5
  isLangfuseConfigured
6
- } from "./chunk-BMSL4E4A.js";
6
+ } from "./chunk-FCXWOXII.js";
7
7
  export {
8
8
  AgentsEnvSchema,
9
9
  _resetAgentsEnvCache,
package/dist/fallback.js CHANGED
@@ -5,9 +5,9 @@ import {
5
5
  runEmbedWithFallback,
6
6
  runWithFallback,
7
7
  withAnthropicCacheControl
8
- } from "./chunk-XLBS3XUI.js";
9
- import "./chunk-BZKIMAGK.js";
10
- import "./chunk-BMSL4E4A.js";
8
+ } from "./chunk-C4CC5UEF.js";
9
+ import "./chunk-HVLFZW6E.js";
10
+ import "./chunk-FCXWOXII.js";
11
11
  export {
12
12
  buildSystemWithCache,
13
13
  filterAvailableProviders,
@@ -5,10 +5,10 @@ import {
5
5
  listGenerationProviders,
6
6
  mockGenerationProvider,
7
7
  registerGenerationProvider
8
- } from "../chunk-RWQL4HXC.js";
9
- import "../chunk-XLBS3XUI.js";
10
- import "../chunk-BZKIMAGK.js";
11
- import "../chunk-BMSL4E4A.js";
8
+ } from "../chunk-HZQXXUKB.js";
9
+ import "../chunk-C4CC5UEF.js";
10
+ import "../chunk-HVLFZW6E.js";
11
+ import "../chunk-FCXWOXII.js";
12
12
  export {
13
13
  _resetGenerationRegistry,
14
14
  generateImage,
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { B as BaseAgent } from './agent-DDGWuUpe.js';
1
+ import { B as BaseAgent } from './agent-DyyFxZf-.js';
2
2
  export { AgentsEnv, AgentsEnvSchema, FallbackProviderName, getAgentsEnv, isLangfuseConfigured } from './env.js';
3
3
  export { CreateFallbackModelOptions, EmbeddingFallbackOptions, FallbackResult, buildSystemWithCache, filterAvailableProviders, isRetryableError, runEmbedWithFallback, runWithFallback, withAnthropicCacheControl } from './fallback.js';
4
4
  export { GenerationCallOptions, GenerationContext, GenerationModality, GenerationProvider, GenerationResult, ImageGenerationRequest, VideoGenerationRequest, _resetGenerationRegistry, generateImage, generateVideo, listGenerationProviders, mockGenerationProvider, registerGenerationProvider } from './generation/index.js';
5
- import { A as AgentMessage, O as OrchestratorConfig, a as AgentContext, b as AgentResponse, R as RouterConfig, c as AgentConfig } from './types-BytC-HfQ.js';
6
- export { d as AgentTool, e as AgentUsageEvent, M as MemoryConfig, T as TokenUsage, f as ToolCallResult } from './types-BytC-HfQ.js';
5
+ import { A as AgentMessage, O as OrchestratorConfig, a as AgentContext, b as AgentResponse, R as RouterConfig, c as AgentConfig } from './types-CRbPsTpE.js';
6
+ export { d as AgentTool, e as AgentUsageEvent, M as MemoryConfig, T as TokenUsage, f as ToolCallResult } from './types-CRbPsTpE.js';
7
7
  export { TelemetryMetadata, buildTelemetryConfig, flushTelemetry, initLangfuse } from './observability.js';
8
8
  export { EmbedOptions, GenerateOptions, GenerateStructuredResult, OPENAI_MAX_PROPERTY_NAME_LENGTH, assertSafeOpenAIJsonPayload, configure, embed, embedMany, findOversizedPropertyName, generateStructured, generateText, getConfig, streamText, validateStructured } from './sdk/index.js';
9
9
  export { BUILT_IN_TOOLS, databaseQueryTool, knowledgeBaseTool, webSearchTool } from './tools.js';
10
10
  export { GenerateTextResult, ModelMessage, StreamTextResult } from 'ai';
11
- export { ModelPreset, models, resolveModel } from './sdk/models.js';
11
+ export { ModelPreset, models, resolveModel, toAi302ModelId } from './sdk/models.js';
12
12
  export { NebutraAIConfig, NebutraAIConfigSchema, ProviderType, ResolvedNebutraAIConfig } from './sdk/config.js';
13
13
  export { createEmbeddingModel, createModel } from './sdk/provider.js';
14
14
  import 'zod';
@@ -153,15 +153,21 @@ declare class AgentRouter {
153
153
  /**
154
154
  * Create a fully-populated AgentContext.
155
155
  * Generates a random conversationId when none is provided.
156
+ *
157
+ * `scope` names the tenant and the product whose balance the run spends:
158
+ * balances are per product, so a run cannot be billed without one.
156
159
  */
157
- declare function createAgentContext(tenantId: string, userId: string, conversationId?: string, metadata?: Record<string, unknown>): AgentContext;
160
+ declare function createAgentContext(scope: {
161
+ tenantId: string;
162
+ product: string;
163
+ }, userId: string, conversationId?: string, metadata?: Record<string, unknown>): AgentContext;
158
164
  /**
159
165
  * Validate that a tenant has remaining quota for agent execution.
160
166
  *
161
167
  * Returns `{ allowed: true, remaining: -1 }` (unlimited) by default.
162
168
  * Integrate with @nebutra/billing entitlements for production usage.
163
169
  */
164
- declare function checkAgentQuota(tenantId: string): Promise<{
170
+ declare function checkAgentQuota(tenantId: string, product: string): Promise<{
165
171
  allowed: boolean;
166
172
  remaining: number;
167
173
  }>;
package/dist/index.js CHANGED
@@ -7,14 +7,14 @@ import {
7
7
  getConfig,
8
8
  streamText,
9
9
  validateStructured
10
- } from "./chunk-GRSMUTUS.js";
10
+ } from "./chunk-S5N743EP.js";
11
11
  import {
12
12
  createEmbeddingModel,
13
13
  createModel
14
- } from "./chunk-RNFUEMUB.js";
14
+ } from "./chunk-KC6SOI5Z.js";
15
15
  import {
16
16
  NebutraAIConfigSchema
17
- } from "./chunk-V6VC2O6Q.js";
17
+ } from "./chunk-YBCIJKC7.js";
18
18
  import {
19
19
  BUILT_IN_TOOLS,
20
20
  databaseQueryTool,
@@ -28,7 +28,7 @@ import {
28
28
  listGenerationProviders,
29
29
  mockGenerationProvider,
30
30
  registerGenerationProvider
31
- } from "./chunk-RWQL4HXC.js";
31
+ } from "./chunk-HZQXXUKB.js";
32
32
  import {
33
33
  OPENAI_MAX_PROPERTY_NAME_LENGTH,
34
34
  assertSafeOpenAIJsonPayload,
@@ -38,7 +38,7 @@ import {
38
38
  buildTelemetryConfig,
39
39
  flushTelemetry,
40
40
  initLangfuse
41
- } from "./chunk-QYZDHC5A.js";
41
+ } from "./chunk-7VL333VJ.js";
42
42
  import {
43
43
  buildSystemWithCache,
44
44
  filterAvailableProviders,
@@ -46,22 +46,23 @@ import {
46
46
  runEmbedWithFallback,
47
47
  runWithFallback,
48
48
  withAnthropicCacheControl
49
- } from "./chunk-XLBS3XUI.js";
49
+ } from "./chunk-C4CC5UEF.js";
50
50
  import {
51
51
  models,
52
- resolveModel
53
- } from "./chunk-BZKIMAGK.js";
52
+ resolveModel,
53
+ toAi302ModelId
54
+ } from "./chunk-HVLFZW6E.js";
54
55
  import {
55
56
  AgentsEnvSchema,
56
57
  getAgentsEnv,
57
58
  isLangfuseConfigured
58
- } from "./chunk-BMSL4E4A.js";
59
+ } from "./chunk-FCXWOXII.js";
59
60
  import {
60
61
  BaseAgent,
61
62
  clearMemory,
62
63
  getMemory,
63
64
  saveMemory
64
- } from "./chunk-RDOFKRI6.js";
65
+ } from "./chunk-HAAM3SJN.js";
65
66
 
66
67
  // src/context.ts
67
68
  var MAX_BIO_CHARS = 2e3;
@@ -228,18 +229,19 @@ ${agentList}`,
228
229
  };
229
230
 
230
231
  // src/tenant.ts
231
- function createAgentContext(tenantId, userId, conversationId, metadata) {
232
+ function createAgentContext(scope, userId, conversationId, metadata) {
232
233
  return {
233
- tenantId,
234
+ tenantId: scope.tenantId,
235
+ product: scope.product,
234
236
  userId,
235
237
  conversationId: conversationId ?? crypto.randomUUID(),
236
238
  ...metadata !== void 0 ? { metadata } : {}
237
239
  };
238
240
  }
239
- async function checkAgentQuota(tenantId) {
241
+ async function checkAgentQuota(tenantId, product) {
240
242
  try {
241
243
  const { getCreditBalance } = await import("@nebutra/billing/credits");
242
- const balance = await getCreditBalance(tenantId);
244
+ const balance = await getCreditBalance(tenantId, product);
243
245
  if (balance.balance > 0) {
244
246
  return { allowed: true, remaining: balance.balance };
245
247
  }
@@ -275,7 +277,7 @@ var AgentOrchestrator = class {
275
277
  }
276
278
  /** Route a message to the best agent and execute. */
277
279
  async chat(message, context) {
278
- await this.assertQuota(context.tenantId);
280
+ await this.assertQuota(context.tenantId, context.product);
279
281
  const agentConfigs = [...this.agents.values()].map((a) => a.config);
280
282
  const agentId = await this.router.route(message, agentConfigs, context, this.defaultAgentId);
281
283
  const agent = this.agents.get(agentId);
@@ -286,8 +288,8 @@ var AgentOrchestrator = class {
286
288
  return agent.run(messages, context);
287
289
  }
288
290
  /** Check tenant quota before execution. */
289
- async assertQuota(tenantId) {
290
- const { allowed } = await checkAgentQuota(tenantId);
291
+ async assertQuota(tenantId, product) {
292
+ const { allowed } = await checkAgentQuota(tenantId, product);
291
293
  if (!allowed) {
292
294
  throw new Error(`Tenant "${tenantId}" has exceeded agent execution quota`);
293
295
  }
@@ -339,6 +341,7 @@ export {
339
341
  runWithFallback,
340
342
  saveMemory,
341
343
  streamText,
344
+ toAi302ModelId,
342
345
  validateStructured,
343
346
  webSearchTool,
344
347
  withAnthropicCacheControl
@@ -3,8 +3,8 @@ import {
3
3
  buildTelemetryConfig,
4
4
  flushTelemetry,
5
5
  initLangfuse
6
- } from "./chunk-QYZDHC5A.js";
7
- import "./chunk-BMSL4E4A.js";
6
+ } from "./chunk-7VL333VJ.js";
7
+ import "./chunk-FCXWOXII.js";
8
8
  export {
9
9
  _resetLangfuseCache,
10
10
  buildTelemetryConfig,
@@ -1,5 +1,5 @@
1
- import { B as BaseAgent } from '../agent-DDGWuUpe.js';
2
- import { c as AgentConfig } from '../types-BytC-HfQ.js';
1
+ import { B as BaseAgent } from '../agent-DyyFxZf-.js';
2
+ import { c as AgentConfig } from '../types-CRbPsTpE.js';
3
3
 
4
4
  /**
5
5
  * LangChain.js agent adapter — stub.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseAgent
3
- } from "../chunk-RDOFKRI6.js";
3
+ } from "../chunk-HAAM3SJN.js";
4
4
 
5
5
  // src/providers/langchain.ts
6
6
  var LangChainAgent = class extends BaseAgent {
@@ -1,5 +1,5 @@
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';
1
+ import { B as BaseAgent } from '../agent-DyyFxZf-.js';
2
+ import { A as AgentMessage, a as AgentContext, b as AgentResponse } from '../types-CRbPsTpE.js';
3
3
 
4
4
  /**
5
5
  * Vercel AI SDK agent adapter.
@@ -3,16 +3,16 @@ import {
3
3
  } from "../chunk-VZPQOXWW.js";
4
4
  import {
5
5
  buildTelemetryConfig
6
- } from "../chunk-QYZDHC5A.js";
6
+ } from "../chunk-7VL333VJ.js";
7
7
  import {
8
8
  runWithFallback,
9
9
  withAnthropicCacheControl
10
- } from "../chunk-XLBS3XUI.js";
11
- import "../chunk-BZKIMAGK.js";
12
- import "../chunk-BMSL4E4A.js";
10
+ } from "../chunk-C4CC5UEF.js";
11
+ import "../chunk-HVLFZW6E.js";
12
+ import "../chunk-FCXWOXII.js";
13
13
  import {
14
14
  BaseAgent
15
- } from "../chunk-RDOFKRI6.js";
15
+ } from "../chunk-HAAM3SJN.js";
16
16
 
17
17
  // src/providers/vercel-ai.ts
18
18
  var VercelAIAgent = class extends BaseAgent {
@@ -16,11 +16,13 @@ import { z } from 'zod';
16
16
  * - openai: Direct OpenAI API access
17
17
  * - siliconflow: SiliconFlow cloud — Qwen, DeepSeek, etc. (OpenAI-compatible, China-optimized)
18
18
  * - sensenova: 商汤 SenseNova — OpenAI-compatible (`compatible-mode/v1`)
19
+ * - ai302: 302.AI aggregator — OpenAI-compatible, broad model catalogue
19
20
  * - gateway: Vercel AI Gateway with OIDC auth (for Vercel-deployed apps)
20
21
  */
21
22
  declare const ProviderType: z.ZodEnum<{
22
23
  openrouter: "openrouter";
23
24
  openai: "openai";
25
+ ai302: "ai302";
24
26
  siliconflow: "siliconflow";
25
27
  sensenova: "sensenova";
26
28
  gateway: "gateway";
@@ -30,6 +32,7 @@ declare const NebutraAIConfigSchema: z.ZodObject<{
30
32
  provider: z.ZodDefault<z.ZodEnum<{
31
33
  openrouter: "openrouter";
32
34
  openai: "openai";
35
+ ai302: "ai302";
33
36
  siliconflow: "siliconflow";
34
37
  sensenova: "sensenova";
35
38
  gateway: "gateway";
@@ -2,7 +2,8 @@ import {
2
2
  NebutraAIConfigSchema,
3
3
  ProviderType,
4
4
  resolveApiKey
5
- } from "../chunk-V6VC2O6Q.js";
5
+ } from "../chunk-YBCIJKC7.js";
6
+ import "../chunk-HVLFZW6E.js";
6
7
  export {
7
8
  NebutraAIConfigSchema,
8
9
  ProviderType,
@@ -3,7 +3,7 @@ import { ModelMessage, JSONValue, GenerateTextResult, StreamTextResult } from 'a
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
 
package/dist/sdk/index.js CHANGED
@@ -7,25 +7,26 @@ import {
7
7
  getConfig,
8
8
  streamText,
9
9
  validateStructured
10
- } from "../chunk-GRSMUTUS.js";
10
+ } from "../chunk-S5N743EP.js";
11
11
  import {
12
12
  createEmbeddingModel,
13
13
  createModel
14
- } from "../chunk-RNFUEMUB.js";
14
+ } from "../chunk-KC6SOI5Z.js";
15
15
  import {
16
16
  NebutraAIConfigSchema
17
- } from "../chunk-V6VC2O6Q.js";
17
+ } from "../chunk-YBCIJKC7.js";
18
18
  import {
19
19
  OPENAI_MAX_PROPERTY_NAME_LENGTH,
20
20
  assertSafeOpenAIJsonPayload,
21
21
  findOversizedPropertyName
22
22
  } from "../chunk-VZPQOXWW.js";
23
- import "../chunk-XLBS3XUI.js";
23
+ import "../chunk-C4CC5UEF.js";
24
24
  import {
25
25
  models,
26
- resolveModel
27
- } from "../chunk-BZKIMAGK.js";
28
- import "../chunk-BMSL4E4A.js";
26
+ resolveModel,
27
+ toAi302ModelId
28
+ } from "../chunk-HVLFZW6E.js";
29
+ import "../chunk-FCXWOXII.js";
29
30
  export {
30
31
  NebutraAIConfigSchema,
31
32
  OPENAI_MAX_PROPERTY_NAME_LENGTH,
@@ -42,5 +43,6 @@ export {
42
43
  models,
43
44
  resolveModel,
44
45
  streamText,
46
+ toAi302ModelId,
45
47
  validateStructured
46
48
  };
@@ -1,46 +1,26 @@
1
- /**
2
- * Model presets for common Nebutra use cases.
3
- *
4
- * All IDs use "vendor/model" format (OpenRouter / SiliconFlow).
5
- * When using direct OpenAI provider, only OpenAI models are valid.
6
- * When using Vercel AI Gateway, use "provider/model" format.
7
- * When using SiliconFlow, use "Vendor/Model" format (e.g. "Qwen/Qwen2.5-72B-Instruct").
8
- *
9
- * These hardcoded ids are the FALLBACK tier of the hybrid model strategy — they
10
- * are the current Pareto frontier (audited 2026-06-05 against OpenRouter ∩
11
- * models.dev). For always-fresh resolution use `resolveFrontierModel(tier)` from
12
- * `@nebutra/ai-providers/catalog`, which picks the newest routable model per tier
13
- * at runtime and falls back to exactly these values when offline.
14
- */
15
1
  declare const models: {
16
- /** High-quality reasoning — default for complex tasks */
17
- readonly flagship: "anthropic/claude-sonnet-4.6";
18
- /** Deep reasoning for architecture and research */
19
- readonly reasoning: "anthropic/claude-opus-4.8";
20
- /** Fast + cheap — chat, summaries, classification */
21
- readonly fast: "anthropic/claude-haiku-4.5";
22
- /** OpenAI flagship */
23
- readonly "openai-flagship": "openai/gpt-5.5";
24
- /** Google flagship */
25
- readonly "google-flagship": "google/gemini-3.1-pro-preview";
26
- /** Google fast */
27
- readonly "google-fast": "google/gemini-3.5-flash";
28
- /** Embedding model */
29
- readonly embedding: "openai/text-embedding-3-small";
30
- /** Embedding model (high-dimensional) */
31
- readonly "embedding-large": "openai/text-embedding-3-large";
32
- /** SiliconFlow — Qwen 2.5 72B (flagship open-source) */
33
- readonly "sf-qwen": "Qwen/Qwen2.5-72B-Instruct";
34
- /** SiliconFlow — DeepSeek R1 reasoning (via SiliconFlow Pro) */
35
- readonly "sf-deepseek-r1": "Pro/deepseek-ai/DeepSeek-R1";
36
- /** SiliconFlow — DeepSeek V3 (fast, capable) */
37
- readonly "sf-deepseek-v3": "deepseek-ai/DeepSeek-V3";
38
2
  /** SenseNova 6.7 Flash-Lite — small multimodal agent (default for i18n CI) */
39
3
  readonly "sn-flash-lite": "sensenova-6.7-flash-lite";
40
4
  /** DeepSeek V4 Flash via SenseNova Token Plan — long context, cheap */
41
5
  readonly "sn-deepseek-flash": "deepseek-v4-flash";
42
6
  /** Alias: prefer flash-lite for bulk translation */
43
7
  readonly "sn-translate": "sensenova-6.7-flash-lite";
8
+ readonly "302-deepseek": "deepseek-v4-pro";
9
+ readonly "302-deepseek-fast": "deepseek-v4-flash";
10
+ readonly "302-qwen": "qwen3.8-max";
11
+ readonly "302-glm": "glm-5.3";
12
+ readonly "302-kimi": "kimi-k3";
13
+ readonly "302-minimax": "MiniMax-M3";
14
+ /** Embedding model */
15
+ readonly embedding: "openai/text-embedding-3-small";
16
+ /** Embedding model (high-dimensional) */
17
+ readonly "embedding-large": "openai/text-embedding-3-large";
18
+ readonly reasoning: "anthropic/claude-opus-5";
19
+ readonly flagship: "anthropic/claude-sonnet-5";
20
+ readonly fast: "anthropic/claude-haiku-4.5";
21
+ readonly "openai-flagship": "openai/gpt-5.6-sol";
22
+ readonly "google-flagship": "google/gemini-3.1-pro-preview";
23
+ readonly "google-fast": "google/gemini-3.7-flash";
44
24
  };
45
25
  type ModelPreset = keyof typeof models;
46
26
  /**
@@ -48,5 +28,6 @@ type ModelPreset = keyof typeof models;
48
28
  * If the input is not a preset key, returns it as-is (passthrough).
49
29
  */
50
30
  declare function resolveModel(modelOrPreset: string): string;
31
+ declare function toAi302ModelId(modelOrPreset: string): string;
51
32
 
52
- export { type ModelPreset, models, resolveModel };
33
+ export { type ModelPreset, models, resolveModel, toAi302ModelId };
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  models,
3
- resolveModel
4
- } from "../chunk-BZKIMAGK.js";
3
+ resolveModel,
4
+ toAi302ModelId
5
+ } from "../chunk-HVLFZW6E.js";
5
6
  export {
6
7
  models,
7
- resolveModel
8
+ resolveModel,
9
+ toAi302ModelId
8
10
  };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createEmbeddingModel,
3
3
  createModel
4
- } from "../chunk-RNFUEMUB.js";
5
- import "../chunk-V6VC2O6Q.js";
6
- import "../chunk-BZKIMAGK.js";
4
+ } from "../chunk-KC6SOI5Z.js";
5
+ import "../chunk-YBCIJKC7.js";
6
+ import "../chunk-HVLFZW6E.js";
7
7
  export {
8
8
  createEmbeddingModel,
9
9
  createModel
package/dist/tools.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as AgentTool } from './types-BytC-HfQ.js';
1
+ import { d as AgentTool } from './types-CRbPsTpE.js';
2
2
 
3
3
  /**
4
4
  * Built-in tool registry for agents.
@@ -30,6 +30,8 @@ interface AgentTool {
30
30
  }
31
31
  interface AgentContext {
32
32
  readonly tenantId: string;
33
+ /** The product whose balance pays for this run (ADR 2026-09-27 product wallets). */
34
+ readonly product: string;
33
35
  readonly userId: string;
34
36
  readonly conversationId: string;
35
37
  readonly metadata?: Record<string, unknown>;
@@ -75,6 +77,7 @@ interface RouterConfig {
75
77
  }
76
78
  interface AgentUsageEvent {
77
79
  readonly tenantId: string;
80
+ readonly product: string;
78
81
  readonly userId: string;
79
82
  readonly agentId: string;
80
83
  readonly model: string;
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@nebutra/agents",
3
- "version": "1.1.2",
3
+ "version": "3.0.0",
4
4
  "description": "Nebutra AI runtime: multi-agent orchestration + Vercel AI SDK helpers (absorbed @nebutra/ai-sdk in 1.0.0)",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "nebutra": {
9
+ "status": "wip",
10
+ "graph": "runtime",
9
11
  "featureId": "agents",
10
12
  "category": "ai",
11
13
  "surface": "model-runtime",
@@ -84,9 +86,9 @@
84
86
  "langfuse": "^3.38.20",
85
87
  "langfuse-vercel": "^3.38.20",
86
88
  "zod": "^4.3.6",
87
- "@nebutra/billing": "0.1.3",
88
- "@nebutra/cache": "0.0.3",
89
- "@nebutra/logger": "0.1.2"
89
+ "@nebutra/billing": "3.0.0",
90
+ "@nebutra/cache": "3.0.0",
91
+ "@nebutra/logger": "3.0.0"
90
92
  },
91
93
  "devDependencies": {
92
94
  "@types/node": "^25.9.1",
@@ -1,46 +0,0 @@
1
- // src/sdk/models.ts
2
- var models = {
3
- /** High-quality reasoning — default for complex tasks */
4
- flagship: "anthropic/claude-sonnet-4.6",
5
- /** Deep reasoning for architecture and research */
6
- reasoning: "anthropic/claude-opus-4.8",
7
- /** Fast + cheap — chat, summaries, classification */
8
- fast: "anthropic/claude-haiku-4.5",
9
- /** OpenAI flagship */
10
- "openai-flagship": "openai/gpt-5.5",
11
- /** Google flagship */
12
- "google-flagship": "google/gemini-3.1-pro-preview",
13
- /** Google fast */
14
- "google-fast": "google/gemini-3.5-flash",
15
- /** Embedding model */
16
- embedding: "openai/text-embedding-3-small",
17
- /** Embedding model (high-dimensional) */
18
- "embedding-large": "openai/text-embedding-3-large",
19
- // --- SiliconFlow presets (use with provider: "siliconflow") ---
20
- /** SiliconFlow — Qwen 2.5 72B (flagship open-source) */
21
- "sf-qwen": "Qwen/Qwen2.5-72B-Instruct",
22
- /** SiliconFlow — DeepSeek R1 reasoning (via SiliconFlow Pro) */
23
- "sf-deepseek-r1": "Pro/deepseek-ai/DeepSeek-R1",
24
- /** SiliconFlow — DeepSeek V3 (fast, capable) */
25
- "sf-deepseek-v3": "deepseek-ai/DeepSeek-V3",
26
- // --- SenseNova Token Plan presets (use with provider: "sensenova") ---
27
- // Base URL: https://token.sensenova.cn/v1
28
- // Docs: https://github.com/OpenSenseNova/SenseNova6.7/blob/main/API_CN.md
29
- /** SenseNova 6.7 Flash-Lite — small multimodal agent (default for i18n CI) */
30
- "sn-flash-lite": "sensenova-6.7-flash-lite",
31
- /** DeepSeek V4 Flash via SenseNova Token Plan — long context, cheap */
32
- "sn-deepseek-flash": "deepseek-v4-flash",
33
- /** Alias: prefer flash-lite for bulk translation */
34
- "sn-translate": "sensenova-6.7-flash-lite"
35
- };
36
- function resolveModel(modelOrPreset) {
37
- if (modelOrPreset in models) {
38
- return models[modelOrPreset];
39
- }
40
- return modelOrPreset;
41
- }
42
-
43
- export {
44
- models,
45
- resolveModel
46
- };