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 +55 -47
- package/doc/CAPABILITIES.md +5 -5
- package/lib/providers/llm-local.js +6 -4
- package/lib/providers/local-host.js +12 -5
- package/lib/sg/serve.js +13 -3
- package/package.json +4 -1
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&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
|
|
25
|
+
## What it is
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
with
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- **
|
|
31
|
-
|
|
32
|
-
- **Task memory that reopens** — a late correction
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
several things at once. This library puts them in front of one surface at a time.
|
|
79
|
+
---
|
|
71
80
|
|
|
72
|
-
|
|
73
|
-
replayable without a model · **[PoC]** = an accounting demonstration, not a benchmark.
|
|
81
|
+
## Capabilities & maturity
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
|
175
|
-
>
|
|
176
|
-
>
|
|
177
|
-
>
|
|
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 …`).
|
package/doc/CAPABILITIES.md
CHANGED
|
@@ -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` |
|
|
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
|
-
[
|
|
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
|
|
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
|
|
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
|
-
|
|
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`)
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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) : '')
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 ) {
|
|
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.
|
|
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
|
},
|