skynet-graph 1.2.0 → 1.3.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
@@ -14,26 +14,34 @@ for free, and steers the model's output against a certified method vocabulary. N
14
14
  <p align="center"><i>Active R&D · a CommonJS library to embed + an <code>sg</code> CLI · Node 18+, no build step · AGPL-3.0</i></p>
15
15
 
16
16
  <p align="center">
17
+ <a href="https://www.npmjs.com/package/skynet-graph"><img src="https://img.shields.io/npm/v/skynet-graph?logo=npm&amp;color=cb3837" alt="npm version"></a>
18
+ <a href="https://www.npmjs.com/package/skynet-graph"><img src="https://img.shields.io/npm/dm/skynet-graph?color=cb3837" alt="npm downloads"></a>
19
+ <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/skynet-graph" alt="node version"></a>
20
+ <a href="./LICENSE"><img src="https://img.shields.io/npm/l/skynet-graph?color=blue" alt="AGPL-3.0"></a>
17
21
  <a href="https://doi.org/10.5281/zenodo.21032471"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.21032471.svg" alt="DOI"></a>
18
22
  <a href="https://doi.org/10.5281/zenodo.21201877"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.21201877.svg" alt="DOI"></a>
19
23
  </p>
20
24
 
21
- ## What you get
25
+ ## What it is
22
26
 
23
- A 27B model crushed to 9.5 GB (IQ2) runs on your GPUbut quantization broke part of its judgment,
24
- and any big task overflows it. This engine gives that model four things, **each measured on real GPUs,
25
- with negative controls and deterministic re-runs**:
27
+ **skynet-graph is the low-level reasoning substrate + a library of composable combos**the engine you
28
+ *embed*. The substrate is a versionable, git-like reasoning graph: declarative concept rules + provider
29
+ functions driven to a coherent belief state by stabilization, with native cascading retraction and
30
+ rollback / fork / merge on what the system currently holds true. On top of it, a set of **combos**
31
+ (`Graph.combos.*`, C1–C9 + the forge) each turn on one capability for a task type — the *bricks* an
32
+ application assembles.
26
33
 
