@nebutra/agents 1.0.0 → 1.1.1

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 (39) hide show
  1. package/.turbo/turbo-build.log +40 -0
  2. package/.turbo/turbo-test.log +19 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/CHANGELOG.md +36 -0
  5. package/dist/agent-CahnMASx.d.ts +32 -0
  6. package/dist/chunk-5JZJ5KMC.js +37 -0
  7. package/dist/chunk-5LX742GP.js +224 -0
  8. package/dist/chunk-B7XWL35G.js +60 -0
  9. package/dist/chunk-NPQECBXL.js +82 -0
  10. package/dist/chunk-NVPE5EDI.js +42 -0
  11. package/dist/chunk-RDOFKRI6.js +166 -0
  12. package/dist/chunk-RLWM437Q.js +61 -0
  13. package/dist/chunk-UVL2UVVM.js +56 -0
  14. package/dist/index.d.ts +476 -0
  15. package/dist/index.js +533 -0
  16. package/dist/providers/langchain.d.ts +18 -0
  17. package/dist/providers/langchain.js +23 -0
  18. package/dist/providers/vercel-ai.d.ts +16 -0
  19. package/dist/providers/vercel-ai.js +83 -0
  20. package/dist/sdk/config.d.ts +48 -0
  21. package/dist/sdk/config.js +10 -0
  22. package/dist/sdk/index.d.ts +94 -0
  23. package/dist/sdk/index.js +33 -0
  24. package/dist/sdk/models.d.ts +40 -0
  25. package/dist/sdk/models.js +8 -0
  26. package/dist/sdk/provider.d.ts +21 -0
  27. package/dist/sdk/provider.js +10 -0
  28. package/dist/tools.d.ts +20 -0
  29. package/dist/tools.js +12 -0
  30. package/dist/types-NtgB3pch.d.ts +92 -0
  31. package/package.json +8 -13
  32. package/src/__tests__/cost-observability.test.ts +1 -1
  33. package/src/__tests__/generation.test.ts +111 -0
  34. package/src/__tests__/runtime-gateway.test.ts +108 -0
  35. package/src/gateway.ts +234 -0
  36. package/src/generation/index.ts +157 -0
  37. package/src/generation/mock-provider.ts +123 -0
  38. package/src/generation/types.ts +87 -0
  39. package/src/index.ts +18 -0
