@bendyline/gezel-inference 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +90 -8
  2. package/dist/cache/adapter.d.ts +7 -0
  3. package/dist/cache/controller.d.ts +12 -0
  4. package/dist/cache/controller.js +24 -0
  5. package/dist/catalog/chat-models/bt/btl4-35b-q4/manifest.json +1 -0
  6. package/dist/catalog/chat-models/gl/glm-5.2-754b-q2/manifest.json +1 -0
  7. package/dist/catalog/chat-models/index.json +1 -1
  8. package/dist/catalog/chat-models/lf/lfm2.5-24b-a2b-q4/manifest.json +122 -0
  9. package/dist/catalog/chat-models/lf/lfm2.5-24b-a2b-q4/versions/1.0.0/manifest.json +15 -0
  10. package/dist/catalog/chat-models/mi/minicpm-v4.7-35b-a3b-q4/manifest.json +185 -0
  11. package/dist/catalog/chat-models/mi/minicpm-v4.7-35b-a3b-q4/versions/1.0.0/manifest.json +94 -0
  12. package/dist/catalog/chat-models/mi/minicpm5-1b-q4/manifest.json +1 -0
  13. package/dist/catalog/chat-models/mi/minicpm5-2b-q4/manifest.json +1 -0
  14. package/dist/catalog/chat-models/qw/qwen3.5-0.8b-q4/manifest.json +1 -0
  15. package/dist/catalog/chat-models/raw-index.json +1 -1
  16. package/dist/catalog/provenance.json +1 -1
  17. package/dist/engines/electron-native-reuse.d.ts +9 -0
  18. package/dist/engines/electron-native-reuse.js +146 -45
  19. package/dist/engines/native-manifest.d.ts +8 -0
  20. package/dist/engines/native-manifest.js +30 -0
  21. package/dist/http/openai-compat/apple-model.d.ts +2 -0
  22. package/dist/http/openai-compat/apple-model.js +21 -1
  23. package/dist/http/openai-compat/chat-target.d.ts +12 -0
  24. package/dist/http/openai-compat/chat-target.js +27 -1
  25. package/dist/http/routes/engines.js +9 -3
  26. package/dist/http/routes/v1-chat.js +21 -8
  27. package/dist/http/routes/v1-models.d.ts +6 -0
  28. package/dist/http/routes/v1-models.js +133 -28
  29. package/dist/http/routes/v1-responses.js +9 -2
  30. package/dist/in-flight-requests.d.ts +19 -0
  31. package/dist/in-flight-requests.js +34 -0
  32. package/dist/inference-service.js +3 -3
  33. package/dist/providers/llama-cpp/build-provider.js +33 -8
  34. package/dist/providers/llama-cpp/cache-adapter.d.ts +12 -0
  35. package/dist/providers/llama-cpp/cache-adapter.js +34 -0
  36. package/dist/providers/llama-cpp/gguf-metadata-async.js +7 -0
  37. package/dist/providers/mlx/provider.js +11 -7
  38. package/dist/providers/mlx/python/batch_processors.py +26 -0
  39. package/dist/providers/mlx/python/gezel_mlx_server.py +88 -20
  40. package/dist/providers/mlx/python/response_format.py +374 -0
  41. package/dist/providers/mlx/python/spec_decode.py +5 -0
  42. package/dist/providers/mlx/python/think_budget.py +35 -1
  43. package/dist/providers/mlx/python/tool_grammar.py +62 -3
  44. package/dist/providers/mlx/reasoning-stream.d.ts +20 -2
  45. package/dist/providers/mlx/reasoning-stream.js +77 -16
  46. package/dist/providers/mlx/runtime-diagnostics.d.ts +16 -0
  47. package/dist/providers/mlx/runtime-diagnostics.js +29 -2
  48. package/dist/providers/native/capacity-broker.d.ts +35 -0
  49. package/dist/providers/native/capacity-broker.js +69 -4
  50. package/dist/providers/native/local-engine-runtime.js +7 -2
  51. package/dist/remotes/identity.d.ts +1 -0
  52. package/dist/remotes/identity.js +6 -0
  53. package/dist/remotes/pinned-fetch.d.ts +2 -0
  54. package/dist/remotes/pinned-fetch.js +30 -1
  55. package/dist/remotes/types.d.ts +14 -0
  56. package/dist/runtime-lock.js +1 -1
  57. package/dist/system/memory.js +11 -1
  58. package/dist/utils/service-worker-entry.d.ts +4 -0
  59. package/dist/utils/service-worker-entry.js +4 -0
  60. package/package.json +14 -10