27
- - **Big tasks, piece by piece** the model never sees the whole problem, only one bounded piece at a
28
- time: accuracy **×2.5–3.2** on multi-step tasks, and where whole-context prompting **collapses (0/33)**,
29
- the pieces hold (**10/33**).
30
- - **Repair for low quants** verified method shapes steer the output: SQL **8→63 %**, finance tables
31
- **7→62 %**, at **zero big-model calls** on covered queries.
32
- - **Task memory that reopens** — a late correction automatically un-does everything that depended on
33
- it, and "done" steps **reopen themselves with the reason**. No stale state, ever.
34
- - **An external think mode** — benched head-to-head: the model's *native* think budget scores
35
- **13/24 a coin flip** on side-judgment; the external critical mind renders **0 wrong verdicts**
36
- (24/24 provable on a declared frame, an honest UNDECIDED otherwise).
34
+ For a small local model those bricks add up to four capabilities (one line each here; the measured
35
+ numbers, negative controls, limits and maturity live in **[doc/CAPABILITIES.md](doc/CAPABILITIES.md)**):
36
+
37
+ - **Piece-by-piece** works a big task one bounded piece at a time: **×2.5–3.2** on multi-step tasks, and where whole-context prompting collapses (**0/33**) the pieces hold (**10/33**).
38
+ - **Low-quant repair** — certified method shapes steer the output: SQL **8→63 %**, finance **7→62 %**, at zero big-model calls on covered queries.
39
+ - **Task memory that reopens** — a late correction retracts its consequences in cascade; "done" steps reopen with the reason, at 0 model calls.
40
+ - **External think mode** the model proposes, the graph refutes with the reason: a native think budget scores **13/24 ≈ chance**, the external critical mind **0 wrong verdicts**.
41
+
42
+ These are the building blocks; **[mindsmith](https://www.npmjs.com/package/mindsmith) is the public façade
43
+ that puts them in users' hands** (an OpenAI endpoint + MCP tools + local rooms). This repo is what mindsmith
44
+ — or your own app — is built on.
37
45
 
38
46
  **See it in 30 seconds — no model, no GPU** (a deterministic replay of a real end-to-end run — the
39
47
  9.5 GB quant analyzing an annual report, erratum and crash included):
@@ -62,22 +70,20 @@ Where it sits in your setup — two zero-integration doors into one local loop:
62
70
  [your local GGUF model] — nothing leaves the machine
63
71
  ```
64
72
 
65
- ---
66
-
67
- ## The four capabilities, measured
73
+ > **Two packages, one loop.** This repo — **`skynet-graph`** — is the **substrate + the combos**: the
74
+ > reasoning engine and the composable bricks (`Graph.combos.*`, C1–C9 + the forge) that each *activate* a
75
+ > capability for a task type. **[mindsmith](https://www.npmjs.com/package/mindsmith)** is the ready-made
76
+ > **app that actually uses them** — the endpoint + MCP tools + local rooms drawn above, assembled and
77
+ > hardened. **To *run* this on your model → `npx mindsmith serve`. Embed skynet-graph to *build* your own.**
68
78
 
69
- Small local models are fine on a *surface* step; they derail when a request makes them **compose**
70
- several things at once. This library puts them in front of one surface at a time.
79
+ ---
71
80
 
72
- **Proof degrees:** **[live]** = measured on GPU with real local models · **[measured]** = deterministic,
73
- replayable without a model · **[PoC]** = an accounting demonstration, not a benchmark.
81
+ ## Capabilities & maturity
74
82
 
75
- | Capability | What happens | Measured |
76
- |---|---|---|
77
- | **Repair low-quants** | a menu of *certified* method shapes steers a heavily-quantized model's output it recovers most of what compression broke, at **zero big-model calls** | SQL, covered queries: low-quant 8→**63 %** (high-quant 46→92 %), N=201 · finance, traffic view: 7→**62 %** (20→78 %), N=120 · [live] |
78
- | **Task memory that reopens** | task state = typed facts with provenance (JTMS): a drifted premise **retracts its consequences in cascade**, and a "done" step **reopens itself with the reason** — it never rots | recomputable drift re-derives at **0 model calls**, selectively (independent facts untouched); crash-replay is bit-identical at 0 calls [live] · 100 % recall at **894 constant tokens/call** vs 50 % at 4 286 for a carry-everything baseline [PoC] |
79
- | **Piece-by-piece (the zoom)** | the task becomes a typed DAG; each piece is served with ONLY its bounded neighbourhood (parent goal + resolved inputs + what to produce) — the model never sees the whole | cross-domain at N=200/domain (560 tasks total): math word problems 16 %→**52 %** (×3.2), financial-table QA 20 %→**50 %** (×2.5), bootstrap CIs hold [live] · **where the lone model collapses, the pieces hold**: deep tasks 0/33 whole vs 10/33 decomposed [live] · on 20 compound "monster" tasks (~20 ops): whole-context floors at **0/20**, hierarchical 2-level split reaches 73 % of sections [live] |
80
- | **An external think mode** | the model proposes; the graph **refutes with the reason** and enumerates the admissible options (tested through its own gate, never guessed); the model revises — bounded, with honest refusal | one dialogue round: 17/24 → **24/24** correct at zero false admissions [live] · native think budget vs the external critical mind on side-judgment: **13/24 (≈ chance, 11 confident wrong verdicts) vs 0 wrong verdicts** — 24/24 mechanical on a declared perimeter, honest UNDECIDED otherwise [live] · disciplined argument coverage: **77 % vs 58 %** whole-context at 48 arguments [live] · anchored generation of MISSING theses: **0 fabrication** across every negative control [live] |
83
+ The measured detail numbers, negative controls, limits and snippets per feature — lives in
84
+ **[doc/CAPABILITIES.md](doc/CAPABILITIES.md)**; **[mindsmith](https://www.npmjs.com/package/mindsmith)**
85
+ is where those capabilities are framed and put in users' hands. Maturity uses a 6-rung honest scale
86
+ (rung 6 = external replications, empty pre-launch):
81
87
 
82
88
  | Feature — details per row in [doc/CAPABILITIES.md](doc/CAPABILITIES.md) | Maturity (6-rung honest scale; rung 6 = external replications, empty pre-launch) |
83
89
  |---|---|
@@ -95,20 +101,12 @@ builds `.sgc` method stocks from any dataset that has an executable oracle, behi
95
101
  gate (held across every campaign: 0 false shapes admitted, 3 datasets, 2 forge models) — each stock ships
96
102
  with an auditable sha256 validation dossier.
97
103
 
98
- ### Where it beats what you would reach for today
99
-
100
- Each row is a measured delta or a checked absence — details, limits and snippets per row in
101
- [doc/CAPABILITIES.md](doc/CAPABILITIES.md).
104
+ ### Where it beats what you'd reach for
102
105
 
103
- | You would reach for | The known weakness there | Here, measured |
104
- |---|---|---|
105
- | a bigger / higher-quant model | needs VRAM you don't have | certified-stock steering on *your* hardware: SQL 8→**63 %**, finance 7→**62 %**, 0 frontier calls on the covered slice |
106
- | an agent scratchpad / vector memory | boxes only get ticked; similarity retrieval serves **stale** answers | premise drift → cascade retraction + reopen-with-reason: **12/12 vs 0/12** (stale) on drift, 0 model calls |
107
- | a decomposition framework | free-text plans, unbounded context growth | typed needs/produces + bounded per-piece context: ×2.5–3.2 at N=200, and **0/33 → 10/33** where the whole-task baseline collapses |
108
- | a native think mode / self-critique loop | self-critique underperforms external feedback (2024-25 literature, + our own refutation ×3) | benched head-to-head: a native think budget scores **13/24 ≈ chance** on side-judgment (11 confident wrong verdicts) — the external critical mind renders **0 wrong verdicts**: 24/24 mechanical on a declared perimeter, honest UNDECIDED otherwise; and the gate **never yields** (17/24 → **24/24** at zero false admissions) |
109
- | LLM-as-judge / a debate prompt | miscalibrated, gameable, no audit trail | witness-gated coverage (0 fabrication), typed ledger, and a judge that **declines by measured bound** (UNDECIDED below margin) |
110
- | a prompt hub / RAG index for methods | anything pasted in is trusted | `.sgc` rooms: admission-gated import (a bad bundle never lands), sha256 dossiers, local files you own |
111
- | a rules engine / event sourcing | fires rules or versions events — never versions *belief* | rollback / diff / fork / merge on what the system currently holds true, **rules included** — no market equivalent found |
106
+ The head-to-head against a bigger model, an agent scratchpad, a decomposition framework, a native think
107
+ mode, LLM-as-judge, a RAG method-index, or a rules engine — each a measured delta or a checked absence —
108
+ is framed for users in **[mindsmith](https://www.npmjs.com/package/mindsmith)**'s "Why not just…?", with
109
+ the per-feature numbers behind each in **[doc/CAPABILITIES.md](doc/CAPABILITIES.md)**.
112
110
 
113
111
  **What is honestly NOT claimed** (each of these was tested, and the page follows the results):
114
112
  - the guarantee is **at admission, not at execution** — at use time the stock *orients*; a suggestion is not
@@ -125,14 +123,19 @@ Each row is a measured delta or a checked absence — details, limits and snippe
125
123
 
126
124
  ## Quick start
127
125
 
126
+ **Run the demos + tests** — from a clone (the demos and the test suite are not in the npm tarball; npm is for
127
+ *embedding the library in your app*, see just below):
128
+
128
129
  ```bash
130
+ git clone https://github.com/9pings/skynet-graph && cd skynet-graph
129
131
  npm install # no build step — pure CommonJS, Node 18+
130
132
  npm test # 1350 tests — 0 failures, 2 known skips
131
-
132
133
  node bin/sg run --concepts ./concepts --builtins --seed ./seed.json
133
134
  node examples/integrated-demo/run.js --replay # the four capabilities assembled, no GPU
134
135
  ```
135
136
 
137
+ **Embed it in your app** — `npm install skynet-graph`, then:
138
+
136
139
  ```js
137
140
  const Graph = require('skynet-graph');
138
141
 
@@ -168,13 +171,18 @@ sg ask "your question" --concepts ./concepts --local-model <path.gguf>
168
171
  sg studio # (or: sg serve --studio — live request lines in its trace panel)
169
172
  ```
170
173
 
171
- Runnable, deterministic, GPU-free demos of every use-case class live in **`examples/bootstrap/`**
172
- one short file per combo and per surface, each printing the guarantee it demonstrates.
174
+ For the ready-made **appliance** a full local LLM server on **one VRAM load** (the model answers *with*
175
+ think, the graph runs *no-think*, sharing a single gguf), plus local `.sgc` rooms use
176
+ **[mindsmith](https://www.npmjs.com/package/mindsmith)** `serve --model <gguf>` (`--ctx`, `--gpu`,
177
+ `--gpu-layers`, `--think`, custom llama.cpp build). It's built on these surfaces.
178
+
179
+ Runnable, deterministic, GPU-free demos of every use-case class live (in the repo) under
180
+ **`examples/bootstrap/`** — one short file per combo and per surface, each printing the guarantee it demonstrates.
173
181
 
174
- > **The ready-made appliance** if you just want the endpoint + local `.sgc` stock rooms without embedding
175
- > the library, use **[skynet-dequantizer](https://github.com/9pings/skynet-dequantizer)**: `skynet-dequantizer serve` (OpenAI-compatible,
176
- > no-egress by default, proven on real sockets) · `skynet-dequantizer rooms list|import|export|freeze` (your own
177
- > shareable stock mini-repos — no catalog, no subscription, sha256 dossiers).
182
+ > **The app that uses these [mindsmith](https://www.npmjs.com/package/mindsmith).** The endpoint + rooms
183
+ > above, packaged and hardened: `npx mindsmith serve` (OpenAI-compatible, no-egress by default, proven on
184
+ > real sockets) · `mindsmith rooms list|import|export|freeze` (your own shareable stock mini-repos — no
185
+ > catalog, no subscription, sha256 dossiers). Embed *this* library instead when you are building your own.
178
186
  >
179
187
  > **For AI agents reading this repo**: `CLAUDE.md` at the root is the machine-oriented map (architecture,
180
188
  > commands, gotchas); the MCP surface is one command away (`claude mcp add sg -- node bin/sg mcp …`).
@@ -23,7 +23,7 @@ measurement → shipped in the library behind a surface, with tests; not yet rep
23
23
 
24
24
  ## Where each capability is served
25
25
 
26
- | Feature | library | `sg mcp` | dequantizer `serve` | dequantizer `mcp` |
26
+ | Feature | library | `sg mcp` | mindsmith `serve` | mindsmith `mcp` |
27
27
  |---|---|---|---|---|
28
28
  | F1 low-quant repair | ✓ | `ask` / `hint` / `propose` | ✓ (OpenAI endpoint) | ✓ `ask` / `hint` / `propose` |
29
29
  | F2 zoom | ✓ (bricks) | — (known gap, see F2) | — | — |
@@ -33,7 +33,7 @@ measurement → shipped in the library behind a surface, with tests; not yet rep
33
33
  | F6 rooms | ✓ | `lattice_load` | rooms CLI | `lattice_load` |
34
34
  | F7 substrate | ✓ (the engine itself) | — | — | — |
35
35
 
36
- [skynet-dequantizer](https://github.com/9pings/skynet-dequantizer) is the ready-made appliance over
36
+ [mindsmith](https://github.com/9pings/mindsmith) is the ready-made appliance over
37
37
  this library (an OpenAI-compatible local endpoint + `.sgc` rooms); everything else ships here.
38
38
 
39
39
  ---
@@ -204,7 +204,7 @@ claude mcp add sg -- node bin/sg mcp --frontier-model <path.gguf> --stock ./meth
204
204
  # options; `force:true` → recorded UNTRUSTED, never admission. SOFT lane: `hint` (advisory menu).
205
205
  ```
206
206
 
207
- Also wired in the dequantizer's `mcp` surface.
207
+ Also wired in the mindsmith's `mcp` surface.
208
208
 
209
209
  **In plain terms.** Like a chess coach who never moves the pieces for you: when you propose an
210
210
  illegal move it says exactly *why* it is illegal and lists the legal ones, and you choose again.
@@ -278,7 +278,7 @@ const cm = Graph.combos.createCriticalMind({ ask });
278
278
  const r = await cm.run({ topic, statements, viewpoints }); // ledger + per-side synthesis + verdict|UNDECIDED
279
279
  ```
280
280
 
281
- Over MCP (both `sg mcp` and the dequantizer's `mcp`): the `critique` tool. Its **iteration
281
+ Over MCP (both `sg mcp` and the mindsmith's `mcp`): the `critique` tool. Its **iteration
282
282
  contract**: OPEN points and an UNDECIDED verdict are a *typed data request* — the host gathers real
283
283
  statements (web, docs, its own context) and calls `critique` again with `statements`; the frame
284
284
  upgrades to MATERIAL and the margin can move honestly.
@@ -307,7 +307,7 @@ is refused, never written), and `freeze` pins it under a sha256 dossier you can
307
307
  **How to use it, simply:**
308
308
 
309
309
  ```bash
310
- skynet-dequantizer rooms list|import|export|freeze # your own shareable stock mini-repos
310
+ mindsmith rooms list|import|export|freeze # your own shareable stock mini-repos
311
311
  # in this repo, the MCP counterpart is `lattice_load` — learning through the version-gated
312
312
  # admission; there is NO direct-write tool.
313
313
  ```
@@ -11,9 +11,11 @@
11
11
  * embedded-inference study). ZERO-CORE: the ONLY model coupling is `ask({system,user,maxTokens,temperature}) -> string`
12
12
  * (mirrors `llm.js#makeOpenAIAsk`); this is a drop-in `ask` for `createLLMProvider({ ask })`.
13
13
  *
14
- * node-llama-cpp is an OPTIONAL native dep (precedent: the durable layer's `node:sqlite`) declared in
15
- * `optionalDependencies`, loaded via dynamic import (v3 is ESM-only), so a host that never wires a local model never
16
- * needs it. GPU auto-detected (CUDA/Metal/Vulkan) with a CPU fallback; the no-build/browser sibling is `wllama` (same
14
+ * node-llama-cpp is an OPTIONAL native dep (precedent: the durable layer's `node:sqlite`), loaded via dynamic
15
+ * import (v3 is ESM-only), so a host that never wires a local model never needs it. The `mindsmith` appliance
16
+ * declares it in `optionalDependencies`; a skynet-graph-direct host that wants embedded inference installs it
17
+ * (`npm install node-llama-cpp` — prebuilt binaries, no compile). GPU auto-detected (CUDA/Metal/Vulkan) with a
18
+ * CPU fallback; the no-build/browser sibling is `wllama` (same
17
19
  * GGUF, WASM) — a future `makeWasmAsk`.
18
20
  *
19
21
  * THE CROWN JEWEL — CONSTRAINED DECODING. `opts.jsonSchema` (or `opts.gbnf`) forces the model to emit a grammar-valid
@@ -78,7 +80,7 @@ function makeLocalAsk( opts ) {
78
80
 
79
81
  return async function ask( { system, user, maxTokens = 1024, temperature = 0, grammar: callGrammar, reasoningBudget: callRB } ) {
80
82
  return host.ask({
81
- modelPath, contextSize: opts.contextSize, gpu: opts.gpu, lora: opts.lora,
83
+ modelPath, contextSize: opts.contextSize, gpu: opts.gpu, lora: opts.lora, llama: opts.llama, model: opts.model,
82
84
  grammar: callGrammar || grammar, system, user, maxTokens, temperature,
83
85
  reasoningBudget: callRB != null ? callRB : opts.reasoningBudget, seed: opts.seed == null ? 0 : opts.seed
84
86
  });
@@ -32,8 +32,12 @@
32
32
  */
33
33
 
34
34
  function modelKey( spec ) {
35
+ // EVERYTHING that affects the actual VRAM load is in the key (so distinct configs never collide and
36
+ // identical configs SHARE one load — the single-model, two-budgets case). reasoningBudget is NOT here:
37
+ // it is a per-CALL parameter, so one loaded model answers both no-think (budget 0) and with-think calls.
35
38
  return [spec.modelPath, 'ctx=' + (spec.contextSize || 4096), 'gpu=' + (spec.gpu === undefined ? 'auto' : spec.gpu),
36
- 'lora=' + (spec.lora ? JSON.stringify(spec.lora) : '')].join('|');
39
+ 'lora=' + (spec.lora ? JSON.stringify(spec.lora) : ''),
40
+ 'llama=' + (spec.llama ? stable(spec.llama) : ''), 'model=' + (spec.model ? stable(spec.model) : '')].join('|');
37
41
  }
38
42
  function grammarKey( g ) { return g ? (g.jsonSchema ? 'js:' + stable(g.jsonSchema) : g.gbnf ? 'gb:' + g.gbnf : '') : ''; }
39
43
  // Finalize a completion under an optional COMPILED grammar. A JSON-SCHEMA grammar exposes `.parse()` (re-emit canonical
@@ -106,7 +110,7 @@ function createLocalModelHost( opts ) {
106
110
 
107
111
  /** The one entry point: resolve/share the model, check the prompt cache, serialize the GPU call. */
108
112
  async function ask( req ) {
109
- const spec = { modelPath: req.modelPath, contextSize: req.contextSize, gpu: req.gpu, lora: req.lora };
113
+ const spec = { modelPath: req.modelPath, contextSize: req.contextSize, gpu: req.gpu, lora: req.lora, llama: req.llama, model: req.model };
110
114
  const mk = modelKey(spec), gk = grammarKey(req.grammar);
111
115
  const ck = cacheKey(mk, gk, req);
112
116
  if ( cacheMax && cache.has(ck) ) { stats.cacheHits++; const v = cache.get(ck); cache.delete(ck); cache.set(ck, v); return v; }
@@ -141,11 +145,14 @@ function defaultLoader( hostOpts ) {
141
145
  return async function load( spec ) {
142
146
  let nlc;
143
147
  try { nlc = await import('node-llama-cpp'); }
144
- catch ( e ) { throw new Error('node-llama-cpp is not installed. Run `npm run local-inference:setup`. Cause: ' + e.message); }
148
+ catch ( e ) { throw new Error('node-llama-cpp is not installed — the embedded gguf backend needs it. Run: npm install node-llama-cpp (prebuilt binaries, no compile). Cause: ' + e.message); }
145
149
  const { getLlama, LlamaChatSession } = nlc;
146
- const llama = await getLlama(spec.gpu === undefined ? undefined : { gpu: spec.gpu });
150
+ // getLlama options (gpu · build:"auto"|"forceRebuild"|"never" · usePrebuiltBinaries:false = build from source ·
151
+ // cmakeOptions · maxThreads …) = the CUSTOM-BUILD surface; loadModel options (gpuLayers · useMmap · useMlock …).
152
+ const llamaOpts = Object.assign({}, spec.llama, spec.gpu === undefined ? {} : { gpu: spec.gpu });
153
+ const llama = await getLlama(Object.keys(llamaOpts).length ? llamaOpts : undefined);
147
154
  const before = tryVram(llama);
148
- const model = await llama.loadModel({ modelPath: spec.modelPath });
155
+ const model = await llama.loadModel(Object.assign({ modelPath: spec.modelPath }, spec.model));
149
156
  const context = await model.createContext(Object.assign({ contextSize: spec.contextSize || 4096 }, spec.lora ? { lora: spec.lora } : {}));
150
157
  const after = tryVram(llama);
151
158
  const vramBytes = (before != null && after != null) ? Math.max(0, after - before) : 0;
package/lib/sg/serve.js CHANGED
@@ -76,8 +76,10 @@ function createServeHandler( opts ) {
76
76
  var nextId = 0;
77
77
 
78
78
  var JSON_H = { 'content-type': 'application/json' };
79
- var oaiError = function ( status, message, type ) {
80
- return { status: status, headers: JSON_H, body: { error: { message: message, type: type || 'invalid_request_error' } } };
79
+ var oaiError = function ( status, message, type, code ) {
80
+ var err = { message: message, type: type || 'invalid_request_error' };
81
+ if ( code ) err.code = code;
82
+ return { status: status, headers: JSON_H, body: { error: err } };
81
83
  };
82
84
 
83
85
  return async function handle( req ) {
@@ -102,7 +104,15 @@ function createServeHandler( opts ) {
102
104
 
103
105
  var r;
104
106
  try { r = await proxy.answer(q); }
105
- catch ( e ) { return oaiError(500, String(e && e.message || e), 'server_error'); }
107
+ catch ( e ) {
108
+ // a DELIBERATE typed refusal is not a server crash → a 4xx so OpenAI clients don't retry it as transient.
109
+ var msg = String(e && e.message || e), ec = e && e.code;
110
+ if ( ec === 'NO_REACHABLE_TIER' || /no (reachable )?tier|datapolicy|no-egress|forbidden by policy/i.test(msg) )
111
+ return oaiError(403, msg, 'policy_refusal', ec || 'no_reachable_tier');
112
+ if ( ec === 'UPSTREAM_FAILED' || /every reachable tier failed|all tiers failed|upstream/i.test(msg) )
113
+ return oaiError(502, msg, 'upstream_error', ec || 'upstream_failed');
114
+ return oaiError(500, msg, 'server_error');
115
+ }
106
116
  var m = (typeof proxy.metrics === 'function') ? proxy.metrics() : null;
107
117
  if ( typeof opts.onAnswer === 'function' )
108
118
  opts.onAnswer({ query: q, answer: r.answer, source: r.source, cached: !!r.cached, cost: r.cost });
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "skynet-graph",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "license": "AGPL-3.0-or-later",
5
5
  "main": "./lib/index.js",
6
6
  "author": "Nathanael Braun <pp9ping@gmail.com>",
7
+ "repository": { "type": "git", "url": "git+https://github.com/9pings/skynet-graph.git" },
8
+ "homepage": "https://github.com/9pings/skynet-graph#readme",
9
+ "bugs": "https://github.com/9pings/skynet-graph/issues",
7
10
  "bin": {
8
11
  "sg": "bin/sg"
9
12
  },