@@ -0,0 +1,40 @@
1
+
2
+ > @nebutra/agents@1.1.1 build /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/agents
3
+ > tsup
4
+
5
+ CLI Building entry: src/index.ts, src/tools.ts, src/providers/langchain.ts, src/providers/vercel-ai.ts, src/sdk/config.ts, src/sdk/index.ts, src/sdk/models.ts, src/sdk/provider.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.1
8
+ CLI Using tsup config: /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/agents/tsup.config.ts
9
+ CLI Target: node20
10
+ CLI Cleaning output folder
11
+ ESM Build start
12
+ ESM dist/index.js 16.08 KB
13
+ ESM dist/providers/langchain.js 536.00 B
14
+ ESM dist/sdk/config.js 166.00 B
15
+ ESM dist/chunk-UVL2UVVM.js 1.26 KB
16
+ ESM dist/tools.js 203.00 B
17
+ ESM dist/providers/vercel-ai.js 2.43 KB
18
+ ESM dist/chunk-B7XWL35G.js 1.51 KB
19
+ ESM dist/chunk-RDOFKRI6.js 4.71 KB
20
+ ESM dist/sdk/index.js 534.00 B
21
+ ESM dist/chunk-NPQECBXL.js 2.37 KB
22
+ ESM dist/chunk-5LX742GP.js 7.78 KB
23
+ ESM dist/sdk/provider.js 190.00 B
24
+ ESM dist/sdk/models.js 102.00 B
25
+ ESM dist/chunk-RLWM437Q.js 1.63 KB
26
+ ESM dist/chunk-NVPE5EDI.js 1.49 KB
27
+ ESM dist/chunk-5JZJ5KMC.js 1.22 KB
28
+ ESM ⚡️ Build success in 48ms
29
+ DTS Build start
30
+ DTS ⚡️ Build success in 5575ms
31
+ DTS dist/index.d.ts 19.71 KB
32
+ DTS dist/tools.d.ts 724.00 B
33
+ DTS dist/providers/langchain.d.ts 546.00 B
34
+ DTS dist/providers/vercel-ai.d.ts 569.00 B
35
+ DTS dist/sdk/index.d.ts 3.88 KB
36
+ DTS dist/sdk/models.d.ts 1.73 KB
37
+ DTS dist/sdk/provider.d.ts 882.00 B
38
+ DTS dist/sdk/config.d.ts 1.80 KB
39
+ DTS dist/agent-CahnMASx.d.ts 1.10 KB
40
+ DTS dist/types-NtgB3pch.d.ts 3.02 KB
@@ -0,0 +1,19 @@
1
+
2
+ > @nebutra/agents@1.1.1 test /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/agents
3
+ > vitest run
4
+
5
+
6
+  RUN  v4.1.4 /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/agents
7
+
8
+ ✓ src/__tests__/fallback-wiring.test.ts (9 tests) 235ms
9
+ ✓ src/__tests__/cost-observability.test.ts (18 tests) 510ms
10
+ ✓ falls through retryable errors and returns the next provider's result  335ms
11
+ ✓ src/__tests__/public-api.test.ts (16 tests) 72ms
12
+ ✓ src/__tests__/runtime-gateway.test.ts (3 tests) 80ms
13
+ ✓ src/__tests__/generation.test.ts (7 tests) 61ms
14
+
15
+  Test Files  5 passed (5)
16
+  Tests  53 passed (53)
17
+  Start at  08:15:21
18
+  Duration  4.98s (transform 2.66s, setup 0ms, import 7.42s, tests 958ms, environment 1ms)
19
+
@@ -0,0 +1,4 @@
1
+
2
+ > @nebutra/agents@1.1.1 typecheck /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/agents
3
+ > tsc --noEmit
4
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,36 @@
1
+ # @nebutra/agents
2
+
3
+ ## 1.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Publish registry package metadata under the MIT license.
8
+
9
+ - Updated dependencies []:
10
+ - @nebutra/billing@0.1.2
11
+ - @nebutra/cache@0.0.2
12
+ - @nebutra/logger@0.1.1
13
+
14
+ ## 1.1.0
15
+
16
+ ### Minor Changes
17
+
18
+ - [`092a1ce`](https://github.com/Nebutra/Nebutra-Sailor/commit/092a1ce810965e2d81767642e4bad05b80df81f4) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Add the Atelier agentic creative-canvas capability.
19
+ - `@nebutra/agents`: new image/video **generation modality**
20
+ (`@nebutra/agents/generation`) on the same env-key-gated provider layer as
21
+ the LLM fallback chain, with a deterministic always-available mock provider.
22
+ - `@nebutra/atelier-canvas`: new package — server-authoritative non-overlap
23
+ placement, persist-then-broadcast consistency, per-`(tenant,canvas)`
24
+ serialization, tenant-scoped store (in-memory + Prisma adapter). The
25
+ `./agent` subpath adds the creative-direction prompt strategy + generation
26
+ tool + `AgentConfig` factory (optional `@nebutra/agents` peer).
27
+ - `@nebutra/feature-flags`: new `FLAGS.ATELIER_CANVAS` (off by default).
28
+
29
+ Additive `AtelierCanvas` Prisma model. A flag-gated `/atelier` demo route in
30
+ `apps/web` exercises the loop end-to-end with the mock provider (no AI key
31
+ required).
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies [[`5d3d7e6`](https://github.com/Nebutra/Nebutra-Sailor/commit/5d3d7e6c59cae5aa242bb988b75a9888cfd0db39)]:
36
+ - @nebutra/billing@0.1.1
@@ -0,0 +1,32 @@
1
+ import { c as AgentConfig, A as AgentMessage, a as AgentContext, b as AgentResponse } from './types-NtgB3pch.js';
2
+
3
+ /**
4
+ * BaseAgent — abstract agent with memory, usage tracking, and tenant scoping.
5
+ *
6
+ * Concrete agents (VercelAIAgent, LangChainAgent, etc.) extend this class
7
+ * and implement the `execute()` method for their specific SDK.
8
+ */
9
+
10
+ declare class BaseAgent {
11
+ readonly config: AgentConfig;
12
+ constructor(config: AgentConfig);
13
+ /**
14
+ * Run the agent with full lifecycle:
15
+ * 1. Load long-term memory (if configured)
16
+ * 2. Trim to short-term window
17
+ * 3. Delegate to provider-specific `execute()`
18
+ * 4. Track usage for billing
19
+ * 5. Persist new messages to memory
20
+ */
21
+ run(messages: readonly AgentMessage[], context: AgentContext): Promise<AgentResponse>;
22
+ /**
23
+ * Provider-specific execution. Subclasses MUST override this.
24
+ */
25
+ protected execute(_messages: readonly AgentMessage[], _context: AgentContext): Promise<AgentResponse>;
26
+ /**
27
+ * Emit a usage event for downstream billing / metering.
28
+ */
29
+ private emitUsage;
30
+ }
31
+
32
+ export { BaseAgent as B };
@@ -0,0 +1,37 @@
1
+ // src/sdk/models.ts
2
+ var models = {
3
+ /** High-quality reasoning — default for complex tasks */
4
+ flagship: "anthropic/claude-sonnet-4",
5
+ /** Deep reasoning for architecture and research */
6
+ reasoning: "anthropic/claude-opus-4",
7
+ /** Fast + cheap — chat, summaries, classification */
8
+ fast: "anthropic/claude-haiku-4",
9
+ /** OpenAI flagship */
10
+ "openai-flagship": "openai/gpt-5.4",
11
+ /** Google flagship */
12
+ "google-flagship": "google/gemini-2.5-pro",
13
+ /** Google fast */
14
+ "google-fast": "google/gemini-2.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
+ };
27
+ function resolveModel(modelOrPreset) {
28
+ if (modelOrPreset in models) {
29
+ return models[modelOrPreset];
30
+ }
31
+ return modelOrPreset;
32
+ }
33
+
34
+ export {
35
+ models,
36
+ resolveModel
37
+ };
@@ -0,0 +1,224 @@
1
+ import {
2
+ resolveModel
3
+ } from "./chunk-5JZJ5KMC.js";
4
+
5
+ // src/env.ts
6
+ import { z } from "zod";
7
+ var FallbackProviderName = z.enum(["openrouter", "anthropic", "openai"]);
8
+ var FallbackChain = z.string().transform(
9
+ (raw) => raw.split(",").map((s) => s.trim()).filter(Boolean)
10
+ ).pipe(z.array(FallbackProviderName).min(1));
11
+ var AgentsEnvSchema = z.object({
12
+ // ── Anthropic (direct) ─────────────────────────────────────────────────
13
+ ANTHROPIC_API_KEY: z.string().optional(),
14
+ // ── Langfuse (LLM tracing — optional) ─────────────────────────────────
15
+ LANGFUSE_PUBLIC_KEY: z.string().optional(),
16
+ LANGFUSE_SECRET_KEY: z.string().optional(),
17
+ LANGFUSE_HOST: z.string().url().default("https://cloud.langfuse.com"),
18
+ // ── Multi-provider fallback chain ─────────────────────────────────────
19
+ /**
20
+ * Comma-separated chain of providers tried in order on retryable failures.
21
+ * Default: "openrouter,anthropic,openai" — OpenRouter first (multi-model),
22
+ * then direct Anthropic (prompt caching), then direct OpenAI as last resort.
23
+ */
24
+ LLM_FALLBACK_CHAIN: FallbackChain.default(() => [
25
+ "openrouter",
26
+ "anthropic",
27
+ "openai"
28
+ ]),
29
+ /**
30
+ * Comma-separated chain of providers tried for EMBEDDINGS, in order.
31
+ * Default: "openrouter,openai" — Anthropic does not currently expose
32
+ * embedding models, so it is excluded by default. If unset, falls back
33
+ * to LLM_FALLBACK_CHAIN with embedding-incompatible providers filtered out.
34
+ */
35
+ LLM_EMBEDDING_FALLBACK_CHAIN: FallbackChain.default(() => [
36
+ "openrouter",
37
+ "openai"
38
+ ])
39
+ });
40
+ var _cached;
41
+ function getAgentsEnv() {
42
+ if (_cached) return _cached;
43
+ _cached = AgentsEnvSchema.parse(globalThis.process?.env ?? {});
44
+ return _cached;
45
+ }
46
+ function isLangfuseConfigured() {
47
+ const env = getAgentsEnv();
48
+ return Boolean(env.LANGFUSE_PUBLIC_KEY && env.LANGFUSE_SECRET_KEY);
49
+ }
50
+
51
+ // src/fallback.ts
52
+ import { logger } from "@nebutra/logger";
53
+ var ENV_KEY_BY_PROVIDER = {
54
+ openrouter: "OPENROUTER_API_KEY",
55
+ anthropic: "ANTHROPIC".concat("_API_KEY"),
56
+ openai: "OPENAI".concat("_API_KEY")
57
+ };
58
+ function hasProviderKey(provider) {
59
+ const k = ENV_KEY_BY_PROVIDER[provider];
60
+ return Boolean(globalThis.process?.env?.[k]);
61
+ }
62
+ function filterAvailableProviders(chain) {
63
+ const filtered = chain.filter(hasProviderKey);
64
+ return filtered.length > 0 ? filtered : chain;
65
+ }
66
+ async function buildModel(provider, modelOrPreset) {
67
+ const modelId = resolveModel(modelOrPreset);
68
+ const envKey = ENV_KEY_BY_PROVIDER[provider];
69
+ const apiKey = globalThis.process?.env?.[envKey];
70
+ if (!apiKey) throw new Error(`${envKey} missing`);
71
+ switch (provider) {
72
+ case "openrouter": {
73
+ const { createOpenRouter } = await import("@openrouter/ai-sdk-provider");
74
+ return createOpenRouter({ apiKey }).chat(modelId);
75
+ }
76
+ case "anthropic": {
77
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
78
+ const anthropicModelId = modelId.startsWith("anthropic/") ? modelId.slice("anthropic/".length) : modelId;
79
+ return createAnthropic({ apiKey })(anthropicModelId);
80
+ }
81
+ case "openai": {
82
+ const { createOpenAI } = await import("@ai-sdk/openai");
83
+ const openaiModelId = modelId.startsWith("openai/") ? modelId.slice("openai/".length) : modelId;
84
+ return createOpenAI({ apiKey })(openaiModelId);
85
+ }
86
+ }
87
+ }
88
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
89
+ function isRetryableError(error) {
90
+ if (!error || typeof error !== "object") return false;
91
+ const e = error;
92
+ const status = typeof e.statusCode === "number" ? e.statusCode : typeof e.status === "number" ? e.status : void 0;
93
+ if (status !== void 0 && RETRYABLE_STATUS.has(status)) return true;
94
+ const code = typeof e.code === "string" ? e.code : "";
95
+ if (code === "ECONNRESET" || code === "ETIMEDOUT" || code === "ENOTFOUND" || code === "EAI_AGAIN") {
96
+ return true;
97
+ }
98
+ if (e.isRetryable === true) return true;
99
+ return false;
100
+ }
101
+ async function runWithFallback(invoke, options = {}) {
102
+ const env = getAgentsEnv();
103
+ const rawChain = options.chain ?? env.LLM_FALLBACK_CHAIN;
104
+ const chain = options.filterAvailable === false ? rawChain : filterAvailableProviders(rawChain);
105
+ const model = options.model ?? "flagship";
106
+ let lastError;
107
+ let attempts = 0;
108
+ for (const provider of chain) {
109
+ attempts += 1;
110
+ try {
111
+ const lm = await buildModel(provider, model);
112
+ const result = await invoke(lm);
113
+ if (attempts > 1) {
114
+ logger.info("LLM fallback succeeded", { provider, attempts });
115
+ }
116
+ return { result, provider, attempts };
117
+ } catch (error) {
118
+ lastError = error;
119
+ if (!isRetryableError(error)) {
120
+ logger.error("LLM call failed (non-retryable)", { provider, error });
121
+ throw error;
122
+ }
123
+ logger.warn("LLM provider failed \u2014 trying next in chain", {
124
+ provider,
125
+ nextIndex: attempts,
126
+ error
127
+ });
128
+ }
129
+ }
130
+ throw new Error(
131
+ `All LLM providers in fallback chain [${chain.join(", ")}] failed. Last error: ${String(lastError)}`
132
+ );
133
+ }
134
+ function withAnthropicCacheControl() {
135
+ return {
136
+ anthropic: { cacheControl: { type: "ephemeral" } }
137
+ };
138
+ }
139
+ function buildSystemWithCache(systemPrompt) {
140
+ return {
141
+ role: "system",
142
+ content: systemPrompt,
143
+ providerOptions: withAnthropicCacheControl()
144
+ };
145
+ }
146
+ var EMBEDDING_CAPABLE = /* @__PURE__ */ new Set([
147
+ "openrouter",
148
+ "openai"
149
+ ]);
150
+ async function buildEmbeddingModel(provider, modelOrPreset) {
151
+ const modelId = resolveModel(modelOrPreset);
152
+ const envKey = ENV_KEY_BY_PROVIDER[provider];
153
+ const apiKey = globalThis.process?.env?.[envKey];
154
+ if (!apiKey) throw new Error(`${envKey} missing`);
155
+ switch (provider) {
156
+ case "openrouter": {
157
+ const { createOpenRouter } = await import("@openrouter/ai-sdk-provider");
158
+ return createOpenRouter({ apiKey }).textEmbeddingModel(modelId);
159
+ }
160
+ case "openai": {
161
+ const { createOpenAI } = await import("@ai-sdk/openai");
162
+ const openaiModelId = modelId.startsWith("openai/") ? modelId.slice("openai/".length) : modelId;
163
+ return createOpenAI({ apiKey }).textEmbeddingModel(openaiModelId);
164
+ }
165
+ case "anthropic": {
166
+ throw new Error("Anthropic does not expose embedding models");
167
+ }
168
+ }
169
+ }
170
+ async function runEmbedWithFallback(invoke, options = {}) {
171
+ const env = getAgentsEnv();
172
+ const rawChain = options.chain ?? env.LLM_EMBEDDING_FALLBACK_CHAIN;
173
+ const capable = rawChain.filter((p) => EMBEDDING_CAPABLE.has(p));
174
+ const chain = options.filterAvailable === false ? capable : capable.filter(hasProviderKey);
175
+ if (chain.length === 0) {
176
+ logger.warn("Embedding fallback chain is empty after filtering", {
177
+ raw: rawChain,
178
+ capable
179
+ });
180
+ throw new Error(
181
+ "No embedding-capable providers available \u2014 set OPENROUTER_API_KEY or OPENAI_API_KEY (Anthropic does not expose embedding models)."
182
+ );
183
+ }
184
+ const model = options.model ?? "embedding";
185
+ let lastError;
186
+ let attempts = 0;
187
+ for (const provider of chain) {
188
+ attempts += 1;
189
+ try {
190
+ const em = await buildEmbeddingModel(provider, model);
191
+ const result = await invoke(em);
192
+ if (attempts > 1) {
193
+ logger.info("Embedding fallback succeeded", { provider, attempts });
194
+ }
195
+ return { result, provider, attempts };
196
+ } catch (error) {
197
+ lastError = error;
198
+ if (!isRetryableError(error)) {
199
+ logger.error("Embedding call failed (non-retryable)", { provider, error });
200
+ throw error;
201
+ }
202
+ logger.warn("Embedding provider failed \u2014 trying next in chain", {
203
+ provider,
204
+ nextIndex: attempts,
205
+ error
206
+ });
207
+ }
208
+ }
209
+ throw new Error(
210
+ `All embedding providers in fallback chain [${chain.join(", ")}] failed. Last error: ${String(lastError)}`
211
+ );
212
+ }
213
+
214
+ export {
215
+ AgentsEnvSchema,
216
+ getAgentsEnv,
217
+ isLangfuseConfigured,
218
+ filterAvailableProviders,
219
+ isRetryableError,
220
+ runWithFallback,
221
+ withAnthropicCacheControl,
222
+ buildSystemWithCache,
223
+ runEmbedWithFallback
224
+ };
@@ -0,0 +1,60 @@
1
+ import {
2
+ getAgentsEnv,
3
+ isLangfuseConfigured
4
+ } from "./chunk-5LX742GP.js";
5
+
6
+ // src/observability.ts
7
+ import { logger } from "@nebutra/logger";
8
+ var _client;
9
+ async function initLangfuse() {
10
+ if (_client !== void 0) return _client;
11
+ if (!isLangfuseConfigured()) {
12
+ _client = null;
13
+ return null;
14
+ }
15
+ try {
16
+ const env = getAgentsEnv();
17
+ const { Langfuse } = await import("langfuse");
18
+ _client = new Langfuse({
19
+ publicKey: env.LANGFUSE_PUBLIC_KEY,
20
+ secretKey: env.LANGFUSE_SECRET_KEY,
21
+ baseUrl: env.LANGFUSE_HOST
22
+ });
23
+ logger.info("Langfuse telemetry enabled", { host: env.LANGFUSE_HOST });
24
+ return _client;
25
+ } catch (error) {
26
+ logger.warn("Failed to initialise Langfuse \u2014 telemetry disabled", { error });
27
+ _client = null;
28
+ return null;
29
+ }
30
+ }
31
+ function buildTelemetryConfig(args) {
32
+ if (!isLangfuseConfigured()) {
33
+ return { isEnabled: false };
34
+ }
35
+ return {
36
+ isEnabled: true,
37
+ functionId: args.functionId,
38
+ metadata: {
39
+ ...args.metadata ?? {},
40
+ // Langfuse picks up these conventional keys from metadata
41
+ ...args.metadata?.tenantId ? { langfuseUserId: args.metadata.tenantId } : {},
42
+ ...args.metadata?.sessionId ? { langfuseSessionId: args.metadata.sessionId } : {}
43
+ }
44
+ };
45
+ }
46
+ async function flushTelemetry() {
47
+ const client = await initLangfuse();
48
+ if (!client) return;
49
+ try {
50
+ await client.flushAsync();
51
+ } catch (error) {
52
+ logger.warn("Langfuse flush failed", { error });
53
+ }
54
+ }
55
+
56
+ export {
57
+ initLangfuse,
58
+ buildTelemetryConfig,
59
+ flushTelemetry
60
+ };
@@ -0,0 +1,82 @@
1
+ import {
2
+ runEmbedWithFallback
3
+ } from "./chunk-5LX742GP.js";
4
+ import {
5
+ createModel
6
+ } from "./chunk-RLWM437Q.js";
7
+ import {
8
+ NebutraAIConfigSchema
9
+ } from "./chunk-NVPE5EDI.js";
10
+
11
+ // src/sdk/index.ts
12
+ import {
13
+ embed as _embed,
14
+ embedMany as _embedMany,
15
+ generateText as _generateText,
16
+ streamText as _streamText
17
+ } from "ai";
18
+ var _resolved = NebutraAIConfigSchema.parse({});
19
+ function configure(config = {}) {
20
+ _resolved = NebutraAIConfigSchema.parse(config);
21
+ }
22
+ function getConfig() {
23
+ return _resolved;
24
+ }
25
+ async function generateText(messages, options = {}) {
26
+ const model = createModel(options.model ?? _resolved.defaultModel, _resolved);
27
+ return await _generateText({
28
+ model,
29
+ messages,
30
+ ...options.system ? { system: options.system } : {},
31
+ temperature: options.temperature ?? _resolved.temperature,
32
+ ...options.maxTokens ? { maxTokens: options.maxTokens } : {},
33
+ ...options.providerOptions ? { providerOptions: { openrouter: options.providerOptions } } : {}
34
+ });
35
+ }
36
+ async function streamText(messages, options = {}) {
37
+ const model = createModel(options.model ?? _resolved.defaultModel, _resolved);
38
+ const userOnFinish = options.onFinish;
39
+ return _streamText({
40
+ model,
41
+ messages,
42
+ ...options.system ? { system: options.system } : {},
43
+ temperature: options.temperature ?? _resolved.temperature,
44
+ ...options.maxTokens ? { maxTokens: options.maxTokens } : {},
45
+ ...options.providerOptions ? { providerOptions: { openrouter: options.providerOptions } } : {},
46
+ ...userOnFinish ? {
47
+ onFinish: ({ text, finishReason, totalUsage }) => {
48
+ const event = {
49
+ text,
50
+ finishReason: String(finishReason),
51
+ usage: {
52
+ inputTokens: totalUsage?.inputTokens,
53
+ outputTokens: totalUsage?.outputTokens,
54
+ totalTokens: totalUsage?.totalTokens
55
+ }
56
+ };
57
+ return userOnFinish(event);
58
+ }
59
+ } : {}
60
+ });
61
+ }
62
+ async function embed(value, options = {}) {
63
+ const { result } = await runEmbedWithFallback(async (model) => _embed({ model, value }), {
64
+ model: options.model ?? "embedding"
65
+ });
66
+ return result;
67
+ }
68
+ async function embedMany(values, options = {}) {
69
+ const { result } = await runEmbedWithFallback(async (model) => _embedMany({ model, values }), {
70
+ model: options.model ?? "embedding"
71
+ });
72
+ return result;
73
+ }
74
+
75
+ export {
76
+ configure,
77
+ getConfig,
78
+ generateText,
79
+ streamText,
80
+ embed,
81
+ embedMany
82
+ };
@@ -0,0 +1,42 @@
1
+ // src/sdk/config.ts
2
+ import { z } from "zod";
3
+ var ProviderType = z.enum(["openrouter", "openai", "siliconflow", "gateway"]);
4
+ var NebutraAIConfigSchema = z.object({
5
+ /** Which provider backend to use. Defaults to "openrouter". */
6
+ provider: ProviderType.default("openrouter"),
7
+ /** API key override. Falls back to env vars per provider. */
8
+ apiKey: z.string().optional(),
9
+ /** Default model identifier. Provider-specific format. */
10
+ defaultModel: z.string().default("anthropic/claude-sonnet-4"),
11
+ /** Default temperature for generations. */
12
+ temperature: z.number().min(0).max(2).default(0.7),
13
+ /** Default max tokens for output. */
14
+ maxTokens: z.number().int().positive().optional(),
15
+ /** Extra headers merged into every request (e.g. HTTP-Referer for OpenRouter). */
16
+ headers: z.record(z.string(), z.string()).optional(),
17
+ /** Extra body fields merged into every request. */
18
+ extraBody: z.record(z.string(), z.unknown()).optional()
19
+ });
20
+ function resolveApiKey(config) {
21
+ if (config.apiKey) return config.apiKey;
22
+ const envMap = {
23
+ openrouter: "OPENROUTER_API_KEY",
24
+ openai: "OPENAI_API_KEY",
25
+ siliconflow: "SILICONFLOW_API_KEY",
26
+ gateway: "VERCEL_OIDC_TOKEN"
27
+ };
28
+ const envVar = envMap[config.provider];
29
+ const value = globalThis.process?.env?.[envVar];
30
+ if (!value) {
31
+ throw new Error(
32
+ `[@nebutra/agents] Missing API key. Set "${envVar}" in environment or pass "apiKey" in config.`
33
+ );
34
+ }
35
+ return value;
36
+ }
37
+
38
+ export {
39
+ ProviderType,
40
+ NebutraAIConfigSchema,
41
+ resolveApiKey
42
+ };