nuabase 2.3.11 → 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/CHANGELOG.md +32 -0
- package/README.md +233 -200
- package/dist/chunks/local-agent-Bj-QKikS.d.cts +270 -0
- package/dist/chunks/local-agent-Bj-QKikS.d.mts +270 -0
- package/dist/cjs/chunks/strict-json-schema-bKwaMWm1.cjs +145 -0
- package/dist/cjs/index.cjs +989 -11504
- package/dist/cjs/local-agent.cjs +819 -877
- package/dist/esm/chunks/strict-json-schema-Du2LLrVP.mjs +138 -0
- package/dist/esm/index.mjs +987 -11502
- package/dist/esm/local-agent.mjs +792 -878
- package/dist/index.d.cts +94 -0
- package/dist/index.d.mts +94 -0
- package/dist/local-agent.d.cts +150 -0
- package/dist/local-agent.d.mts +150 -0
- package/package.json +12 -17
- package/dist/cjs/chunks/strict-json-schema-DUE7RTxG.cjs +0 -177
- package/dist/esm/chunks/strict-json-schema-DI9qiH5h.mjs +0 -174
- package/dist/types/__tests__/helpers/client-factory.d.ts +0 -39
- package/dist/types/__tests__/helpers/index.d.ts +0 -2
- package/dist/types/backend/direct.d.ts +0 -29
- package/dist/types/backend/gateway.d.ts +0 -24
- package/dist/types/backend/types.d.ts +0 -68
- package/dist/types/index.d.cts +0 -7
- package/dist/types/index.d.mts +0 -7
- package/dist/types/lib/auth.d.ts +0 -14
- package/dist/types/lib/env.d.ts +0 -1
- package/dist/types/lib/error-response.d.ts +0 -6
- package/dist/types/lib/error-utils.d.ts +0 -6
- package/dist/types/local-agent.d.cts +0 -2
- package/dist/types/local-agent.d.mts +0 -2
- package/dist/types/nua-llm-core/index.d.ts +0 -23
- package/dist/types/nua-llm-core/lib/error-utils.d.ts +0 -6
- package/dist/types/nua-llm-core/lib/logger.d.ts +0 -44
- package/dist/types/nua-llm-core/lib/nua-errors.d.ts +0 -12
- package/dist/types/nua-llm-core/lib/prompt-template-utils.d.ts +0 -2
- package/dist/types/nua-llm-core/lib/schema-utils.d.ts +0 -1
- package/dist/types/nua-llm-core/lib/stable-stringify.d.ts +0 -1
- package/dist/types/nua-llm-core/lib/strict-json-schema.d.ts +0 -7
- package/dist/types/nua-llm-core/lib/types.d.ts +0 -8
- package/dist/types/nua-llm-core/lib/validate-mappable-input-data.d.ts +0 -4
- package/dist/types/nua-llm-core/local-agent.d.ts +0 -7
- package/dist/types/nua-llm-core/modules/agent/agent-loop.d.ts +0 -10
- package/dist/types/nua-llm-core/modules/agent/agent-turn.d.ts +0 -17
- package/dist/types/nua-llm-core/modules/agent/provider-formatters.d.ts +0 -22
- package/dist/types/nua-llm-core/modules/agent/run-agent.d.ts +0 -7
- package/dist/types/nua-llm-core/modules/agent/types.d.ts +0 -90
- package/dist/types/nua-llm-core/modules/cast/cast-request.d.ts +0 -32
- package/dist/types/nua-llm-core/modules/cast/envelope.d.ts +0 -9
- package/dist/types/nua-llm-core/modules/cast/list-schema.d.ts +0 -32
- package/dist/types/nua-llm-core/modules/cast/prompts/list-prompt.d.ts +0 -3
- package/dist/types/nua-llm-core/modules/cast/prompts/render-prompt.d.ts +0 -7
- package/dist/types/nua-llm-core/modules/cast/prompts/value-prompt.d.ts +0 -3
- package/dist/types/nua-llm-core/modules/cast/run-cast.d.ts +0 -44
- package/dist/types/nua-llm-core/modules/engine/engine-router.d.ts +0 -23
- package/dist/types/nua-llm-core/modules/engine/http/http-engine.d.ts +0 -13
- package/dist/types/nua-llm-core/modules/engine/http/provider-call.d.ts +0 -33
- package/dist/types/nua-llm-core/modules/engine/http/provider-config.d.ts +0 -70
- package/dist/types/nua-llm-core/modules/engine/http/provider-engines.d.ts +0 -35
- package/dist/types/nua-llm-core/modules/engine/llm-engine.d.ts +0 -73
- package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/claude-code.d.ts +0 -2
- package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/codex.d.ts +0 -2
- package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/failure-classification.d.ts +0 -4
- package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/index.d.ts +0 -3
- package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/pi.d.ts +0 -2
- package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/types.d.ts +0 -61
- package/dist/types/nua-llm-core/modules/engine/local-agent/agent-id.d.ts +0 -4
- package/dist/types/nua-llm-core/modules/engine/local-agent/create-local-agent.d.ts +0 -36
- package/dist/types/nua-llm-core/modules/engine/local-agent/detect.d.ts +0 -20
- package/dist/types/nua-llm-core/modules/engine/local-agent/local-agent-engine.d.ts +0 -32
- package/dist/types/nua-llm-core/modules/engine/local-agent/local-agent.d.ts +0 -25
- package/dist/types/nua-llm-core/modules/engine/local-agent/process-runner.d.ts +0 -16
- package/dist/types/nua-llm-core/modules/engine/local-agent/semaphore.d.ts +0 -8
- package/dist/types/nua-llm-core/modules/execution/llm-response-extraction.d.ts +0 -5
- package/dist/types/nua-llm-core/modules/json-schema-validation/ajv-instances.d.ts +0 -12
- package/dist/types/nua-llm-core/modules/json-schema-validation/nua-json-schema-value-validation.d.ts +0 -10
- package/dist/types/nua-llm-core/modules/json-schema-validation/validate-json-schema.d.ts +0 -2
- package/dist/types/nua-llm-core/modules/model-info/index.d.ts +0 -15
- package/dist/types/nua-llm-core/modules/streaming/sse-parser.d.ts +0 -7
- package/dist/types/nua-llm-core/nua-llm-client.d.ts +0 -37
- package/dist/types/nua.d.ts +0 -42
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# nuabase
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- be95150: `nuabase` now only runs casts in your process, on LLM providers' APIs with your keys (`Nua.direct({ providers })`) or on a coding agent on your machine (`Nua.direct({ localAgent })`).
|
|
8
|
+
|
|
9
|
+
Removed: `Nua.gateway()`, `nua.queueGet()`, `nua.queueList()`, and the `GatewayConfig`, `GatewayMeta` and `QueueResult` types. A result's `source` is now always `'direct'`.
|
|
10
|
+
|
|
11
|
+
Also removed: the `LlmBackend`, `CastValueParams` and `CastArrayParams` types. `Nua`'s constructor is private, so nothing could implement or pass them.
|
|
12
|
+
|
|
13
|
+
`nuabase` now needs Node.js 18 or later (it said `>=16.6.0`), for the global `fetch` it calls LLM providers with.
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- 9ed74c9: `nuabase/local-agent` can now start an interactive coding-agent session in the user's project, for the user to carry on in a terminal: `startAgentSession()` writes the first message and a shell launcher that starts Claude Code, Codex or Pi on it (in the agent's auto mode with `approval: 'auto'`), and on macOS opens it in a new terminal window. It answers with what it did: `opened`, `command` (the user runs it in a terminal), `open_failed` (the files are written but the window didn't open, with the error) or `refused` with the reason. `agentSessionMode()` says beforehand which of these the platform allows.
|
|
18
|
+
- 61eaba5: A failed cast now says why on its own: `reason` is the last attempt's failure in the engine's own words (a coding agent's or provider's message, fit to show a user), and `attempts` is how many were made (0 when the request was refused before any). `error` stays the one-line summary for logs. Coding-agent failures no longer prefix the agent's name (`claude: `) to its message, and Codex's API errors are reported by their message instead of as JSON.
|
|
19
|
+
- 5028584: DeepSeek is now a direct provider: pass `providers: { deepseek: { apiKey } }` and name a DeepSeek model such as `deepseek-flash` or `deepseek-v4-pro`. DeepSeek requests run with thinking turned off, and schemas are described in the prompt because DeepSeek has no strict structured outputs. A new `deepseek` alias uses DeepSeek V4 Pro, directly or through OpenRouter.
|
|
20
|
+
|
|
21
|
+
The model aliases now point at current models: `gemini` → Gemini 3.8 Flash, `qwen` → Qwen3.8 2.4T A95B, `kimi` → Kimi K3, `sonnet` → Claude Sonnet 5.5, `opus` → Claude Opus 5.5, `gpt` → GPT-6.1 Sol, and the OpenRouter fallback for `fast` → GLM-5.3 Flash.
|
|
22
|
+
- 25569d9: Local agents take an `effort` option (`'low' | 'medium' | 'high'`), passed to the CLI's own setting: Claude Code `--effort`, Codex `model_reasoning_effort`, Pi `--thinking`. Left out, the CLI's default applies. Codex casts no longer run at low reasoning effort unless you ask for it with `effort: 'low'`.
|
|
23
|
+
- 25569d9: `LocalAgent.with({ model, effort, timeoutMs })` gives the same agent with other call options, sharing its executable, environment, logger and `concurrency` limit. One `localAgent()` per CLI can now serve casts on several of its models, such as Codex models that have no alias, without each getting its own limit on processes running at once.
|
|
24
|
+
- 449e580: `usage` now reports `promptCacheReadTokens` and `promptCacheWriteTokens` when the provider does: the prompt tokens it read from or wrote to its prompt cache. They are part of `promptTokens`. Reported for OpenRouter, DeepSeek and Gemini, and for the Claude Code, Codex and Pi local agents.
|
|
25
|
+
- 3abfeee: Casts and agents now make use of provider prompt caching. Claude on OpenRouter caches the prompt, instructions and schema of a cast, so repeat calls of the same cast read them at about a tenth of the price, and agent turns read the conversation the previous turn sent. Casts through OpenRouter stay on one upstream provider, so models that cache automatically (DeepSeek, GLM, Kimi, GPT) reuse it too.
|
|
26
|
+
- 4f38416: Results are now parsed with your Zod schema instead of being checked against its JSON Schema with AJV. `data` is Zod's parsed output: `.default()`s are filled in, `.transform()`s run, `.refine()` checks apply (a failing answer is retried), and unknown object keys are dropped. The JSON Schema shown to the model now describes the schema's input side, so schemas with `.transform()` no longer throw. AJV is no longer bundled.
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- b9b5da9: Type declarations now resolve under every module resolution, including `node16`/`nodenext` from ES modules and CommonJS, where nuabase's exports used to type as `any`. Each entry point ships one self-contained `.d.mts` and `.d.cts`, bundled by the same build as the JavaScript. `LlmProviderId` and `Logger` are now exported. The ES module build imports Node's built-ins directly instead of through `createRequire`.
|
|
31
|
+
- dec76aa: Schemas with `$ref`s, such as those using `.meta({ id })` or recursive schemas, now work with native schema enforcement. The schema sent to the engine had refs that pointed nowhere, so engines either rejected it or did not enforce it.
|
|
32
|
+
- 267e07a: Tool-calling agents on Gemini 3 models now work: Gemini's thought signatures are sent back with each function call, which Gemini 3 and later require.
|
|
33
|
+
- 3bbdc4c: `list()` rows are typed by the output's name when it is a literal: with `output: { name: 'account', schema: z.string() }`, each row is `{ id: string | number; account: string }` instead of a row whose every field has the output's type.
|
|
34
|
+
|
|
3
35
|
## 2.3.11
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,76 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
# Nuabase TypeScript SDK
|
|
2
2
|
|
|
3
|
-
Nuabase turns LLM prompts into type-safe functions
|
|
3
|
+
Nuabase turns LLM prompts and Zod schemas into type-safe functions. Pass a prompt, input data, and a Zod schema; receive validated, typed data back.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Every call runs directly in your process—either against the LLM provider API you choose, using your API keys, or via a local coding agent CLI already installed on your machine (`claude`, `codex`, or `pi`).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Common use cases:**
|
|
8
|
+
- Extract structured fields from free-form text to auto-fill complex forms
|
|
9
|
+
- Enrich arrays of items (e.g., adding nutrition facts to a list of foods)
|
|
10
|
+
- Classify and tag sales leads with company metadata
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
- Enrich a list of food items with nutrition facts
|
|
11
|
-
- Tag sales leads with more details about the company
|
|
12
|
-
|
|
13
|
-
Behind the scenes, Nuabase runs the LLM transformation as async jobs, and once ready, call your server API with the results using Webhooks. It can also stream the result directly to your front-end using SSE. You can also see all requests and responses as they happen in the [Nuabase Console](https://console.nuabase.com). All outputs are guaranteed to match the schema you specified.
|
|
14
|
-
|
|
15
|
-
One major reason I built Nuabase is the need for granular, row-level caching. For example, let's say you want to classify your bank transaction entries, and map them to your chart of accounts. With Nuabase's row-level caching, it will only send new entries to the LLM. Any specific entry that it has seen before will be returned from the cache. This also means you can make LLM requests multiple times with identical values, and after the first time, they will return immediately without needing to go through the LLM.
|
|
16
|
-
|
|
17
|
-
## Usage at a glance
|
|
18
|
-
|
|
19
|
-
**1. Input.** Start with the data you want to send to the LLM:
|
|
20
|
-
|
|
21
|
-
```ts
|
|
22
|
-
const leads = [{ id: 'lead-101', notes: 'Growth-stage SaaS, ~80 employees, wants a demo.' }];
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**2. Desired shape.** Describe the structure you expect back:
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
const LeadInsights = z.object({
|
|
29
|
-
industry: z.string(),
|
|
30
|
-
company_size: z.enum(['SMB', 'Mid-market', 'Enterprise']),
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**3. Call the API.** Use `list()` for array operations:
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
const nua = Nua.gateway({ apiKey: 'YOUR_API_KEY' });
|
|
38
|
-
|
|
39
|
-
const result = await nua.list('Classify each lead with industry and company_size bucket.', {
|
|
40
|
-
input: leads,
|
|
41
|
-
primaryKey: 'id',
|
|
42
|
-
output: { name: 'leadInsights', schema: LeadInsights },
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**4. Use the result.** Check for success and access typed data:
|
|
47
|
-
|
|
48
|
-
```ts
|
|
49
|
-
if (!result.success) throw new Error(result.error);
|
|
50
|
-
console.log(result.data[0].leadInsights);
|
|
51
|
-
// -> { industry: 'Software', company_size: 'Mid-market' }
|
|
52
|
-
```
|
|
12
|
+
---
|
|
53
13
|
|
|
54
14
|
## Quick Start
|
|
55
15
|
|
|
56
|
-
1.
|
|
16
|
+
### 1. Install
|
|
57
17
|
|
|
58
18
|
```bash
|
|
59
19
|
npm install nuabase zod
|
|
60
20
|
```
|
|
61
21
|
|
|
62
|
-
2.
|
|
63
|
-
|
|
64
|
-
3. Set `NUABASE_API_KEY` environment variable, or pass it directly in `Nua.gateway({ apiKey: "KEY" })`:
|
|
22
|
+
### 2. Initialize and Call
|
|
65
23
|
|
|
66
|
-
|
|
24
|
+
Configure `Nua.direct()` with an LLM provider API key (or a [local agent](#2-local-coding-agent-nodejs-only)), define your expected Zod schema, and call `get()` or `list()`:
|
|
67
25
|
|
|
68
26
|
```ts
|
|
69
27
|
import { Nua } from 'nuabase';
|
|
70
28
|
import { z } from 'zod';
|
|
71
29
|
|
|
72
|
-
|
|
30
|
+
// 1. Initialize client
|
|
31
|
+
const nua = Nua.direct({
|
|
32
|
+
providers: { openrouter: { apiKey: process.env.OPENROUTER_API_KEY! } },
|
|
33
|
+
});
|
|
73
34
|
|
|
35
|
+
// 2. Define output schema
|
|
74
36
|
const LeadInsights = z.object({
|
|
75
37
|
company_name: z.string(),
|
|
76
38
|
industry: z.string(),
|
|
@@ -78,7 +40,8 @@ const LeadInsights = z.object({
|
|
|
78
40
|
recommended_follow_up: z.enum(['Call', 'Email', 'Event']),
|
|
79
41
|
});
|
|
80
42
|
|
|
81
|
-
|
|
43
|
+
// 3. Prepare input data
|
|
44
|
+
const leads = [
|
|
82
45
|
{
|
|
83
46
|
leadId: 'lead-101',
|
|
84
47
|
name: 'Acme Analytics',
|
|
@@ -89,34 +52,24 @@ const rows = [
|
|
|
89
52
|
name: 'Bright Logistics',
|
|
90
53
|
notes: 'Regional freight operator asking for pricing via email. 12 depots across the Midwest.',
|
|
91
54
|
},
|
|
92
|
-
{
|
|
93
|
-
leadId: 'lead-103',
|
|
94
|
-
name: 'Nimbus Retail',
|
|
95
|
-
notes:
|
|
96
|
-
'Enterprise retailer. Mentioned they will send the procurement team to our booth at NRF.',
|
|
97
|
-
},
|
|
98
55
|
];
|
|
99
56
|
|
|
100
|
-
|
|
101
|
-
|
|
57
|
+
// 4. Execute batch request (sends all rows in a single LLM request)
|
|
58
|
+
const result = await nua.list(
|
|
59
|
+
'Extract company_name, industry, company_size, and recommended_follow_up from each lead.',
|
|
102
60
|
{
|
|
103
|
-
input:
|
|
61
|
+
input: leads,
|
|
104
62
|
primaryKey: 'leadId',
|
|
105
63
|
output: { name: 'leadInsights', schema: LeadInsights },
|
|
106
64
|
}
|
|
107
65
|
);
|
|
108
66
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
67
|
+
// 5. Access typed output
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
throw new Error(result.error);
|
|
112
70
|
}
|
|
113
71
|
|
|
114
|
-
|
|
115
|
-
// leadInsights satisfies the LeadInsights schema
|
|
116
|
-
console.log(leadId, leadInsights.company_name, leadInsights.industry, leadInsights.company_size);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
console.log(response.data);
|
|
72
|
+
console.log(result.data);
|
|
120
73
|
// -> [
|
|
121
74
|
// {
|
|
122
75
|
// leadId: 'lead-101',
|
|
@@ -128,200 +81,280 @@ console.log(response.data);
|
|
|
128
81
|
// },
|
|
129
82
|
// },
|
|
130
83
|
// // ...
|
|
131
|
-
// ]
|
|
84
|
+
// ]
|
|
132
85
|
```
|
|
133
86
|
|
|
134
|
-
|
|
87
|
+
---
|
|
135
88
|
|
|
136
|
-
##
|
|
89
|
+
## Core Methods
|
|
137
90
|
|
|
138
|
-
|
|
139
|
-
- Zero glue code: Nuabase runs the queueing, retries, webhooks, SSE streaming, and polling so your application code stays clean.
|
|
140
|
-
- Built-in performance: granular caching, cost attribution, and usage metrics come for free on every request.
|
|
141
|
-
- Production visibility: every run shows up in the Nuabase dashboard with logs, prompt/response history, and tracing ids.
|
|
91
|
+
### `nua.get(prompt, options?)`
|
|
142
92
|
|
|
143
|
-
|
|
93
|
+
Generates a single value. Omit `options` to receive a plain `string`, or pass an `output` schema for a typed object or primitive.
|
|
144
94
|
|
|
145
|
-
|
|
95
|
+
```ts
|
|
96
|
+
// Plain string response
|
|
97
|
+
const joke = await nua.get('Tell me a one-line joke');
|
|
98
|
+
if (joke.success) console.log(joke.data); // string
|
|
146
99
|
|
|
147
|
-
|
|
100
|
+
// Typed structured response
|
|
101
|
+
const address = await nua.get('Extract the postal address', {
|
|
102
|
+
input: 'Ship to 123 Main St, Springfield, IL 62701',
|
|
103
|
+
output: { name: 'address', schema: AddressSchema },
|
|
104
|
+
});
|
|
105
|
+
if (address.success) console.log(address.data); // z.infer<typeof AddressSchema>
|
|
106
|
+
```
|
|
148
107
|
|
|
149
|
-
|
|
150
|
-
| ----------- | --------------------------------------------------------------------- | -------------------------------------------------- |
|
|
151
|
-
| `success` | `true` | Indicates that the request completed successfully. |
|
|
152
|
-
| `data` | `T` (for `get`) or `Array<{ [pk]: value; [output]: T }>` (for `list`) | The typed result matching your schema. |
|
|
153
|
-
| `usage` | `{ promptTokens, completionTokens, totalTokens }` | Token usage statistics for the LLM request. |
|
|
154
|
-
| `model` | `string` | The LLM model used for this request. |
|
|
155
|
-
| `latencyMs` | `number` | Request latency in milliseconds. |
|
|
156
|
-
| `source` | `'gateway'` \| `'direct'` | Which backend processed the request. |
|
|
157
|
-
| `meta` | `object` | Additional metadata (varies by source). |
|
|
108
|
+
**Options:**
|
|
158
109
|
|
|
159
|
-
|
|
110
|
+
* `input?: unknown` – Context or data to include with the prompt.
|
|
111
|
+
* `output: { name: string; schema: ZodSchema }` – Identifier and Zod schema for the output.
|
|
112
|
+
* `model?: ModelInput` – Override the client's default model for this call.
|
|
160
113
|
|
|
161
|
-
|
|
162
|
-
| ------------ | --------- | ----------------------------------------- |
|
|
163
|
-
| `requestId` | `string` | Unique identifier for tracing. |
|
|
164
|
-
| `cached` | `boolean` | Whether the result was served from cache. |
|
|
165
|
-
| `llmUsage` | `object` | Token usage from LLM calls. |
|
|
166
|
-
| `cacheUsage` | `object` | Token usage served from cache. |
|
|
114
|
+
### `nua.list(prompt, options)`
|
|
167
115
|
|
|
168
|
-
|
|
116
|
+
Processes an array of records in a **single request**. Each returned row carries the `primaryKey` value the model echoed back, next to the output. Rows can come back in a different order than the input, so match them to your input by key.
|
|
169
117
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
| `costUsdEstimate` | `number \| undefined` | `engine: 'local-agent'` only. The agent's own estimate. On a subscription this is not what you pay. |
|
|
118
|
+
```ts
|
|
119
|
+
const result = await nua.list('Classify each lead', {
|
|
120
|
+
input: leads,
|
|
121
|
+
primaryKey: 'id',
|
|
122
|
+
output: { name: 'classification', schema: ClassificationSchema },
|
|
123
|
+
});
|
|
177
124
|
|
|
178
|
-
|
|
125
|
+
if (result.success) {
|
|
126
|
+
result.data.forEach((row) => console.log(row.id, row.classification));
|
|
127
|
+
}
|
|
128
|
+
```
|
|
179
129
|
|
|
180
|
-
|
|
181
|
-
| ----------- | ------------------------- | ----------------------------------- |
|
|
182
|
-
| `success` | `false` | Indicates the request failed. |
|
|
183
|
-
| `error` | `string` | Message describing the error. |
|
|
184
|
-
| `source` | `'gateway'` \| `'direct'` | Which backend returned the error. |
|
|
185
|
-
| `latencyMs` | `number` | Time until failure in milliseconds. |
|
|
130
|
+
**Options:**
|
|
186
131
|
|
|
187
|
-
|
|
132
|
+
* `input: Array<Record<string, unknown>>` – Array of objects to process.
|
|
133
|
+
* `primaryKey: string` – Unique key on each input object. Each output row carries it, so you can match rows to inputs.
|
|
134
|
+
* `output: { name: string; schema: ZodSchema }` – Property name and Zod schema attached to each returned row.
|
|
135
|
+
* `model?: ModelInput` – Override the client's default model for this call.
|
|
188
136
|
|
|
189
|
-
|
|
137
|
+
---
|
|
190
138
|
|
|
191
|
-
|
|
192
|
-
Creates a client that connects to the Nuabase API gateway. Use this for production deployments with caching, request tracking, and async job support.
|
|
139
|
+
## Client Configuration (`Nua.direct`)
|
|
193
140
|
|
|
194
|
-
-
|
|
195
|
-
- `baseUrl?: string` – Override the API host. Defaults to `https://api.nuabase.com`.
|
|
141
|
+
`Nua.direct(config)` runs in-process using one of two execution engines: **HTTP Providers** or a **Local Coding Agent**.
|
|
196
142
|
|
|
197
|
-
|
|
198
|
-
Creates a client that runs casts in this process instead of through the gateway. Use this for serverless, edge functions, or CLI tools where you don't need the gateway infrastructure. It runs on one of two kinds of engine, chosen by the config.
|
|
143
|
+
### 1. HTTP Providers
|
|
199
144
|
|
|
200
|
-
|
|
145
|
+
Call LLM APIs directly using your own API keys:
|
|
201
146
|
|
|
202
|
-
|
|
203
|
-
|
|
147
|
+
```ts
|
|
148
|
+
const nua = Nua.direct({
|
|
149
|
+
providers: {
|
|
150
|
+
groq: { apiKey: '...' },
|
|
151
|
+
cerebras: { apiKey: '...' },
|
|
152
|
+
gemini: { apiKey: '...' },
|
|
153
|
+
openrouter: { apiKey: '...' },
|
|
154
|
+
deepseek: { apiKey: '...' },
|
|
155
|
+
},
|
|
156
|
+
model: { alias: 'fast' }, // optional default; defaults to { alias: 'fast' }
|
|
157
|
+
});
|
|
158
|
+
```
|
|
204
159
|
|
|
205
|
-
|
|
160
|
+
**Specifying Models:**
|
|
161
|
+
Models can be set globally on the client or per-call using either a provider-specific model object or an alias. Each alias lists provider models in a fixed order and uses the first one whose provider you supplied an API key for: `{ alias: 'fast' }` tries Cerebras, then Groq, then OpenRouter. If you have a key for none of an alias's providers, the call fails. Some aliases, such as `sonnet`, only go through OpenRouter.
|
|
206
162
|
|
|
207
163
|
```ts
|
|
208
|
-
|
|
164
|
+
// Specific provider and model
|
|
165
|
+
await nua.get('Extract company name', {
|
|
209
166
|
output: { name: 'companyName', schema: z.string() },
|
|
210
|
-
model: { provider: 'openrouter', model: 'z-ai/glm-5.
|
|
167
|
+
model: { provider: 'openrouter', model: 'z-ai/glm-5.3-flash' },
|
|
211
168
|
});
|
|
212
169
|
|
|
213
|
-
|
|
170
|
+
// Provider-agnostic alias
|
|
171
|
+
await nua.get('Extract company name', {
|
|
214
172
|
output: { name: 'companyName', schema: z.string() },
|
|
215
173
|
model: { alias: 'fast' },
|
|
216
174
|
});
|
|
217
175
|
```
|
|
218
176
|
|
|
219
|
-
|
|
177
|
+
### 2. Local Coding Agent (Node.js only)
|
|
178
|
+
|
|
179
|
+
Run prompts through a coding agent CLI already installed and logged in on your machine (`claude -p`, `codex exec`, or `pi --mode json`). This uses your existing CLI login or subscription—no API key required.
|
|
220
180
|
|
|
221
|
-
|
|
181
|
+
Import helpers from `nuabase/local-agent` (kept at a separate entry point so the main `nuabase` entry point stays free of Node built-ins, which edge runtimes such as Cloudflare Workers or Vercel Edge lack):
|
|
222
182
|
|
|
223
183
|
```ts
|
|
224
184
|
import { Nua } from 'nuabase';
|
|
225
|
-
import {
|
|
185
|
+
import { localAgent, findLocalAgent, detectLocalAgents } from 'nuabase/local-agent';
|
|
226
186
|
|
|
227
|
-
|
|
187
|
+
// Option A: Target a specific agent
|
|
188
|
+
const nua = Nua.direct({
|
|
189
|
+
localAgent: localAgent({ agent: 'claude-code', model: 'haiku' }),
|
|
190
|
+
});
|
|
228
191
|
|
|
229
|
-
//
|
|
230
|
-
const
|
|
192
|
+
// Option B: Automatically pick the first installed & logged-in agent (checks Claude Code first)
|
|
193
|
+
const autoNua = Nua.direct({
|
|
194
|
+
localAgent: await findLocalAgent(),
|
|
195
|
+
});
|
|
231
196
|
|
|
197
|
+
// Optional: Inspect available agents on the machine (makes no model calls)
|
|
232
198
|
const agents = await detectLocalAgents();
|
|
233
|
-
// [{ agent: 'claude-code', installed: true, loggedIn: true, version: '...', binaryPath: '...' }, ...]
|
|
199
|
+
// -> [{ agent: 'claude-code', installed: true, loggedIn: true, version: '...', binaryPath: '...' }, ...]
|
|
234
200
|
```
|
|
235
201
|
|
|
236
|
-
|
|
202
|
+
**Agent Functions & Options:**
|
|
237
203
|
|
|
238
|
-
|
|
204
|
+
* `localAgent(config)` – Configures a named agent (`'claude-code' | 'codex' | 'pi'`). Throws immediately if the executable isn't found; fails on the first call if not logged in. Note: Agent lookup happens once at creation.
|
|
205
|
+
* `findLocalAgent(options?)` – Checks version and login status across supported CLIs (without making model calls) and resolves to the first ready agent, or rejects if none are available.
|
|
239
206
|
|
|
240
|
-
|
|
207
|
+
| Option | Type | Default | Description |
|
|
208
|
+
| --- | --- | --- | --- |
|
|
209
|
+
| `agent` | `'claude-code' \| 'codex' \| 'pi'` | Required for `localAgent()` | Which CLI agent to run. |
|
|
210
|
+
| `binaryPath` | `string` | Looked up on `PATH` | Path to executable (`localAgent` only). On Windows, point to a native executable (`.cmd` shims are not supported). |
|
|
211
|
+
| `model` | `string` | Agent's default | The agent's own model name for calls that name no model. Claude Code accepts aliases (`'haiku'`, `'sonnet'`, `'opus'`), and so does a per-call `model: { alias }`. Codex and Pi have no aliases: Codex uses its own model names; Pi uses `'provider/id'` (e.g., `'openrouter/z-ai/glm-5.3-flash'`). |
|
|
212
|
+
| `effort` | `'low' \| 'medium' \| 'high'` | Agent's default | How hard the model thinks, as the CLI's own setting: Claude Code `--effort`, Codex `model_reasoning_effort`, Pi `--thinking`. |
|
|
213
|
+
| `timeoutMs` | `number` | `180000` | Maximum execution time per call in milliseconds. |
|
|
214
|
+
| `concurrency` | `number` | `2` | Maximum concurrent agent processes. |
|
|
215
|
+
| `auth` | `'subscription' \| 'inherit'` | `'subscription'` | `'subscription'` strips env vars like `ANTHROPIC_API_KEY` and `OPENAI_API_KEY` so calls bill your logged-in plan instead of API keys. |
|
|
216
|
+
| `logger` | object | `console` | Object with `debug`, `info`, `warn`, and `error` methods for call and retry logs. |
|
|
241
217
|
|
|
242
|
-
|
|
243
|
-
- `binaryPath?: string` – Path to the agent executable. Defaults to looking it up on `PATH`. On Windows, point this at a native executable; `.cmd` shims are not supported.
|
|
244
|
-
- and the options below.
|
|
218
|
+
`agent.with({ model, effort, timeoutMs })` returns the same agent with other call options. It shares the agent's executable, environment, logger and `concurrency` limit, so one `localAgent()` per CLI can serve casts on several of its models without running more processes at once than the limit allows. Options left out keep the agent's; a `model` or `effort` given as `undefined` is the CLI's default.
|
|
245
219
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
- `concurrency?: number` – Maximum agent processes at once. Defaults to `2`.
|
|
253
|
-
- `auth?: 'subscription' | 'inherit'` – `subscription` (default) hides `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` and similar variables from the agent so it bills the logged-in plan rather than an API key.
|
|
254
|
-
- `logger?` – An object with `debug`, `info`, `warn` and `error` methods, where calls and retries are logged. Defaults to the console.
|
|
220
|
+
```ts
|
|
221
|
+
const codex = localAgent({ agent: 'codex', concurrency: 2 });
|
|
222
|
+
const quick = Nua.direct({ localAgent: codex.with({ model: 'gpt-5.6-terra', effort: 'low' }) });
|
|
223
|
+
const careful = Nua.direct({ localAgent: codex.with({ model: 'gpt-5.6-sol', timeoutMs: 300_000 }) });
|
|
224
|
+
// Both run on the same two processes at most.
|
|
225
|
+
```
|
|
255
226
|
|
|
256
|
-
|
|
227
|
+
> **Note:** Local agent calls count against your personal subscription limits. Check each vendor's terms before exposing local-agent execution to end users.
|
|
257
228
|
|
|
258
|
-
###
|
|
229
|
+
### Interactive Agent Sessions (Node.js only)
|
|
259
230
|
|
|
260
|
-
|
|
261
|
-
Returns a simple string response from the LLM.
|
|
231
|
+
Hand a prompt to the user's coding agent as an interactive session they carry on in a terminal, in their own project (unlike casts, which run in an empty sandbox):
|
|
262
232
|
|
|
263
233
|
```ts
|
|
264
|
-
|
|
265
|
-
|
|
234
|
+
import { startAgentSession } from 'nuabase/local-agent';
|
|
235
|
+
|
|
236
|
+
const session = await startAgentSession({
|
|
237
|
+
agent: 'claude-code',
|
|
238
|
+
cwd: '/path/to/project',
|
|
239
|
+
model: 'opus',
|
|
240
|
+
approval: 'auto',
|
|
241
|
+
prompt: 'Fix the failing import described in notes.md',
|
|
242
|
+
dir: '/path/to/project/tmp/agent-sessions',
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
switch (session.kind) {
|
|
246
|
+
case 'opened': // macOS: a new terminal window is running the agent
|
|
247
|
+
break;
|
|
248
|
+
case 'command': // elsewhere: the user starts it
|
|
249
|
+
console.log(`Run in a terminal: ${session.files.command}`);
|
|
250
|
+
break;
|
|
251
|
+
case 'open_failed': // macOS, but the window didn't open (session.error); the files are written
|
|
252
|
+
console.log(`Couldn't open a terminal (${session.error}). Run: ${session.files.command}`);
|
|
253
|
+
break;
|
|
254
|
+
case 'refused': // 'unsupported_platform' | 'prompt_too_long' | 'agent_not_found'; nothing written
|
|
255
|
+
console.error(session.message);
|
|
256
|
+
}
|
|
266
257
|
```
|
|
267
258
|
|
|
268
|
-
|
|
269
|
-
Returns a typed single value. Parameters:
|
|
259
|
+
It writes the prompt to its own file (`0600`) and a launcher (`0700`) that passes it to the agent as one argument after `--`. Quotes, backticks and `$NAMES` in the prompt reach the agent as written, and the prompt can't set the agent's flags or run commands. Neither file is ever overwritten or deleted. On macOS the launcher opens in a new window of the user's terminal app (`open`, which needs no Automation permission).
|
|
270
260
|
|
|
271
|
-
|
|
272
|
-
- `options.input?: unknown` – Optional input data to include in the prompt.
|
|
273
|
-
- `options.output: { name: string; schema: ZodSchema }` – Output name and Zod schema.
|
|
261
|
+
Expected failures come back as `kind: 'refused'`, with nothing written: Windows, which can't run the launcher (`unsupported_platform`); a prompt of 128 KiB or more on Linux, which caps a single argument at that size (`prompt_too_long`); and an agent that isn't on `PATH` when no `binaryPath` is given (`agent_not_found`). It throws only for an unknown `agent` or a `name` already used.
|
|
274
262
|
|
|
275
|
-
|
|
276
|
-
const result = await nua.get('Extract the address', {
|
|
277
|
-
input: '123 Main St, Springfield, IL 62701',
|
|
278
|
-
output: { name: 'address', schema: AddressSchema },
|
|
279
|
-
});
|
|
280
|
-
if (result.success) console.log(result.data); // typed as z.infer<typeof AddressSchema>
|
|
281
|
-
```
|
|
263
|
+
`agentSessionMode(platform?)` says beforehand how a session would start, for showing the right button: `'terminal'` on macOS, `'command'` on other POSIX systems, `'none'` on Windows.
|
|
282
264
|
|
|
283
|
-
|
|
284
|
-
|
|
265
|
+
| Option | Type | Default | Description |
|
|
266
|
+
| --- | --- | --- | --- |
|
|
267
|
+
| `agent` | `'claude-code' \| 'codex' \| 'pi'` | Required | Which CLI agent to start. |
|
|
268
|
+
| `binaryPath` | `string` | Looked up on `PATH` | Path to the executable. The launcher runs it by this absolute path, whatever the terminal's `PATH`. |
|
|
269
|
+
| `cwd` | `string` | Required | The project the agent works in. It loads the project's `AGENTS.md` / `CLAUDE.md`. |
|
|
270
|
+
| `model` | `string` | Agent's default | The agent's own model name. |
|
|
271
|
+
| `approval` | `'ask' \| 'auto'` | `'ask'` | `'auto'` starts the agent's auto mode (Claude Code `--permission-mode auto`, Codex `--approve-for-me`, Pi `--approve`). Its reviewer, not the user, approves routine edits and commands, and stops risky ones. |
|
|
272
|
+
| `prompt` | `string` | Required | The session's first message. |
|
|
273
|
+
| `dir` | `string` | Required | Where both files are written. Created if missing. |
|
|
274
|
+
| `name` | `string` | `<UTC time>-<agent>-<4 hex digits>` | The files' name without extension. Neither file may exist yet. |
|
|
275
|
+
| `platform` | `NodeJS.Platform` | `process.platform` | The platform the launcher is for. |
|
|
285
276
|
|
|
286
|
-
|
|
287
|
-
- `options.input: Array<Record<string, unknown>>` – Rows to process.
|
|
288
|
-
- `options.primaryKey: string` – Field that uniquely identifies each row.
|
|
289
|
-
- `options.output: { name: string; schema: ZodSchema }` – Output configuration.
|
|
277
|
+
---
|
|
290
278
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
279
|
+
## Response Format & Error Handling
|
|
280
|
+
|
|
281
|
+
Every `get()` and `list()` call returns a discriminated union (`result.success`). Every failure of the LLM call itself comes back as `success: false`.
|
|
282
|
+
|
|
283
|
+
The call rejects (throws) only for invalid arguments, before anything is sent: an empty prompt, a non-array `input`, an empty `primaryKey`, or a Zod schema that JSON Schema can't express (such as `z.date()`).
|
|
284
|
+
|
|
285
|
+
### Success (`result.success === true`)
|
|
286
|
+
|
|
287
|
+
| Field | Type | Description |
|
|
288
|
+
| --- | --- | --- |
|
|
289
|
+
| `success` | `true` | Request succeeded and passed schema validation. |
|
|
290
|
+
| `data` | `T` or `Array<{ [pk]: K } & { [output]: T }>` | Validated output matching your schema. |
|
|
291
|
+
| `usage` | `{ promptTokens, completionTokens, totalTokens, promptCacheReadTokens?, promptCacheWriteTokens? }` | Token usage stats (`promptCache*` included when reported by the provider as subsets of `promptTokens`). |
|
|
292
|
+
| `model` | `string` | What answered: `provider:model` (e.g., `openrouter:z-ai/glm-5.3-flash`) or `agent:model` (e.g., `claude-code:haiku`; `default` when the agent's model isn't known). |
|
|
293
|
+
| `latencyMs` | `number` | Total request duration in milliseconds, including retries. |
|
|
294
|
+
| `source` | `'direct'` | Indicates the request executed in-process. |
|
|
295
|
+
| `meta` | `DirectMeta` | Execution details (see table below). |
|
|
296
|
+
|
|
297
|
+
**`DirectMeta` (`result.meta`):**
|
|
298
|
+
|
|
299
|
+
| Field | Type | Description |
|
|
300
|
+
| --- | --- | --- |
|
|
301
|
+
| `engine` | `'http' \| 'local-agent'` | Execution engine used. |
|
|
302
|
+
| `schemaEnforcement` | `'native' \| 'in-prompt'` | Whether the engine enforced the schema natively or via prompt instructions. The output is validated either way. |
|
|
303
|
+
| `provider` | `string` | *(`engine: 'http'` only)* LLM provider name. |
|
|
304
|
+
| `agent` | `'claude-code' \| 'codex' \| 'pi'` | *(`engine: 'local-agent'` only)* Local agent CLI used. |
|
|
305
|
+
| `model` | `string \| undefined` | The provider's model, or the model the agent reported or was asked for (`undefined` when neither is known). |
|
|
306
|
+
| `costUsdEstimate` | `number \| undefined` | *(`engine: 'local-agent'` only)* The CLI's estimated API cost (does not reflect subscription billing). |
|
|
307
|
+
|
|
308
|
+
### Error (`result.success === false`)
|
|
309
|
+
|
|
310
|
+
| Field | Type | Description |
|
|
311
|
+
| --- | --- | --- |
|
|
312
|
+
| `success` | `false` | Request failed (see retries below). |
|
|
313
|
+
| `error` | `string` | Error description. |
|
|
314
|
+
| `source` | `'direct'` | Indicates the request executed in-process. |
|
|
315
|
+
| `latencyMs` | `number` | Time elapsed until failure in milliseconds. |
|
|
316
|
+
|
|
317
|
+
> **💡 Tip: Maximize Prompt Caching**
|
|
318
|
+
> Keep your `prompt` string static across calls and pass dynamic values via `input`. Nuabase places the prompt and schema at the beginning of the payload so providers can cache that prefix. Cache reads are heavily discounted—whereas on providers like Claude, changing the prompt prefix on every call incurs a 25% cache-write premium without ever getting a cache hit.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## How It Works Under the Hood
|
|
301
323
|
|
|
302
|
-
|
|
303
|
-
Submits an async job for single-value operations. Returns `{ success: true, jobId }`.
|
|
324
|
+
Each `get()` or `list()` call converts your Zod schema to JSON Schema to tell the model the shape, sends it alongside your prompt and input data, and parses the returned payload with your Zod schema. `data` is Zod's parsed output: `.default()`s are filled in, `.transform()`s run, `.refine()` checks apply, and unknown object keys are dropped. The JSON Schema describes what the schema accepts (its input side), so `.transform()` is fine; checks it can't express, such as `.refine()`, aren't shown to the model, but a payload that fails them is retried like any other invalid output.
|
|
304
325
|
|
|
305
|
-
|
|
306
|
-
Submits an async job for array operations. Returns `{ success: true, jobId }`.
|
|
326
|
+
Transient errors and outputs that fail validation are **automatically retried up to 2 times**. Fatal errors fail at once: a bad API key, an agent that isn't logged in, an exhausted quota, a timeout, or a model none of your providers serves.
|
|
307
327
|
|
|
308
|
-
|
|
328
|
+
### HTTP Provider Endpoints
|
|
309
329
|
|
|
310
|
-
|
|
311
|
-
- Explore more SDK usage patterns (SSE streaming, webhooks, caching) in the full docs at https://docs.nuabase.com.
|
|
330
|
+
When using `Nua.direct({ providers })`, your process makes 1 HTTPS request per call (plus up to 2 retries) directly to the selected provider:
|
|
312
331
|
|
|
313
|
-
|
|
332
|
+
| Provider | Endpoint |
|
|
333
|
+
| --- | --- |
|
|
334
|
+
| **Groq** | `https://api.groq.com/openai/v1/chat/completions` |
|
|
335
|
+
| **Cerebras** | `https://api.cerebras.ai/v1/chat/completions` |
|
|
336
|
+
| **Gemini** | `https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent` |
|
|
337
|
+
| **OpenRouter** | `https://openrouter.ai/api/v1/chat/completions` |
|
|
338
|
+
| **DeepSeek** | `https://api.deepseek.com/chat/completions` |
|
|
314
339
|
|
|
315
|
-
|
|
340
|
+
OpenRouter is itself a router: it forwards each request to a provider that hosts the model.
|
|
316
341
|
|
|
317
|
-
|
|
342
|
+
### Local Agent Sandboxing & Schema Handling
|
|
318
343
|
|
|
319
|
-
|
|
344
|
+
When using `Nua.direct({ localAgent })`, each call spawns the CLI in a fresh, empty temporary directory with all tools, project settings, and MCP servers disabled.
|
|
320
345
|
|
|
321
|
-
|
|
346
|
+
* **Schema wrapping:** Because agent CLIs require a top-level object, Nuabase wraps your schema as `{ value: ... }` and passes it to the CLI's schema flag.
|
|
347
|
+
* **Fallback to in-prompt schemas:**
|
|
348
|
+
* **Codex** only accepts OpenAI strict-mode schemas; schemas with optional properties or open-ended objects are injected into the prompt instead.
|
|
349
|
+
* **Pi** has no native schema flag, so schemas are always described in the prompt.
|
|
350
|
+
* If any agent rejects a native schema flag, Nuabase automatically retries with the schema injected into the prompt. The output is parsed with your Zod schema either way.
|
|
322
351
|
|
|
323
|
-
|
|
352
|
+
---
|
|
324
353
|
|
|
325
|
-
##
|
|
354
|
+
## Resources & Community
|
|
326
355
|
|
|
327
|
-
|
|
356
|
+
* **Documentation:** [docs.nuabase.com](https://docs.nuabase.com)
|
|
357
|
+
* **Testing Guide:** [TESTING.md](TESTING.md)
|
|
358
|
+
* **Support:** [hello@nuabase.com](mailto:hello@nuabase.com) | [@NuabaseHQ on X](https://x.com/NuabaseHQ)
|
|
359
|
+
* **Credits:** Package template based on [rtivital/ts-package-template](https://github.com/rtivital/ts-package-template)
|
|
360
|
+
* **License:** MIT
|