langchain-nace 0.0.0-stage → 0.1.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.
Potentially problematic release.
This version of langchain-nace might be problematic. Click here for more details.
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +267 -2
- package/dist/classifier-BzDkrL3L.cjs +1256 -0
- package/dist/classifier-BzDkrL3L.cjs.map +1 -0
- package/dist/classifier-Cp_Lo5Sj.d.cts +292 -0
- package/dist/classifier-Cp_Lo5Sj.d.mts +292 -0
- package/dist/classifier-D3hp7mSi.mjs +1067 -0
- package/dist/classifier-D3hp7mSi.mjs.map +1 -0
- package/dist/index.cjs +161 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +227 -0
- package/dist/index.d.mts +227 -0
- package/dist/index.mjs +133 -0
- package/dist/index.mjs.map +1 -0
- package/dist/middleware/index.cjs +621 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +288 -0
- package/dist/middleware/index.d.mts +288 -0
- package/dist/middleware/index.mjs +603 -0
- package/dist/middleware/index.mjs.map +1 -0
- package/package.json +105 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
Before release, a review found and fixed: an API key printed by `console.log(router)`; the tool gate reusing a cached verdict for a different call with the same tool call id; OpenAI `input_image`, Anthropic `source` and Google `media` image blocks sent as text; cancellation and `timeout` not aborting the HTTP request; an unsendable screenshot blocking every later call; `__proto__` keys dropped; noul criteria with unknown keys dropped silently.
|
|
8
|
+
|
|
9
|
+
- `NaceClassifier`: a Runnable that asks Drex typed questions (`noul`, `choice`, `score`) about text, JSON, LangChain messages and images, and returns full probabilities, usage and the request id. Default model `drex-max`.
|
|
10
|
+
- Images on Drex Max from bytes, paths, base64, data URLs, LangChain image blocks and `nace-sdk` uploads; images inside messages (objects or `{ role, content }`) are moved to `images` as "Image N".
|
|
11
|
+
- `NaceRouter` with `.branch()`, and `naceConditionalEdge` for LangGraph with a `human_review` fallback.
|
|
12
|
+
- `langchain-nace/middleware`: `naceAutoModeMiddleware` (tool gate: risk, destructive, asked-for; block, ask a person, or allow; fails closed) and `naceModelRouterMiddleware` (one Drex choice per run; escalates when unsure or on errors).
|
|
13
|
+
- Drop-in for `@langchain/typesafe`: same question objects, options, response fields, and middleware names (`autoModeMiddleware`, `modelRouterMiddleware`).
|
|
14
|
+
- OpenRouter backend; attribution headers `User-Agent` and `X-Nace-Client: langchain-nace-js/<version>`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nace AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,268 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">langchain-nace</h1>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<b>Calibrated decisions for LangChain.js agents, powered by <a href="https://console.nace.ai/docs">Drex</a>.</b><br/>
|
|
5
|
+
Route requests, pick models and check tool calls before they run, with real probabilities instead of generated text.
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://www.npmjs.com/package/langchain-nace"><img alt="npm" src="https://img.shields.io/npm/v/langchain-nace?logo=npm&color=cb3837"></a>
|
|
10
|
+
<a href="https://github.com/nace-ai/langchain-nace-js/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/nace-ai/langchain-nace-js/ci.yml?branch=main&label=CI&logo=github"></a>
|
|
11
|
+
<a href="https://github.com/nace-ai/langchain-nace-js/actions/workflows/live.yml"><img alt="Live contract" src="https://img.shields.io/github/actions/workflow/status/nace-ai/langchain-nace-js/live.yml?branch=main&label=live%20contract&logo=github"></a>
|
|
12
|
+
<img alt="Node 20+" src="https://img.shields.io/badge/node-%E2%89%A520-339933?logo=node.js&logoColor=white">
|
|
13
|
+
<img alt="TypeScript" src="https://img.shields.io/badge/types-included-3178c6?logo=typescript&logoColor=white">
|
|
14
|
+
<a href="https://github.com/nace-ai/langchain-nace-js/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
**Drex** is Nace's family of decision models. It reads text, JSON, LangChain messages and, on Drex Max, images. It answers typed questions (yes/no, pick one, score on a rubric) with a probability for every option. This package brings it to LangChain.js:
|
|
20
|
+
|
|
21
|
+
| | |
|
|
22
|
+
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| 🧭 **`NaceClassifier`** | A Runnable that answers many questions about one state in a single request, with full probabilities, usage and a request id. |
|
|
24
|
+
| 🔀 **`NaceRouter` / `naceConditionalEdge`** | Route LCEL chains and LangGraph graphs on Drex's choice; send unsure cases to a person. |
|
|
25
|
+
| 🛡️ **`naceAutoModeMiddleware`** | Check every tool call before it runs: allow, ask a person, or block. Remembers the user's original request, reads screenshots, and blocks if Drex can't answer. |
|
|
26
|
+
| 💸 **`naceModelRouterMiddleware`** | Pick the cheapest chat model that can handle each run; escalate when unsure. |
|
|
27
|
+
| 🔁 **Drop-in for `@langchain/typesafe`** | Change the imports and the API key; your code keeps working. |
|
|
28
|
+
|
|
29
|
+
**Contents:** [Install](#install) · [Quickstart](#quickstart) · [Switching from TypeSafe](#switching-from-langchaintypesafe) · [Models](#models) · [Images](#images-drex-max) · [Routing](#routing) · [Agent middleware](#agent-middleware-experimental) · [Backends](#backends) · [Errors](#errors) · [Tracing](#tracing) · [Development](#development)
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install langchain-nace @langchain/core
|
|
35
|
+
npm install langchain @langchain/langgraph # only for langchain-nace/middleware
|
|
36
|
+
export NACE_API_KEY=nace_sk_...
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- **Node 20 or later.** ESM and CommonJS, with TypeScript types.
|
|
40
|
+
- **Peers:** `@langchain/core` ≥ 1.2.16; for the middleware, `langchain` ≥ 1.3.0 and `@langchain/langgraph`.
|
|
41
|
+
- **One copy of `@langchain/core` and `@langchain/langgraph`.** If your package manager installs two, add `overrides`/`resolutions`. With two copies, a `Command` from one isn't recognized by agents built on the other, so resuming after a person answers fails.
|
|
42
|
+
|
|
43
|
+
Looking for Python? The package of the same name is on [PyPI](https://pypi.org/project/langchain-nace/).
|
|
44
|
+
|
|
45
|
+
## Quickstart
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { NaceClassifier } from "langchain-nace";
|
|
49
|
+
|
|
50
|
+
const classifier = new NaceClassifier({
|
|
51
|
+
// model: "drex-max" by default
|
|
52
|
+
questions: {
|
|
53
|
+
refund: { type: "noul", instructions: "Is the customer asking for a refund?" },
|
|
54
|
+
team: {
|
|
55
|
+
type: "choice",
|
|
56
|
+
instructions: "Which team should handle this?",
|
|
57
|
+
criteria: { billing: "Payments and refunds", technical: "Bugs" },
|
|
58
|
+
},
|
|
59
|
+
mood: {
|
|
60
|
+
type: "score",
|
|
61
|
+
instructions: "How upset is the customer?",
|
|
62
|
+
criteria: ["calm", "annoyed", "furious"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const response = await classifier.invoke(
|
|
68
|
+
"I was charged twice for March and I'm annoyed. Please refund one.",
|
|
69
|
+
);
|
|
70
|
+
response.nouls.refund.noul; // 0.98: probability of yes
|
|
71
|
+
response.choices.team.probabilities; // { billing: 0.99, technical: 0.01 }
|
|
72
|
+
response.scores.mood.score; // 1.1: expected level
|
|
73
|
+
response.requestId; // quote it in support requests
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Question | Ask | Get back |
|
|
77
|
+
| -------- | ----------------------------------------- | -------------------------------------------------------------------------- |
|
|
78
|
+
| `noul` | a yes/no proposition | `noul`: the probability of yes |
|
|
79
|
+
| `choice` | pick one of your labels | `choice`, `confidence`, and `probabilities` for every label |
|
|
80
|
+
| `score` | a level on an ordered rubric (low → high) | `score` (may fall between levels), `legend`, `probabilities`, `confidence` |
|
|
81
|
+
|
|
82
|
+
All questions about one state go in one request, and answers keep their full probabilities, so thresholds stay in your code. The state can be a string, any JSON, a LangChain message, or messages anywhere inside objects and arrays. `batch` works as on any Runnable; it runs at most 8 requests at once unless you set `maxConcurrency`. To ask different questions on one call, pass `{ questions }` in the call options.
|
|
83
|
+
|
|
84
|
+
## Switching from `@langchain/typesafe`
|
|
85
|
+
|
|
86
|
+
Change the imports and set `NACE_API_KEY`. Question objects, constructor options, response fields, middleware names and middleware options all stay the same:
|
|
87
|
+
|
|
88
|
+
```diff
|
|
89
|
+
- import { TypeSafeClassifier } from "@langchain/typesafe";
|
|
90
|
+
- import { autoModeMiddleware, modelRouterMiddleware } from "@langchain/typesafe/middleware";
|
|
91
|
+
+ import { NaceClassifier } from "langchain-nace";
|
|
92
|
+
+ import { autoModeMiddleware, modelRouterMiddleware } from "langchain-nace/middleware";
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
| `@langchain/typesafe` | `langchain-nace` |
|
|
96
|
+
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
97
|
+
| `TypeSafeClassifier` | `NaceClassifier` (default model `drex-max`) |
|
|
98
|
+
| question objects; `Noul`, `Choice`, `Score` | the same |
|
|
99
|
+
| constructor options (`questions`, `model`, `apiKey`, `baseUrl`, `timeout`, `fetch`, `maxRetries`, `maxConcurrency`, …) | the same |
|
|
100
|
+
| `model`, `answers`, `usage`, `requestId`, `nouls`, `choices`, `scores` | the same, plus `evaluationTimeMs`, `usage.imageTokens`, `raw` |
|
|
101
|
+
| `modelRouterMiddleware({ choices, instructions, classifierOptions })` | the same; the decision is in state under `modelRoute` |
|
|
102
|
+
| `autoModeMiddleware({ tools, instructions, criteria, blockedMessage, classifierOptions })` | the same |
|
|
103
|
+
| `TypeSafe*Error` | `Nace*Error`, with `.isInstance()` |
|
|
104
|
+
| `TYPESAFE_API_KEY`, `TYPESAFE_BASE_URL` | `NACE_API_KEY`, `NACE_BASE_URL` |
|
|
105
|
+
|
|
106
|
+
What behaves differently, on purpose:
|
|
107
|
+
|
|
108
|
+
- **Different model, different numbers.** Thresholds tuned on Jev's probabilities may need retuning.
|
|
109
|
+
- **The timeout is 60 s** (TypeSafe: 30 s), because Drex may take up to 55 s.
|
|
110
|
+
- **The tool gate asks more.** Besides your `instructions`/`criteria` question, which still blocks at 0.5, Drex scores the risk and answers "destructive?" and "did the user ask for this?". The gate can also ask a person (see below).
|
|
111
|
+
- **The model router doesn't throw.** When Drex fails or isn't confident it uses the strongest model; with no user message, the agent's own model.
|
|
112
|
+
- **`dangerouslyAllowBrowser` defaults to `false`.**
|
|
113
|
+
|
|
114
|
+
## Models
|
|
115
|
+
|
|
116
|
+
| Model | Reads | Questions per request |
|
|
117
|
+
| ------------------------ | --------------------- | --------------------- |
|
|
118
|
+
| **`drex-max`** (default) | text, JSON and images | up to 16 |
|
|
119
|
+
| `drex-v2.0` | text and JSON | up to 512 |
|
|
120
|
+
|
|
121
|
+
Any other model id is sent as given (`drex-v1.5`, `drex-latest`, and models released later). Drex enforces each model's limits, and its errors say what to change. Pin a model with `model` or `NACE_DEFAULT_DECISION_MODEL`. The package default moves to the newest flagship with each Drex release, so pin one if you need results that never change.
|
|
122
|
+
|
|
123
|
+
## Images (Drex Max)
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const damage = new NaceClassifier({
|
|
127
|
+
questions: {
|
|
128
|
+
damaged: {
|
|
129
|
+
type: "choice",
|
|
130
|
+
instructions: "Which image shows damage?",
|
|
131
|
+
criteria: { "Image 1": null, "Image 2": null },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
await damage.invoke("Two product photos.", {
|
|
136
|
+
images: [readFileSync("a.png"), "data:image/jpeg;base64,..."],
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- **Accepted:**
|
|
141
|
+
- bytes (`Buffer`, `Uint8Array`, `ArrayBuffer`), file paths, base64 and data URLs;
|
|
142
|
+
- LangChain image blocks (`{ type: "image", data, mimeType }`, `source_type: "base64"`, `image_url`);
|
|
143
|
+
- provider blocks: OpenAI `input_image`, Anthropic `source`, Google `media`;
|
|
144
|
+
- files uploaded with [`nace-sdk`](https://www.npmjs.com/package/nace-sdk).
|
|
145
|
+
- **Images inside messages** are moved to `images` automatically and referred to as "Image N". This works for message objects and for plain `{ role, content: [...] }` objects.
|
|
146
|
+
- **Never dropped silently.** Remote URLs are refused, because Drex doesn't fetch them. Images sent to a text-only model (`drex-v2.0`, `drex-v1.5`) throw `NaceImageModelError` before anything is sent.
|
|
147
|
+
|
|
148
|
+
## Routing
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { NaceRouter } from "langchain-nace";
|
|
152
|
+
|
|
153
|
+
const router = new NaceRouter({
|
|
154
|
+
question: {
|
|
155
|
+
type: "choice",
|
|
156
|
+
instructions: "Which team?",
|
|
157
|
+
criteria: { billing: null, technical: null },
|
|
158
|
+
},
|
|
159
|
+
state: (input: { text: string }) => input.text,
|
|
160
|
+
minConfidence: 0.6,
|
|
161
|
+
fallback: "human",
|
|
162
|
+
});
|
|
163
|
+
const chain = router.branch(
|
|
164
|
+
{ billing: billingChain, technical: techChain, human: escalate },
|
|
165
|
+
escalate,
|
|
166
|
+
);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**LangGraph:** `naceConditionalEdge({ question, state, minConfidence: 0.7 })` is a path function for `addConditionalEdges`. Below the threshold it returns `"human_review"`, so that node can `interrupt()` for a person. See [`examples/langgraph-router.ts`](https://github.com/nace-ai/langchain-nace-js/blob/main/examples/langgraph-router.ts).
|
|
170
|
+
|
|
171
|
+
## Agent middleware (experimental)
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { MemorySaver } from "@langchain/langgraph";
|
|
175
|
+
import { createAgent } from "langchain";
|
|
176
|
+
import { naceAutoModeMiddleware, naceModelRouterMiddleware } from "langchain-nace/middleware";
|
|
177
|
+
|
|
178
|
+
const router = naceModelRouterMiddleware({
|
|
179
|
+
choices: {
|
|
180
|
+
fast: { model: "anthropic:claude-haiku-5-5", criteria: "Short lookups" },
|
|
181
|
+
strong: { model: "anthropic:claude-sonnet-5-5", criteria: "Multi-step changes" },
|
|
182
|
+
},
|
|
183
|
+
instructions: "Pick the cheapest model that can do this safely.",
|
|
184
|
+
});
|
|
185
|
+
const gate = naceAutoModeMiddleware({ screenshots: true }); // drex-max reads the screenshots
|
|
186
|
+
|
|
187
|
+
const agent = createAgent({
|
|
188
|
+
model: "anthropic:claude-haiku-5-5",
|
|
189
|
+
tools,
|
|
190
|
+
middleware: [router, gate],
|
|
191
|
+
checkpointer: new MemorySaver(), // lets the gate pause for a person
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### `naceAutoModeMiddleware`: the tool gate
|
|
196
|
+
|
|
197
|
+
Before each tool call, Drex gets one request with:
|
|
198
|
+
|
|
199
|
+
- the user's **original request**, kept however long the run gets;
|
|
200
|
+
- the latest user message and the last 20 messages;
|
|
201
|
+
- the tool call and its description;
|
|
202
|
+
- with `screenshots: true` and Drex Max, the newest screenshots.
|
|
203
|
+
|
|
204
|
+
It answers three questions: a risk score, "destructive?" and "did the user ask for this?". The default `thresholdPolicy()` then decides:
|
|
205
|
+
|
|
206
|
+
| Decision | When |
|
|
207
|
+
| ------------------- | --------------------------------------------- |
|
|
208
|
+
| 🛑 **Block** | risk ≥ 0.75, or destructive and not asked for |
|
|
209
|
+
| ✋ **Ask a person** | risk ≥ 0.4, or destructive and asked for |
|
|
210
|
+
| ✅ **Allow** | otherwise |
|
|
211
|
+
- **Asking a person** raises a LangGraph `interrupt` with a `{ type: "nace_tool_approval", name, args, verdict, … }` value. Resume with `new Command({ resume })`, where `resume` is `{ type: "approve" }`, `{ type: "reject", message }` or `{ type: "edit", args }`.
|
|
212
|
+
- Asking needs a checkpointer. Without one the call is blocked, and the message says so. Use `askHuman: false` to always block instead.
|
|
213
|
+
- **If Drex can't answer, the call is blocked** (`onError: "block"`). A screenshot Drex can't read is dropped, and the check runs again without it.
|
|
214
|
+
- **Everything is replaceable:** pass `questions`, `thresholdPolicy({ ... })`, or your own `(verdict) => GateDecision`.
|
|
215
|
+
- **Resuming:** the gate keeps its first verdict in memory, so resuming asks Drex once. A resume in another process asks Drex again.
|
|
216
|
+
|
|
217
|
+
### `naceModelRouterMiddleware`: the model picker
|
|
218
|
+
|
|
219
|
+
The router picks the model once per run. Below `minConfidence` (default 0.6), or if Drex can't be reached, it uses `escalateTo`: the last choice by default, so list choices cheapest first. The decision and probabilities are kept in state under `modelRoute`.
|
|
220
|
+
|
|
221
|
+
[`examples/support-agent.ts`](https://github.com/nace-ai/langchain-nace-js/blob/main/examples/support-agent.ts) runs four live scenarios:
|
|
222
|
+
- an allowed read;
|
|
223
|
+
- a blocked unrequested delete;
|
|
224
|
+
- a person approving a requested delete;
|
|
225
|
+
- a screenshot-based block on a computer-use click.
|
|
226
|
+
|
|
227
|
+
## Backends
|
|
228
|
+
|
|
229
|
+
| Backend | `baseUrl` | Notes |
|
|
230
|
+
| ------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
|
|
231
|
+
| Nace (default) | `https://console.nace.ai` | or `NACE_BASE_URL` |
|
|
232
|
+
| OpenRouter | `https://openrouter.ai/api/v1` | models `nace-ai/drex-v2.0`, `nace-ai/drex-max`; images are sent the way OpenRouter expects |
|
|
233
|
+
| TypeSafe-compatible hosts | the host's System One root | same request format as Nace |
|
|
234
|
+
|
|
235
|
+
Every request sends `User-Agent` and `X-Nace-Client: langchain-nace-js/<version>`. On OpenRouter it also sends `HTTP-Referer` and `X-OpenRouter-Title`.
|
|
236
|
+
|
|
237
|
+
## Errors
|
|
238
|
+
|
|
239
|
+
Every error extends LangChain's `LangChainError`. Check errors with `.isInstance()`, which keeps working when two copies of the package are installed. Messages never include your state.
|
|
240
|
+
|
|
241
|
+
| Error | When |
|
|
242
|
+
| --------------------------------------------------------- | ---------------------------------------------------- |
|
|
243
|
+
| `NaceAuthenticationError` | 401 |
|
|
244
|
+
| `NaceInsufficientCreditError` | 402, with the Billing link |
|
|
245
|
+
| `NaceUnprocessableEntityError` | 422; lists each issue's `path: message` |
|
|
246
|
+
| `NaceRateLimitError` | 429, after retries (`retryAfterMs`) |
|
|
247
|
+
| `NaceOverloadedError` | 529, after retries |
|
|
248
|
+
| `NaceAPIConnectionError`, `NaceAPITimeoutError` | network problems |
|
|
249
|
+
| `NaceImageModelError`, `NaceImageError`, `NaceMediaError` | images or media that can't be sent; nothing was sent |
|
|
250
|
+
- **Retries:** 408, 429, 5xx and 529 responses are retried (`maxRetries: 2`), waiting as long as `retry-after-ms` asks. Each attempt may take up to 60 s.
|
|
251
|
+
- **Cancelling:** a call's `signal` or `timeout`, or a cancelled agent run, also aborts the HTTP request.
|
|
252
|
+
|
|
253
|
+
## Tracing
|
|
254
|
+
|
|
255
|
+
Calls show up in LangSmith as `llm` runs (`ls_provider: "nace"`) with the questions, the full answers and probabilities, token usage and the Drex request id. The router and the middlewares add their thresholds to the run metadata.
|
|
256
|
+
|
|
257
|
+
## Development
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
pnpm install
|
|
261
|
+
pnpm lint && pnpm typecheck && pnpm test # offline, against a fake Drex
|
|
262
|
+
NACE_API_KEY_LIVE=nace_sk_... pnpm test:int # live contract tests
|
|
263
|
+
pnpm build && pnpm smoke # install the packed tarball in a fresh project
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
[MIT](https://github.com/nace-ai/langchain-nace-js/blob/main/LICENSE)
|