package/README.md CHANGED
@@ -1,14 +1,96 @@
1
1
  # @bendyline/gezel-inference
2
2
 
3
- The shared on-device runtime used by Gezel's full service and by embedded applications. It owns native engine lifecycle and capacity admission, model preparation, OpenAI-compatible chat, knowledge retrieval, worker entry points, and native release verification. The full service supplies product state, tools, cloud providers, UI and background work. There is one implementation of the shared runtime.
3
+ The on-device inference runtime shared by [Gezel](https://github.com/bendyline/gezel)
4
+ and by applications that embed it. It runs local AI models on the user's own
5
+ computer and answers them through OpenAI-compatible routes:
4
6
 
5
- Use the application SDK with `host: { mode: 'in-process', inferenceOnly: true }`. Install this package alongside `@bendyline/gezel-app-sdk`. Startup opens no HTTP listener and returns an authenticated Fetch transport serving `/v1/chat`, `/v1/responses`, `/v1/embeddings`, `/v1/models`, `/v1/models/ensure`, `/v1/knowledge` and the engine routes; `model: "gezel:embeddings"` runs the on-device text embedder. It does not download models. Model preparation requires an explicit request. This is the only implementation of the inference-only profile: a child-hosted `gezeld` serves the same host behind the service's loopback listener.
7
+ - **Native engines** — starting, sizing, and stopping llama.cpp, MLX, and the
8
+ other on-device engines, with admission control so models fit in memory.
9
+ - **Model management** — listing, downloading, verifying, and preparing local
10
+ chat models.
11
+ - **Embeddings and knowledge** — the on-device text embedder
12
+ (`model: "gezel:embeddings"`) and search over installed knowledge catalogs,
13
+ each in its own worker.
14
+ - **OpenAI-compatible routes** — `/v1/chat`, `/v1/responses`,
15
+ `/v1/embeddings`, `/v1/models`, `/v1/models/ensure`, `/v1/knowledge`, and
16
+ the engine routes.
6
17
 
7
- - `/packaging` stages the pinned native release for an Electron host.
8
- - `/native-release` exposes the release pins without starting inference.
9
- - `/speech` exposes the shared Kokoro frontend and dictionary resolver.
10
- - `/internal/*` is the coordinated workspace interface for Gezel's full service; external applications should use the SDK.
18
+ It holds no product state: no gezels, projects, chats, tools, cloud providers,
19
+ or UI. Those belong to the full Gezel service, `@bendyline/gezel-service`,
20
+ which uses this same runtime.
11
21
 
12
- The build copies only chat-model and knowledge-catalog metadata from the exact Gilde pin owned by the catalog package. This is generated content, never a separately edited catalog. `@bendyline/gezel-catalog` loads it through its ordinary source API. The full service separately installs all of Gilde.
22
+ ## Install
13
23
 
14
- Build with `pnpm --filter @bendyline/gezel-inference build` under the repository dependency lease. Unit and integration coverage for the moved runtime remains in the service suite; this package additionally tests the isolated host boundary.
24
+ ```bash
25
+ npm install @bendyline/gezel-app-sdk @bendyline/gezel-inference
26
+ ```
27
+
28
+ Node 24 or newer. On-device text embeddings and the optional relevance model
29
+ also need the `@huggingface/transformers` peer:
30
+
31
+ ```bash
32
+ npm install @huggingface/transformers@^4.3.1
33
+ ```
34
+
35
+ ## Use it through the app SDK
36
+
37
+ Most applications should not import this package directly. Install it beside
38
+ [`@bendyline/gezel-app-sdk`](https://www.npmjs.com/package/@bendyline/gezel-app-sdk)
39
+ and ask the SDK for an inference-only host. The SDK loads this runtime inside
40
+ your process, so there is no second process to supervise, no separate Node
41
+ executable to ship, and no loopback network connection:
42
+
43
+ ```ts
44
+ import { connectOrHost } from '@bendyline/gezel-app-sdk/host';
45
+
46
+ const gezel = await connectOrHost({
47
+ appId: 'acme.editor',
48
+ appName: 'Acme Editor',
49
+ scopes: ['openai'],
50
+ host: { mode: 'in-process', inferenceOnly: true },
51
+ });
52
+
53
+ const { chatModel } = await gezel.ensureModel({ model: 'qwen3.5-0.8b-q4' });
54
+
55
+ const reply = await gezel.openai.chat({
56
+ model: chatModel, // 'mlx:qwen3.5-0.8b-q4' on Apple Silicon, 'llama-cpp:…' elsewhere
57
+ messages: [{ role: 'user', content: 'Suggest a title for these notes: …' }],
58
+ });
59
+ console.log(reply.choices[0].message.content);
60
+
61
+ await gezel.close();
62
+ ```
63
+
64
+ The runtime never downloads a model on its own: weights are fetched only when
65
+ your app asks for them, for example with `ensureModel`. See the
66
+ [app SDK README](https://www.npmjs.com/package/@bendyline/gezel-app-sdk) for
67
+ consent, hosting options, shipping engines and models with your app, and
68
+ knowledge catalogs.
69
+
70
+ ## Entry points
71
+
72
+ | Import | What it provides |
73
+ |---|---|
74
+ | `@bendyline/gezel-inference` | `startService()`, the in-process host the app SDK calls, and `verifyNativeBinaries()` |
75
+ | `@bendyline/gezel-inference/native-release` | The pinned native engine release and its archive digests, as data. Importing it starts nothing |
76
+ | `@bendyline/gezel-inference/packaging` | Build-time helpers that stage the pinned native engines for an Electron app |
77
+ | `@bendyline/gezel-inference/speech` | The Kokoro text-to-speech frontend and its pronunciation-dictionary lookup |
78
+
79
+ The package also includes chat-model and knowledge-catalog metadata generated
80
+ from the pinned [`@bendyline/gilde`](https://www.npmjs.com/package/@bendyline/gilde)
81
+ catalog. The pronunciation dictionaries' license is at
82
+ `dist/kokoro-lexicon/LICENSE`.
83
+
84
+ ## Stability
85
+
86
+ The entry points above are public API under semver.
87
+ `@bendyline/gezel-inference/internal/*` is not: it is how the Gezel service
88
+ reaches the runtime, always installed at exactly the same version, and it can
89
+ change in any release. Do not import it.
90
+
91
+ ## Documentation
92
+
93
+ - [Repository and full documentation](https://github.com/bendyline/gezel)
94
+ - [Embedding guide](https://github.com/bendyline/gezel/blob/main/docs/embedding-sdk.md)
95
+
96
+ MIT © Bendyline
@@ -45,6 +45,13 @@ export interface EngineCacheAdapter {
45
45
  * gone for accounting purposes either way.
46
46
  */
47
47
  evict(sessionIds: readonly string[]): Promise<void>;
48
+ /**
49
+ * This session will never be resumed (a one-shot `gezel run`): keep only
50
+ * what helps other sessions, such as a shared prompt-prefix snapshot, and
51
+ * never a resumable snapshot of its own. Optional — an engine that keeps
52
+ * nothing on disk can ignore it. Must not do I/O.
53
+ */
54
+ markEphemeral?(sessionId: string): void;
48
55
  /**
49
56
  * Snapshot of what the engine currently has cached. Polled by the
50
57
  * controller every few seconds for telemetry; also informs the
@@ -97,6 +97,12 @@ export declare class SessionCacheController {
97
97
  * `setBudget` after construction.
98
98
  */
99
99
  private readonly pendingBudgets;
100
+ /**
101
+ * One-shot sessions, remembered so an adapter registered after the session
102
+ * was created (engines start lazily, on the first turn) still learns them.
103
+ * Bounded: only a session's own turns need the mark.
104
+ */
105
+ private readonly ephemeralSessions;
100
106
  private readonly now;
101
107
  private readonly logger;
102
108
  private reconcileTimer?;
@@ -109,6 +115,12 @@ export declare class SessionCacheController {
109
115
  * sets a per-engine RAM-aware default via `setBudget`.
110
116
  */
111
117
  registerAdapter(adapter: EngineCacheAdapter): void;
118
+ /**
119
+ * A session that will never be resumed keeps no resumable cache of its own
120
+ * on disk. Every one-shot `gezel run` left a ~300 MB llama slot snapshot
121
+ * that nothing ever read (2026-10-15 npm audit).
122
+ */
123
+ markEphemeral(sessionId: string): void;
112
124
  /**
113
125
  * Set the memory budget for a provider. Operator-tunable; Phase 3
114
126
  * exposes this in the Settings UI. Eviction kicks in immediately if
@@ -43,6 +43,7 @@ function emptyHitsBySource() {
43
43
  return { memory: 0, disk: 0, prefix: 0, fresh: 0 };
44
44
  }
45
45
  const RECENT_TURN_WINDOW = 50;
46
+ const MAX_REMEMBERED_EPHEMERAL_SESSIONS = 1024;
46
47
  export class SessionCacheController {
47
48
  providers = new Map();
48
49
  /**
@@ -55,6 +56,12 @@ export class SessionCacheController {
55
56
  * `setBudget` after construction.
56
57
  */
57
58
  pendingBudgets = new Map();
59
+ /**
60
+ * One-shot sessions, remembered so an adapter registered after the session
61
+ * was created (engines start lazily, on the first turn) still learns them.
62
+ * Bounded: only a session's own turns need the mark.
63
+ */
64
+ ephemeralSessions = new Set();
58
65
  now;
59
66
  logger;
60
67
  reconcileTimer;
@@ -93,6 +100,23 @@ export class SessionCacheController {
93
100
  gezelOutcomes: new Map(),
94
101
  recentOutcomes: [],
95
102
  });
103
+ for (const sessionId of this.ephemeralSessions)
104
+ adapter.markEphemeral?.(sessionId);
105
+ }
106
+ /**
107
+ * A session that will never be resumed keeps no resumable cache of its own
108
+ * on disk. Every one-shot `gezel run` left a ~300 MB llama slot snapshot
109
+ * that nothing ever read (2026-10-15 npm audit).
110
+ */
111
+ markEphemeral(sessionId) {
112
+ this.ephemeralSessions.add(sessionId);
113
+ if (this.ephemeralSessions.size > MAX_REMEMBERED_EPHEMERAL_SESSIONS) {
114
+ const oldest = this.ephemeralSessions.values().next().value;
115
+ if (oldest !== undefined)
116
+ this.ephemeralSessions.delete(oldest);
117
+ }
118
+ for (const state of this.providers.values())
119
+ state.adapter.markEphemeral?.(sessionId);
96
120
  }
97
121
  /**
98
122
  * Set the memory budget for a provider. Operator-tunable; Phase 3
@@ -37,6 +37,7 @@
37
37
  "toolCallFormat": "function-call"
38
38
  },
39
39
  "behaviors": [
40
+ "provider.merge-system-messages",
40
41
  "reasoning.strip-think-tags",
41
42
  "prompt.private-reasoning-guidance",
42
43
  "prompt.tool-cookbook-condensed",
@@ -26,6 +26,7 @@
26
26
  "version": "1.0.0",
27
27
  "updatedAt": "2026-07-25T00:00:00Z",
28
28
  "license": "MIT",
29
+ "licenseClass": "open",
29
30
  "parameterSize": "754B",
30
31
  "approxSizeBytes": 211075856448,
31
32
  "supportsTools": true,