@graphorin/provider-llamacpp-node 0.5.0 → 0.6.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 CHANGED
@@ -1,5 +1,31 @@
1
1
  # @graphorin/provider-llamacpp-node
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#135](https://github.com/o-stepper/graphorin/pull/135) [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3) Thanks [@o-stepper](https://github.com/o-stepper)! - Provider adapters now match their real SDK / wire contracts (audit 2026-07-04 Wave B, cluster B1).
8
+
9
+ - vercel adapter: Graphorin tools convert to the AI SDK's name-keyed record with `jsonSchema()`-shaped input schemas; assistant `toolCalls` become `tool-call` content parts, `ToolMessage`s become `tool-result` messages, system-role messages hoist into the `system` option, and `toolChoice` maps onto the SDK spelling. Tool loops now run against the real `ai` peer (previously every tool conversation failed SDK validation). A real-SDK contract test suite (dev-only `ai` dependency) pins the shapes.
10
+ - Anthropic token counter posts Anthropic wire-shaped bodies (system hoist, `tool_use` / `tool_result` blocks, turn merging) instead of raw Graphorin messages that 400'd on any agent transcript; degradation to the tiktoken fallback now WARNs once.
11
+ - HTTP errors carry a canonical `errorKind` (shared `classifyHttpStatus` mapper: 429 rate-limit, 401/403 unauthorized, 5xx transient/capacity, context-length body sniff) plus captured `retry-after` / `x-ratelimit-*` headers; `withFallback` / `withRetry` consult them, so a 429 on the primary finally fails over and honours server-provided delays.
12
+ - llamacpp-node: the system prompt is no longer injected twice, per-request contexts/sequences are disposed after every stream (KV-cache leak), and aborted streams report `finishReason: 'aborted'`.
13
+ - OpenAI-compatible streaming sends `stream_options: { include_usage: true }` so vLLM / Together / OpenAI report real usage; `openAICompatibleAdapter` gained the `capabilities` / `timeoutMs` options its siblings had.
14
+ - `withCostTracking` bills separately-reported reasoning tokens at the output rate; classifiers recognise Bedrock cross-region ids (`us.anthropic.claude-...`) and the AI SDK's dotted provider ids.
15
+
16
+ - Updated dependencies [[`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627)]:
17
+ - @graphorin/core@0.6.0
18
+ - @graphorin/provider@0.6.0
19
+
20
+ ## 0.5.0
21
+
22
+ First version published to the npm registry (with Sigstore build
23
+ provenance). The 0.2.0, 0.3.0, and 0.4.0 versions were internal lockstep
24
+ milestones and were never published. All `@graphorin/*` packages release
25
+ lockstep at the same version; the full release notes for 0.2.0-0.5.0 live
26
+ in the repository-level
27
+ [CHANGELOG](https://github.com/o-stepper/graphorin/blob/main/CHANGELOG.md).
28
+
3
29
  ## 0.1.0
4
30
 
5
31
  Initial release. See the root `CHANGELOG.md` and the corresponding
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # `@graphorin/provider-llamacpp-node`
2
2
 
3
3
  > Companion package to
4
- > [`@graphorin/provider`](../provider/README.md) in-process GGUF
4
+ > [`@graphorin/provider`](../provider/README.md) - in-process GGUF
5
5
  > execution for the
6
6
  > [**Graphorin**](https://github.com/o-stepper/graphorin) framework.
7
7
 
@@ -50,14 +50,14 @@ tighter than the cl100k_base proxy used by the HTTP-shaped adapters.
50
50
  ## HITL durable-resume tradeoff
51
51
 
52
52
  The in-process adapter does **not** survive a process restart
53
- mid-stream the model context lives in the running process and is
53
+ mid-stream - the model context lives in the running process and is
54
54
  lost on exit. For human-in-the-loop workflows that need durable
55
55
  mid-stream resume across restarts, prefer one of the HTTP-shaped
56
56
  adapters instead:
57
57
 
58
- - `ollamaAdapter` Ollama HTTP daemon
59
- - `llamaCppServerAdapter` upstream `llama-server` binary
60
- - `openAICompatibleAdapter` LMStudio / LocalAI / vLLM /
58
+ - `ollamaAdapter` - Ollama HTTP daemon
59
+ - `llamaCppServerAdapter` - upstream `llama-server` binary
60
+ - `openAICompatibleAdapter` - LMStudio / LocalAI / vLLM /
61
61
  Together-style endpoints
62
62
 
63
63
  ## GGUF model provenance
@@ -78,5 +78,5 @@ discipline rather than enforcing it at runtime.
78
78
 
79
79
  ## Project metadata
80
80
 
81
- - **Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko
81
+ - **Project Graphorin** · v0.6.0 · MIT License · © 2026 Oleksiy Stepurenko
82
82
  - Repository: <https://github.com/o-stepper/graphorin>
package/dist/adapter.js CHANGED
@@ -4,7 +4,7 @@ import { applyReasoningPolicy, resolveReasoningRetention } from "@graphorin/prov
4
4
 
5
5
  //#region src/adapter.ts
6
6
  /**
7
- * Default sensitivity envelope for the in-process adapter same as
7
+ * Default sensitivity envelope for the in-process adapter - same as
8
8
  * the loopback HTTP path.
9
9
  *
10
10
  * @stable
@@ -122,19 +122,27 @@ async function* streamLlamaCppNode(ensureModel, sessionFactory, providerName, op
122
122
  };
123
123
  let completionTokens = 0;
124
124
  let errored = false;
125
+ let aborted = false;
125
126
  try {
126
127
  const streamOptions = {};
127
128
  if (req.signal !== void 0) streamOptions.signal = req.signal;
128
129
  if (req.maxTokens !== void 0) streamOptions.maxTokens = req.maxTokens;
129
130
  if (req.temperature !== void 0) streamOptions.temperature = req.temperature;
130
131
  for await (const piece of session.promptStreamingResponse(prompt, streamOptions)) {
132
+ if (req.signal?.aborted) {
133
+ aborted = true;
134
+ break;
135
+ }
131
136
  if (typeof piece !== "string" || piece.length === 0) continue;
132
137
  completionTokens += lengthOf(model.tokenize(piece));
133
138
  yield {
134
139
  type: "text-delta",
135
140
  delta: piece
136
141
  };
137
- if (req.signal?.aborted) break;
142
+ if (req.signal?.aborted) {
143
+ aborted = true;
144
+ break;
145
+ }
138
146
  }
139
147
  } catch (err) {
140
148
  errored = true;
@@ -145,10 +153,14 @@ async function* streamLlamaCppNode(ensureModel, sessionFactory, providerName, op
145
153
  message: err.message
146
154
  }
147
155
  };
156
+ } finally {
157
+ try {
158
+ session.dispose?.();
159
+ } catch {}
148
160
  }
149
161
  yield {
150
162
  type: "finish",
151
- finishReason: errored ? "error" : "stop",
163
+ finishReason: errored ? "error" : aborted ? "aborted" : "stop",
152
164
  usage: {
153
165
  promptTokens,
154
166
  completionTokens,
@@ -170,13 +182,24 @@ async function resolveModel(options) {
170
182
  * `promptStreamingResponse` AsyncIterable contract.
171
183
  */
172
184
  async function defaultSessionFactory(model, system, options) {
173
- const session = new (await (loadLlamaChatSessionCtor(options.runtimeOverrides)))({
174
- contextSequence: (await model.createContext(options.contextSize !== void 0 ? { contextSize: options.contextSize } : void 0)).getSequence(),
185
+ const ChatSession = await loadLlamaChatSessionCtor(options.runtimeOverrides);
186
+ const context = await model.createContext(options.contextSize !== void 0 ? { contextSize: options.contextSize } : void 0);
187
+ const sequence = context.getSequence();
188
+ const session = new ChatSession({
189
+ contextSequence: sequence,
175
190
  ...system !== void 0 ? { systemPrompt: system } : {}
176
191
  });
177
- return { promptStreamingResponse(prompt, streamOptions) {
178
- return promptToIterable(session, prompt, streamOptions);
179
- } };
192
+ return {
193
+ promptStreamingResponse(prompt, streamOptions) {
194
+ return promptToIterable(session, prompt, streamOptions);
195
+ },
196
+ dispose() {
197
+ try {
198
+ sequence.dispose?.();
199
+ } catch {}
200
+ context.dispose?.();
201
+ }
202
+ };
180
203
  }
181
204
  /**
182
205
  * Bridge the peer's callback-streaming `prompt(...)` into the
@@ -234,7 +257,6 @@ function promptToIterable(session, prompt, streamOptions) {
234
257
  }
235
258
  function renderPrompt(req) {
236
259
  const parts = [];
237
- if (req.systemMessage !== void 0) parts.push(`[system] ${req.systemMessage}`);
238
260
  for (const msg of req.messages) {
239
261
  const text = typeof msg.content === "string" ? msg.content : msg.content.map((p) => p.type === "text" ? p.text : p.type === "reasoning" ? p.text : "").join("");
240
262
  parts.push(`[${msg.role}] ${text}`);
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","names":["LLAMA_CPP_NODE_ACCEPTS_SENSITIVITY: ReadonlyArray<Sensitivity>","DEFAULT_CAPABILITIES: ProviderCapabilities","capabilities: ProviderCapabilities","resolved: { model: LlamaModelInstance } | null","resolving: Promise<{ model: LlamaModelInstance }> | null","collected: string[]","usage: Usage","finishReason: ProviderResponse['finishReason']","streamError: { message: string } | undefined","streamOptions: { signal?: AbortSignal; maxTokens?: number; temperature?: number }","queue: string[]","failure: unknown","wake: (() => void) | null","parts: string[]"],"sources":["../src/adapter.ts"],"sourcesContent":["/**\n * `llamaCppNodeAdapter` — in-process GGUF adapter built on\n * `node-llama-cpp@^3.5`. The adapter declares `trust: 'loopback'`\n * permanently because the model lives in the same trust boundary as\n * the host process.\n *\n * @packageDocumentation\n */\n\nimport type {\n Provider,\n ProviderCapabilities,\n ProviderEvent,\n ProviderRequest,\n ProviderResponse,\n Sensitivity,\n Usage,\n} from '@graphorin/core';\nimport { ProviderHttpError } from '@graphorin/provider/errors';\nimport { applyReasoningPolicy, resolveReasoningRetention } from '@graphorin/provider/reasoning';\nimport {\n type LlamaChatSessionPeer,\n type LlamaCppNodeRuntimeOverrides,\n type LlamaModelInstance,\n type LlamaSessionInstance,\n loadLlamaChatSessionCtor,\n loadLlamaModule,\n} from './runtime.js';\n\n/**\n * Default sensitivity envelope for the in-process adapter — same as\n * the loopback HTTP path.\n *\n * @stable\n */\nexport const LLAMA_CPP_NODE_ACCEPTS_SENSITIVITY: ReadonlyArray<Sensitivity> = Object.freeze([\n 'public',\n 'internal',\n 'secret',\n]);\n\n/**\n * Options accepted by {@link llamaCppNodeAdapter}.\n *\n * @stable\n */\nexport interface LlamaCppNodeAdapterOptions {\n /** Filesystem path to the `.gguf` model file. */\n readonly modelPath: string;\n /** Number of layers to offload to the GPU. Default `'auto'`. */\n readonly gpuLayers?: number | 'auto';\n /** Optional context-window override. */\n readonly contextSize?: number;\n /** Provider name attached to spans / log lines. */\n readonly name?: string;\n /** Capability declaration. Merged on top of the defaults table. */\n readonly capabilities?: Partial<ProviderCapabilities>;\n /** Sensitivity override (defaults to the loopback envelope). */\n readonly acceptsSensitivity?: ReadonlyArray<Sensitivity>;\n /**\n * Test-only runtime override. When unset the adapter loads\n * `node-llama-cpp` lazily on first call.\n */\n readonly runtimeOverrides?: LlamaCppNodeRuntimeOverrides;\n /**\n * Optional `model` override that short-circuits the\n * `loadLlamaModule(...).loadModel(...)` flow. Tests pass a fixture\n * shaped instance.\n */\n readonly modelOverride?: LlamaModelInstance;\n /**\n * Optional session factory override. When unset, the adapter builds a\n * real session from the peer (PS-3): `model.createContext()` →\n * `new LlamaChatSession({ contextSequence })`, streaming through\n * `prompt(text, { onTextChunk })`. Overrides\n * (`runtimeOverrides.createSession` or this option) keep the test\n * seam.\n */\n readonly sessionFactory?: (\n model: LlamaModelInstance,\n system?: string,\n ) => Promise<LlamaSessionInstance>;\n}\n\nconst DEFAULT_CAPABILITIES: ProviderCapabilities = {\n streaming: true,\n toolCalling: false,\n parallelToolCalls: false,\n multimodal: false,\n structuredOutput: false,\n reasoning: false,\n contextWindow: 8_192,\n maxOutput: 4_096,\n reasoningContract: 'optional',\n};\n\n/**\n * Build a Graphorin {@link Provider} backed by an in-process GGUF\n * model. The first call lazily loads the `node-llama-cpp` peer + the\n * model file; subsequent calls reuse the cached instances.\n *\n * @stable\n */\nexport function llamaCppNodeAdapter(options: LlamaCppNodeAdapterOptions): Provider {\n const providerName = options.name ?? `llama-cpp-node-${basename(options.modelPath)}`;\n const capabilities: ProviderCapabilities = {\n ...DEFAULT_CAPABILITIES,\n ...options.capabilities,\n };\n const acceptsSensitivity = options.acceptsSensitivity ?? LLAMA_CPP_NODE_ACCEPTS_SENSITIVITY;\n let resolved: { model: LlamaModelInstance } | null = null;\n let resolving: Promise<{ model: LlamaModelInstance }> | null = null;\n const ensureModel = async (): Promise<{ model: LlamaModelInstance }> => {\n if (resolved !== null) return resolved;\n if (resolving !== null) return resolving;\n resolving = (async () => {\n const model = options.modelOverride ?? (await resolveModel(options));\n resolved = { model };\n resolving = null;\n return resolved;\n })();\n return resolving;\n };\n const sessionFactory =\n options.sessionFactory ??\n options.runtimeOverrides?.createSession ??\n ((model: LlamaModelInstance, system?: string) => defaultSessionFactory(model, system, options));\n return {\n name: providerName,\n modelId: options.modelPath,\n capabilities,\n acceptsSensitivity,\n stream(req) {\n return streamLlamaCppNode(\n ensureModel,\n sessionFactory,\n providerName,\n options,\n applyLlamaCppNodePreflight(req, capabilities),\n );\n },\n async generate(req) {\n const events = streamLlamaCppNode(\n ensureModel,\n sessionFactory,\n providerName,\n options,\n applyLlamaCppNodePreflight(req, capabilities),\n );\n const collected: string[] = [];\n let usage: Usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n let finishReason: ProviderResponse['finishReason'] = 'stop';\n let streamError: { message: string } | undefined;\n for await (const event of events) {\n if (event.type === 'text-delta') collected.push(event.delta);\n if (event.type === 'error') streamError = event.error;\n if (event.type === 'finish') {\n usage = event.usage;\n finishReason = event.finishReason;\n }\n }\n // PS-4: a swallowed mid-stream error returned truncated text\n // indistinguishable from success — and a never-throwing\n // generate() bypasses withRetry / withFallback entirely.\n if (streamError !== undefined) {\n throw new ProviderHttpError({\n providerName,\n status: 0,\n message: streamError.message,\n });\n }\n return {\n text: collected.join(''),\n usage,\n finishReason,\n };\n },\n };\n}\n\nfunction applyLlamaCppNodePreflight(\n req: ProviderRequest,\n capabilities: ProviderCapabilities,\n): ProviderRequest {\n const retention = resolveReasoningRetention({\n ...(req.reasoningRetention !== undefined ? { requested: req.reasoningRetention } : {}),\n ...(capabilities.reasoningContract !== undefined\n ? { contract: capabilities.reasoningContract }\n : {}),\n });\n if (retention === 'pass-through-all') return req;\n const filtered = applyReasoningPolicy({ messages: req.messages, retention });\n if (filtered === req.messages) return req;\n return { ...req, messages: filtered };\n}\n\nasync function* streamLlamaCppNode(\n ensureModel: () => Promise<{ model: LlamaModelInstance }>,\n sessionFactory: (model: LlamaModelInstance, system?: string) => Promise<LlamaSessionInstance>,\n providerName: string,\n options: LlamaCppNodeAdapterOptions,\n req: ProviderRequest,\n): AsyncIterable<ProviderEvent> {\n const { model } = await ensureModel();\n const session = await sessionFactory(\n model,\n typeof req.systemMessage === 'string' ? req.systemMessage : undefined,\n );\n const prompt = renderPrompt(req);\n const promptTokens = lengthOf(model.tokenize(prompt));\n yield {\n type: 'stream-start',\n metadata: {\n providerName,\n modelId: options.modelPath,\n createdAt: new Date().toISOString(),\n },\n };\n let completionTokens = 0;\n let errored = false;\n try {\n const streamOptions: { signal?: AbortSignal; maxTokens?: number; temperature?: number } = {};\n if (req.signal !== undefined) streamOptions.signal = req.signal;\n if (req.maxTokens !== undefined) streamOptions.maxTokens = req.maxTokens;\n if (req.temperature !== undefined) streamOptions.temperature = req.temperature;\n for await (const piece of session.promptStreamingResponse(prompt, streamOptions)) {\n if (typeof piece !== 'string' || piece.length === 0) continue;\n completionTokens += lengthOf(model.tokenize(piece));\n yield { type: 'text-delta', delta: piece };\n if (req.signal?.aborted) break;\n }\n } catch (err) {\n errored = true;\n yield { type: 'error', error: { kind: 'unknown', message: (err as Error).message } };\n }\n yield {\n type: 'finish',\n // PS-4: a mid-stream failure must not masquerade as a clean stop —\n // partial text would be indistinguishable from success.\n finishReason: errored ? 'error' : 'stop',\n usage: {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n },\n };\n}\n\nasync function resolveModel(options: LlamaCppNodeAdapterOptions): Promise<LlamaModelInstance> {\n const llama = await loadLlamaModule(options.runtimeOverrides);\n return llama.loadModel({\n modelPath: options.modelPath,\n ...(options.gpuLayers !== undefined ? { gpuLayers: options.gpuLayers } : {}),\n });\n}\n\n/**\n * The REAL default session factory (PS-3): `model.createContext()` →\n * `context.getSequence()` → `new LlamaChatSession({ contextSequence })`\n * from the lazily-loaded peer, adapting its callback-streaming\n * `prompt(text, { onTextChunk })` to the adapter's\n * `promptStreamingResponse` AsyncIterable contract.\n */\nasync function defaultSessionFactory(\n model: LlamaModelInstance,\n system: string | undefined,\n options: LlamaCppNodeAdapterOptions,\n): Promise<LlamaSessionInstance> {\n const ChatSession = await loadLlamaChatSessionCtor(options.runtimeOverrides);\n const context = await model.createContext(\n options.contextSize !== undefined ? { contextSize: options.contextSize } : undefined,\n );\n const sequence = context.getSequence();\n const session = new ChatSession({\n contextSequence: sequence,\n ...(system !== undefined ? { systemPrompt: system } : {}),\n });\n return {\n promptStreamingResponse(prompt, streamOptions) {\n return promptToIterable(session, prompt, streamOptions);\n },\n };\n}\n\n/**\n * Bridge the peer's callback-streaming `prompt(...)` into the\n * AsyncIterable the adapter consumes. A rejection from `prompt`\n * rejects the pending/next iteration so the stream's error path\n * (PS-4) observes it.\n */\nfunction promptToIterable(\n session: LlamaChatSessionPeer,\n prompt: string,\n streamOptions?: {\n readonly signal?: AbortSignal;\n readonly maxTokens?: number;\n readonly temperature?: number;\n },\n): AsyncIterable<string> {\n const queue: string[] = [];\n let done = false;\n let failure: unknown;\n let wake: (() => void) | null = null;\n const notify = (): void => {\n if (wake !== null) {\n const w = wake;\n wake = null;\n w();\n }\n };\n void session\n .prompt(prompt, {\n ...(streamOptions?.signal !== undefined ? { signal: streamOptions.signal } : {}),\n ...(streamOptions?.maxTokens !== undefined ? { maxTokens: streamOptions.maxTokens } : {}),\n ...(streamOptions?.temperature !== undefined\n ? { temperature: streamOptions.temperature }\n : {}),\n onTextChunk: (chunk: string) => {\n if (typeof chunk === 'string' && chunk.length > 0) queue.push(chunk);\n notify();\n },\n })\n .then(\n () => {\n done = true;\n notify();\n },\n (err: unknown) => {\n failure = err instanceof Error ? err : new Error(String(err));\n done = true;\n notify();\n },\n );\n return {\n [Symbol.asyncIterator](): AsyncIterator<string> {\n return {\n async next(): Promise<IteratorResult<string>> {\n for (;;) {\n const head = queue.shift();\n if (head !== undefined) return { done: false, value: head };\n if (failure !== undefined) throw failure;\n if (done) return { done: true, value: undefined };\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n },\n };\n },\n };\n}\n\nfunction renderPrompt(req: ProviderRequest): string {\n const parts: string[] = [];\n if (req.systemMessage !== undefined) parts.push(`[system] ${req.systemMessage}`);\n for (const msg of req.messages) {\n const text =\n typeof msg.content === 'string'\n ? msg.content\n : msg.content\n .map((p) => (p.type === 'text' ? p.text : p.type === 'reasoning' ? p.text : ''))\n .join('');\n parts.push(`[${msg.role}] ${text}`);\n }\n return parts.join('\\n');\n}\n\nfunction basename(path: string): string {\n const idx = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\\\'));\n return idx === -1 ? path : path.slice(idx + 1);\n}\n\nfunction lengthOf(tokens: readonly number[] | Uint32Array | Uint8Array | null | undefined): number {\n if (tokens === null || tokens === undefined) return 0;\n return (tokens as { length?: number }).length ?? 0;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"adapter.js","names":["LLAMA_CPP_NODE_ACCEPTS_SENSITIVITY: ReadonlyArray<Sensitivity>","DEFAULT_CAPABILITIES: ProviderCapabilities","capabilities: ProviderCapabilities","resolved: { model: LlamaModelInstance } | null","resolving: Promise<{ model: LlamaModelInstance }> | null","collected: string[]","usage: Usage","finishReason: ProviderResponse['finishReason']","streamError: { message: string } | undefined","streamOptions: { signal?: AbortSignal; maxTokens?: number; temperature?: number }","queue: string[]","failure: unknown","wake: (() => void) | null","parts: string[]"],"sources":["../src/adapter.ts"],"sourcesContent":["/**\n * `llamaCppNodeAdapter` - in-process GGUF adapter built on\n * `node-llama-cpp@^3.5`. The adapter declares `trust: 'loopback'`\n * permanently because the model lives in the same trust boundary as\n * the host process.\n *\n * @packageDocumentation\n */\n\nimport type {\n Provider,\n ProviderCapabilities,\n ProviderEvent,\n ProviderRequest,\n ProviderResponse,\n Sensitivity,\n Usage,\n} from '@graphorin/core';\nimport { ProviderHttpError } from '@graphorin/provider/errors';\nimport { applyReasoningPolicy, resolveReasoningRetention } from '@graphorin/provider/reasoning';\nimport {\n type LlamaChatSessionPeer,\n type LlamaCppNodeRuntimeOverrides,\n type LlamaModelInstance,\n type LlamaSessionInstance,\n loadLlamaChatSessionCtor,\n loadLlamaModule,\n} from './runtime.js';\n\n/**\n * Default sensitivity envelope for the in-process adapter - same as\n * the loopback HTTP path.\n *\n * @stable\n */\nexport const LLAMA_CPP_NODE_ACCEPTS_SENSITIVITY: ReadonlyArray<Sensitivity> = Object.freeze([\n 'public',\n 'internal',\n 'secret',\n]);\n\n/**\n * Options accepted by {@link llamaCppNodeAdapter}.\n *\n * @stable\n */\nexport interface LlamaCppNodeAdapterOptions {\n /** Filesystem path to the `.gguf` model file. */\n readonly modelPath: string;\n /** Number of layers to offload to the GPU. Default `'auto'`. */\n readonly gpuLayers?: number | 'auto';\n /** Optional context-window override. */\n readonly contextSize?: number;\n /** Provider name attached to spans / log lines. */\n readonly name?: string;\n /** Capability declaration. Merged on top of the defaults table. */\n readonly capabilities?: Partial<ProviderCapabilities>;\n /** Sensitivity override (defaults to the loopback envelope). */\n readonly acceptsSensitivity?: ReadonlyArray<Sensitivity>;\n /**\n * Test-only runtime override. When unset the adapter loads\n * `node-llama-cpp` lazily on first call.\n */\n readonly runtimeOverrides?: LlamaCppNodeRuntimeOverrides;\n /**\n * Optional `model` override that short-circuits the\n * `loadLlamaModule(...).loadModel(...)` flow. Tests pass a fixture\n * shaped instance.\n */\n readonly modelOverride?: LlamaModelInstance;\n /**\n * Optional session factory override. When unset, the adapter builds a\n * real session from the peer (PS-3): `model.createContext()` →\n * `new LlamaChatSession({ contextSequence })`, streaming through\n * `prompt(text, { onTextChunk })`. Overrides\n * (`runtimeOverrides.createSession` or this option) keep the test\n * seam.\n */\n readonly sessionFactory?: (\n model: LlamaModelInstance,\n system?: string,\n ) => Promise<LlamaSessionInstance>;\n}\n\nconst DEFAULT_CAPABILITIES: ProviderCapabilities = {\n streaming: true,\n toolCalling: false,\n parallelToolCalls: false,\n multimodal: false,\n structuredOutput: false,\n reasoning: false,\n contextWindow: 8_192,\n maxOutput: 4_096,\n reasoningContract: 'optional',\n};\n\n/**\n * Build a Graphorin {@link Provider} backed by an in-process GGUF\n * model. The first call lazily loads the `node-llama-cpp` peer + the\n * model file; subsequent calls reuse the cached instances.\n *\n * @stable\n */\nexport function llamaCppNodeAdapter(options: LlamaCppNodeAdapterOptions): Provider {\n const providerName = options.name ?? `llama-cpp-node-${basename(options.modelPath)}`;\n const capabilities: ProviderCapabilities = {\n ...DEFAULT_CAPABILITIES,\n ...options.capabilities,\n };\n const acceptsSensitivity = options.acceptsSensitivity ?? LLAMA_CPP_NODE_ACCEPTS_SENSITIVITY;\n let resolved: { model: LlamaModelInstance } | null = null;\n let resolving: Promise<{ model: LlamaModelInstance }> | null = null;\n const ensureModel = async (): Promise<{ model: LlamaModelInstance }> => {\n if (resolved !== null) return resolved;\n if (resolving !== null) return resolving;\n resolving = (async () => {\n const model = options.modelOverride ?? (await resolveModel(options));\n resolved = { model };\n resolving = null;\n return resolved;\n })();\n return resolving;\n };\n const sessionFactory =\n options.sessionFactory ??\n options.runtimeOverrides?.createSession ??\n ((model: LlamaModelInstance, system?: string) => defaultSessionFactory(model, system, options));\n return {\n name: providerName,\n modelId: options.modelPath,\n capabilities,\n acceptsSensitivity,\n stream(req) {\n return streamLlamaCppNode(\n ensureModel,\n sessionFactory,\n providerName,\n options,\n applyLlamaCppNodePreflight(req, capabilities),\n );\n },\n async generate(req) {\n const events = streamLlamaCppNode(\n ensureModel,\n sessionFactory,\n providerName,\n options,\n applyLlamaCppNodePreflight(req, capabilities),\n );\n const collected: string[] = [];\n let usage: Usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n let finishReason: ProviderResponse['finishReason'] = 'stop';\n let streamError: { message: string } | undefined;\n for await (const event of events) {\n if (event.type === 'text-delta') collected.push(event.delta);\n if (event.type === 'error') streamError = event.error;\n if (event.type === 'finish') {\n usage = event.usage;\n finishReason = event.finishReason;\n }\n }\n // PS-4: a swallowed mid-stream error returned truncated text\n // indistinguishable from success - and a never-throwing\n // generate() bypasses withRetry / withFallback entirely.\n if (streamError !== undefined) {\n throw new ProviderHttpError({\n providerName,\n status: 0,\n message: streamError.message,\n });\n }\n return {\n text: collected.join(''),\n usage,\n finishReason,\n };\n },\n };\n}\n\nfunction applyLlamaCppNodePreflight(\n req: ProviderRequest,\n capabilities: ProviderCapabilities,\n): ProviderRequest {\n const retention = resolveReasoningRetention({\n ...(req.reasoningRetention !== undefined ? { requested: req.reasoningRetention } : {}),\n ...(capabilities.reasoningContract !== undefined\n ? { contract: capabilities.reasoningContract }\n : {}),\n });\n if (retention === 'pass-through-all') return req;\n const filtered = applyReasoningPolicy({ messages: req.messages, retention });\n if (filtered === req.messages) return req;\n return { ...req, messages: filtered };\n}\n\nasync function* streamLlamaCppNode(\n ensureModel: () => Promise<{ model: LlamaModelInstance }>,\n sessionFactory: (model: LlamaModelInstance, system?: string) => Promise<LlamaSessionInstance>,\n providerName: string,\n options: LlamaCppNodeAdapterOptions,\n req: ProviderRequest,\n): AsyncIterable<ProviderEvent> {\n const { model } = await ensureModel();\n const session = await sessionFactory(\n model,\n typeof req.systemMessage === 'string' ? req.systemMessage : undefined,\n );\n const prompt = renderPrompt(req);\n const promptTokens = lengthOf(model.tokenize(prompt));\n yield {\n type: 'stream-start',\n metadata: {\n providerName,\n modelId: options.modelPath,\n createdAt: new Date().toISOString(),\n },\n };\n let completionTokens = 0;\n let errored = false;\n let aborted = false;\n try {\n const streamOptions: { signal?: AbortSignal; maxTokens?: number; temperature?: number } = {};\n if (req.signal !== undefined) streamOptions.signal = req.signal;\n if (req.maxTokens !== undefined) streamOptions.maxTokens = req.maxTokens;\n if (req.temperature !== undefined) streamOptions.temperature = req.temperature;\n for await (const piece of session.promptStreamingResponse(prompt, streamOptions)) {\n if (req.signal?.aborted) {\n aborted = true;\n break;\n }\n if (typeof piece !== 'string' || piece.length === 0) continue;\n completionTokens += lengthOf(model.tokenize(piece));\n yield { type: 'text-delta', delta: piece };\n if (req.signal?.aborted) {\n aborted = true;\n break;\n }\n }\n } catch (err) {\n errored = true;\n yield { type: 'error', error: { kind: 'unknown', message: (err as Error).message } };\n } finally {\n // Release the per-request context / KV cache (core-provider-08).\n try {\n session.dispose?.();\n } catch {\n // Disposal failures must never mask the stream outcome.\n }\n }\n yield {\n type: 'finish',\n // PS-4: a mid-stream failure must not masquerade as a clean stop -\n // partial text would be indistinguishable from success. PS-12: an\n // aborted stream reports 'aborted', mirroring the HTTP adapters.\n finishReason: errored ? 'error' : aborted ? 'aborted' : 'stop',\n usage: {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n },\n };\n}\n\nasync function resolveModel(options: LlamaCppNodeAdapterOptions): Promise<LlamaModelInstance> {\n const llama = await loadLlamaModule(options.runtimeOverrides);\n return llama.loadModel({\n modelPath: options.modelPath,\n ...(options.gpuLayers !== undefined ? { gpuLayers: options.gpuLayers } : {}),\n });\n}\n\n/**\n * The REAL default session factory (PS-3): `model.createContext()` →\n * `context.getSequence()` → `new LlamaChatSession({ contextSequence })`\n * from the lazily-loaded peer, adapting its callback-streaming\n * `prompt(text, { onTextChunk })` to the adapter's\n * `promptStreamingResponse` AsyncIterable contract.\n */\nasync function defaultSessionFactory(\n model: LlamaModelInstance,\n system: string | undefined,\n options: LlamaCppNodeAdapterOptions,\n): Promise<LlamaSessionInstance> {\n const ChatSession = await loadLlamaChatSessionCtor(options.runtimeOverrides);\n const context = await model.createContext(\n options.contextSize !== undefined ? { contextSize: options.contextSize } : undefined,\n );\n const sequence = context.getSequence();\n const session = new ChatSession({\n contextSequence: sequence,\n ...(system !== undefined ? { systemPrompt: system } : {}),\n });\n return {\n promptStreamingResponse(prompt, streamOptions) {\n return promptToIterable(session, prompt, streamOptions);\n },\n dispose() {\n // Sequence first, then its owning context (core-provider-08).\n try {\n sequence.dispose?.();\n } catch {\n // fall through to the context\n }\n context.dispose?.();\n },\n };\n}\n\n/**\n * Bridge the peer's callback-streaming `prompt(...)` into the\n * AsyncIterable the adapter consumes. A rejection from `prompt`\n * rejects the pending/next iteration so the stream's error path\n * (PS-4) observes it.\n */\nfunction promptToIterable(\n session: LlamaChatSessionPeer,\n prompt: string,\n streamOptions?: {\n readonly signal?: AbortSignal;\n readonly maxTokens?: number;\n readonly temperature?: number;\n },\n): AsyncIterable<string> {\n const queue: string[] = [];\n let done = false;\n let failure: unknown;\n let wake: (() => void) | null = null;\n const notify = (): void => {\n if (wake !== null) {\n const w = wake;\n wake = null;\n w();\n }\n };\n void session\n .prompt(prompt, {\n ...(streamOptions?.signal !== undefined ? { signal: streamOptions.signal } : {}),\n ...(streamOptions?.maxTokens !== undefined ? { maxTokens: streamOptions.maxTokens } : {}),\n ...(streamOptions?.temperature !== undefined\n ? { temperature: streamOptions.temperature }\n : {}),\n onTextChunk: (chunk: string) => {\n if (typeof chunk === 'string' && chunk.length > 0) queue.push(chunk);\n notify();\n },\n })\n .then(\n () => {\n done = true;\n notify();\n },\n (err: unknown) => {\n failure = err instanceof Error ? err : new Error(String(err));\n done = true;\n notify();\n },\n );\n return {\n [Symbol.asyncIterator](): AsyncIterator<string> {\n return {\n async next(): Promise<IteratorResult<string>> {\n for (;;) {\n const head = queue.shift();\n if (head !== undefined) return { done: false, value: head };\n if (failure !== undefined) throw failure;\n if (done) return { done: true, value: undefined };\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n },\n };\n },\n };\n}\n\nfunction renderPrompt(req: ProviderRequest): string {\n const parts: string[] = [];\n // `req.systemMessage` is deliberately NOT rendered here: the session\n // factory already sets it as the chat-template `systemPrompt`, and\n // rendering it again would show the model the system prompt twice\n // (core-provider-08).\n for (const msg of req.messages) {\n const text =\n typeof msg.content === 'string'\n ? msg.content\n : msg.content\n .map((p) => (p.type === 'text' ? p.text : p.type === 'reasoning' ? p.text : ''))\n .join('');\n parts.push(`[${msg.role}] ${text}`);\n }\n return parts.join('\\n');\n}\n\nfunction basename(path: string): string {\n const idx = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\\\'));\n return idx === -1 ? path : path.slice(idx + 1);\n}\n\nfunction lengthOf(tokens: readonly number[] | Uint32Array | Uint8Array | null | undefined): number {\n if (tokens === null || tokens === undefined) return 0;\n return (tokens as { length?: number }).length ?? 0;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"counter.js","names":["#model","#tokenLengthOf"],"sources":["../src/counter.ts"],"sourcesContent":["/**\n * `LlamaCppNativeCounter` token counter that wraps the loaded\n * `node-llama-cpp` model's `tokenize(text)` function. The counter is\n * strictly tighter than the cl100k_base proxy because it uses the\n * exact tokenizer the GGUF file embeds.\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\nimport { serialiseMessageForCount, serializedToString } from '@graphorin/provider/counters';\n\nimport type { LlamaModelInstance } from './runtime.js';\n\n/**\n * Options for {@link LlamaCppNativeCounter}.\n *\n * @stable\n */\nexport interface LlamaCppNativeCounterOptions {\n readonly model: LlamaModelInstance;\n readonly modelPath?: string;\n readonly id?: string;\n}\n\n/**\n * Counter that delegates to `model.tokenize(text)` from the loaded\n * GGUF instance. Cache invalidation is keyed on the model file path\n * (when supplied) so swapping models invalidates per-message caches\n * upstream.\n *\n * @stable\n */\nexport class LlamaCppNativeCounter implements TokenCounter {\n readonly id: string;\n readonly version: string;\n readonly #model: LlamaModelInstance;\n\n constructor(options: LlamaCppNativeCounterOptions) {\n this.#model = options.model;\n const fingerprint = options.modelPath !== undefined ? hash(options.modelPath) : 'unknown';\n this.id = options.id ?? `llama-cpp-native@${fingerprint}`;\n this.version = `llama-cpp-native-${fingerprint}-v1`;\n }\n\n async count(messages: ReadonlyArray<Message>): Promise<number> {\n let total = 0;\n for (const msg of messages) {\n const serialised = serialiseMessageForCount(msg);\n total += this.#tokenLengthOf(serializedToString(serialised));\n }\n return total;\n }\n\n async countText(text: string): Promise<number> {\n if (text.length === 0) return 0;\n return this.#tokenLengthOf(text);\n }\n\n #tokenLengthOf(text: string): number {\n if (text.length === 0) return 0;\n const tokens = this.#model.tokenize(text);\n if (tokens === null || tokens === undefined) return 0;\n if (typeof (tokens as { length?: number }).length === 'number') {\n return (tokens as { length: number }).length;\n }\n return 0;\n }\n}\n\nfunction hash(value: string): string {\n // Lightweight deterministic fingerprint the framework only needs\n // it for cache invalidation, not for cryptographic purposes.\n let h = 0;\n for (let i = 0; i < value.length; i++) {\n h = (h * 31 + value.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(16);\n}\n"],"mappings":";;;;;;;;;;;AAiCA,IAAa,wBAAb,MAA2D;CACzD,AAAS;CACT,AAAS;CACT,CAASA;CAET,YAAY,SAAuC;AACjD,QAAKA,QAAS,QAAQ;EACtB,MAAM,cAAc,QAAQ,cAAc,SAAY,KAAK,QAAQ,UAAU,GAAG;AAChF,OAAK,KAAK,QAAQ,MAAM,oBAAoB;AAC5C,OAAK,UAAU,oBAAoB,YAAY;;CAGjD,MAAM,MAAM,UAAmD;EAC7D,IAAI,QAAQ;AACZ,OAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,aAAa,yBAAyB,IAAI;AAChD,YAAS,MAAKC,cAAe,mBAAmB,WAAW,CAAC;;AAE9D,SAAO;;CAGT,MAAM,UAAU,MAA+B;AAC7C,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAO,MAAKA,cAAe,KAAK;;CAGlC,eAAe,MAAsB;AACnC,MAAI,KAAK,WAAW,EAAG,QAAO;EAC9B,MAAM,SAAS,MAAKD,MAAO,SAAS,KAAK;AACzC,MAAI,WAAW,QAAQ,WAAW,OAAW,QAAO;AACpD,MAAI,OAAQ,OAA+B,WAAW,SACpD,QAAQ,OAA8B;AAExC,SAAO;;;AAIX,SAAS,KAAK,OAAuB;CAGnC,IAAI,IAAI;AACR,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,KAAK,IAAI,KAAK,MAAM,WAAW,EAAE,GAAI;AAEvC,QAAO,KAAK,IAAI,EAAE,CAAC,SAAS,GAAG"}
1
+ {"version":3,"file":"counter.js","names":["#model","#tokenLengthOf"],"sources":["../src/counter.ts"],"sourcesContent":["/**\n * `LlamaCppNativeCounter` - token counter that wraps the loaded\n * `node-llama-cpp` model's `tokenize(text)` function. The counter is\n * strictly tighter than the cl100k_base proxy because it uses the\n * exact tokenizer the GGUF file embeds.\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\nimport { serialiseMessageForCount, serializedToString } from '@graphorin/provider/counters';\n\nimport type { LlamaModelInstance } from './runtime.js';\n\n/**\n * Options for {@link LlamaCppNativeCounter}.\n *\n * @stable\n */\nexport interface LlamaCppNativeCounterOptions {\n readonly model: LlamaModelInstance;\n readonly modelPath?: string;\n readonly id?: string;\n}\n\n/**\n * Counter that delegates to `model.tokenize(text)` from the loaded\n * GGUF instance. Cache invalidation is keyed on the model file path\n * (when supplied) so swapping models invalidates per-message caches\n * upstream.\n *\n * @stable\n */\nexport class LlamaCppNativeCounter implements TokenCounter {\n readonly id: string;\n readonly version: string;\n readonly #model: LlamaModelInstance;\n\n constructor(options: LlamaCppNativeCounterOptions) {\n this.#model = options.model;\n const fingerprint = options.modelPath !== undefined ? hash(options.modelPath) : 'unknown';\n this.id = options.id ?? `llama-cpp-native@${fingerprint}`;\n this.version = `llama-cpp-native-${fingerprint}-v1`;\n }\n\n async count(messages: ReadonlyArray<Message>): Promise<number> {\n let total = 0;\n for (const msg of messages) {\n const serialised = serialiseMessageForCount(msg);\n total += this.#tokenLengthOf(serializedToString(serialised));\n }\n return total;\n }\n\n async countText(text: string): Promise<number> {\n if (text.length === 0) return 0;\n return this.#tokenLengthOf(text);\n }\n\n #tokenLengthOf(text: string): number {\n if (text.length === 0) return 0;\n const tokens = this.#model.tokenize(text);\n if (tokens === null || tokens === undefined) return 0;\n if (typeof (tokens as { length?: number }).length === 'number') {\n return (tokens as { length: number }).length;\n }\n return 0;\n }\n}\n\nfunction hash(value: string): string {\n // Lightweight deterministic fingerprint - the framework only needs\n // it for cache invalidation, not for cryptographic purposes.\n let h = 0;\n for (let i = 0; i < value.length; i++) {\n h = (h * 31 + value.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(16);\n}\n"],"mappings":";;;;;;;;;;;AAiCA,IAAa,wBAAb,MAA2D;CACzD,AAAS;CACT,AAAS;CACT,CAASA;CAET,YAAY,SAAuC;AACjD,QAAKA,QAAS,QAAQ;EACtB,MAAM,cAAc,QAAQ,cAAc,SAAY,KAAK,QAAQ,UAAU,GAAG;AAChF,OAAK,KAAK,QAAQ,MAAM,oBAAoB;AAC5C,OAAK,UAAU,oBAAoB,YAAY;;CAGjD,MAAM,MAAM,UAAmD;EAC7D,IAAI,QAAQ;AACZ,OAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,aAAa,yBAAyB,IAAI;AAChD,YAAS,MAAKC,cAAe,mBAAmB,WAAW,CAAC;;AAE9D,SAAO;;CAGT,MAAM,UAAU,MAA+B;AAC7C,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAO,MAAKA,cAAe,KAAK;;CAGlC,eAAe,MAAsB;AACnC,MAAI,KAAK,WAAW,EAAG,QAAO;EAC9B,MAAM,SAAS,MAAKD,MAAO,SAAS,KAAK;AACzC,MAAI,WAAW,QAAQ,WAAW,OAAW,QAAO;AACpD,MAAI,OAAQ,OAA+B,WAAW,SACpD,QAAQ,OAA8B;AAExC,SAAO;;;AAIX,SAAS,KAAK,OAAuB;CAGnC,IAAI,IAAI;AACR,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,KAAK,IAAI,KAAK,MAAM,WAAW,EAAE,GAAI;AAEvC,QAAO,KAAK,IAAI,EAAE,CAAC,SAAS,GAAG"}
package/dist/index.d.ts CHANGED
@@ -5,10 +5,10 @@ import { LlamaCppNativeCounter, LlamaCppNativeCounterOptions } from "./counter.j
5
5
  //#region src/index.d.ts
6
6
 
7
7
  /**
8
- * @graphorin/provider-llamacpp-node in-process GGUF execution
8
+ * @graphorin/provider-llamacpp-node - in-process GGUF execution
9
9
  * adapter for the Graphorin framework. The package wraps
10
10
  * `node-llama-cpp@^3.5` to load `.gguf` model files directly into the
11
- * same Node process no daemon, no port to manage, no GPU contention
11
+ * same Node process - no daemon, no port to manage, no GPU contention
12
12
  * with other processes.
13
13
  *
14
14
  * The adapter declares `trust: 'loopback'` permanently because the
@@ -17,7 +17,7 @@ import { LlamaCppNativeCounter, LlamaCppNativeCounterOptions } from "./counter.j
17
17
  * embedder; same trust boundary).
18
18
  *
19
19
  * The companion package is operationally simpler than the HTTP-shaped
20
- * adapters but does NOT survive a process restart mid-stream the
20
+ * adapters but does NOT survive a process restart mid-stream - the
21
21
  * model context lives in the process and is lost on exit. For HITL
22
22
  * durable mid-stream resume, one of the HTTP-shaped adapters
23
23
  * (`ollamaAdapter`, `llamaCppServerAdapter`, `openAICompatibleAdapter`)
@@ -26,7 +26,7 @@ import { LlamaCppNativeCounter, LlamaCppNativeCounterOptions } from "./counter.j
26
26
  * @packageDocumentation
27
27
  */
28
28
  /** Canonical version constant. Mirrors the `package.json` version. */
29
- declare const VERSION = "0.5.0";
29
+ declare const VERSION = "0.6.0";
30
30
  //#endregion
31
31
  export { LlamaCppNativeCounter, type LlamaCppNativeCounterOptions, type LlamaCppNodeAdapterOptions, type LlamaCppNodeRuntimeOverrides, type LlamaInstance, type LlamaModelInstance, type LlamaSessionInstance, VERSION, llamaCppNodeAdapter };
32
32
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -3,10 +3,10 @@ import { LlamaCppNativeCounter } from "./counter.js";
3
3
 
4
4
  //#region src/index.ts
5
5
  /**
6
- * @graphorin/provider-llamacpp-node in-process GGUF execution
6
+ * @graphorin/provider-llamacpp-node - in-process GGUF execution
7
7
  * adapter for the Graphorin framework. The package wraps
8
8
  * `node-llama-cpp@^3.5` to load `.gguf` model files directly into the
9
- * same Node process no daemon, no port to manage, no GPU contention
9
+ * same Node process - no daemon, no port to manage, no GPU contention
10
10
  * with other processes.
11
11
  *
12
12
  * The adapter declares `trust: 'loopback'` permanently because the
@@ -15,7 +15,7 @@ import { LlamaCppNativeCounter } from "./counter.js";
15
15
  * embedder; same trust boundary).
16
16
  *
17
17
  * The companion package is operationally simpler than the HTTP-shaped
18
- * adapters but does NOT survive a process restart mid-stream the
18
+ * adapters but does NOT survive a process restart mid-stream - the
19
19
  * model context lives in the process and is lost on exit. For HITL
20
20
  * durable mid-stream resume, one of the HTTP-shaped adapters
21
21
  * (`ollamaAdapter`, `llamaCppServerAdapter`, `openAICompatibleAdapter`)
@@ -24,7 +24,7 @@ import { LlamaCppNativeCounter } from "./counter.js";
24
24
  * @packageDocumentation
25
25
  */
26
26
  /** Canonical version constant. Mirrors the `package.json` version. */
27
- const VERSION = "0.5.0";
27
+ const VERSION = "0.6.0";
28
28
 
29
29
  //#endregion
30
30
  export { LlamaCppNativeCounter, VERSION, llamaCppNodeAdapter };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/provider-llamacpp-node in-process GGUF execution\n * adapter for the Graphorin framework. The package wraps\n * `node-llama-cpp@^3.5` to load `.gguf` model files directly into the\n * same Node process no daemon, no port to manage, no GPU contention\n * with other processes.\n *\n * The adapter declares `trust: 'loopback'` permanently because the\n * model lives in the same trust boundary as the host process; the\n * symmetry mirrors `@graphorin/embedder-transformersjs` (in-process\n * embedder; same trust boundary).\n *\n * The companion package is operationally simpler than the HTTP-shaped\n * adapters but does NOT survive a process restart mid-stream the\n * model context lives in the process and is lost on exit. For HITL\n * durable mid-stream resume, one of the HTTP-shaped adapters\n * (`ollamaAdapter`, `llamaCppServerAdapter`, `openAICompatibleAdapter`)\n * is the better choice.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.5.0';\n\nexport { type LlamaCppNodeAdapterOptions, llamaCppNodeAdapter } from './adapter.js';\nexport {\n LlamaCppNativeCounter,\n type LlamaCppNativeCounterOptions,\n} from './counter.js';\nexport type {\n LlamaCppNodeRuntimeOverrides,\n LlamaInstance,\n LlamaModelInstance,\n LlamaSessionInstance,\n} from './runtime.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,UAAU"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/provider-llamacpp-node - in-process GGUF execution\n * adapter for the Graphorin framework. The package wraps\n * `node-llama-cpp@^3.5` to load `.gguf` model files directly into the\n * same Node process - no daemon, no port to manage, no GPU contention\n * with other processes.\n *\n * The adapter declares `trust: 'loopback'` permanently because the\n * model lives in the same trust boundary as the host process; the\n * symmetry mirrors `@graphorin/embedder-transformersjs` (in-process\n * embedder; same trust boundary).\n *\n * The companion package is operationally simpler than the HTTP-shaped\n * adapters but does NOT survive a process restart mid-stream - the\n * model context lives in the process and is lost on exit. For HITL\n * durable mid-stream resume, one of the HTTP-shaped adapters\n * (`ollamaAdapter`, `llamaCppServerAdapter`, `openAICompatibleAdapter`)\n * is the better choice.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.6.0';\n\nexport { type LlamaCppNodeAdapterOptions, llamaCppNodeAdapter } from './adapter.js';\nexport {\n LlamaCppNativeCounter,\n type LlamaCppNativeCounterOptions,\n} from './counter.js';\nexport type {\n LlamaCppNodeRuntimeOverrides,\n LlamaInstance,\n LlamaModelInstance,\n LlamaSessionInstance,\n} from './runtime.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,UAAU"}
package/dist/runtime.d.ts CHANGED
@@ -46,6 +46,13 @@ interface LlamaSessionInstance {
46
46
  readonly maxTokens?: number;
47
47
  readonly temperature?: number;
48
48
  }): AsyncIterable<string>;
49
+ /**
50
+ * Release the per-request context / sequence backing this session.
51
+ * node-llama-cpp contexts hold KV-cache memory (hundreds of MB at
52
+ * large context sizes); the adapter calls this in a `finally` after
53
+ * every stream so long-running agents do not leak until OOM.
54
+ */
55
+ dispose?(): void;
49
56
  }
50
57
  /**
51
58
  * Test-only shape for injecting fixture-driven runtime behaviour.
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","names":[],"sources":["../src/runtime.ts"],"sourcesContent":[],"mappings":";;AAaA;AASA;;;;;;AAeA;AAgBA;;;AASW,UAjDM,aAAA,CAiDN;EAEI,SAAA,CAAA,IAAA,EAAA;IAAR,SAAA,EAAA,MAAA;IAMuB,SAAA,CAAA,EAAA,MAAA,GAAA,MAAA;EAAoB,CAAA,CAAA,EAxDqB,OAwDrB,CAxD6B,kBAwD7B,CAAA;AAWlD;AAaA;;;;;UAxEiB,kBAAA;;8CAE6B,cAAc;;;MACV;;;;;;cAIpC;;;;;;;UAQG,oBAAA;;sBAIO;;;MAInB;;;;;;;UAQY,4BAAA;;4BAEW,QAAQ;;;;;;mCAOzB,wCAEJ,QAAQ;;;;;;8BAMe;;;;;;;;;;UAWb,oBAAA;;sBAIO;;;;MAKnB;;;KAIO,oBAAA;;;MAGN"}
1
+ {"version":3,"file":"runtime.d.ts","names":[],"sources":["../src/runtime.ts"],"sourcesContent":[],"mappings":";;AAaA;AASA;;;;;;AAeA;AAuBA;;;AASW,UAxDM,aAAA,CAwDN;EAEI,SAAA,CAAA,IAAA,EAAA;IAAR,SAAA,EAAA,MAAA;IAMuB,SAAA,CAAA,EAAA,MAAA,GAAA,MAAA;EAAoB,CAAA,CAAA,EA/DqB,OA+DrB,CA/D6B,kBA+D7B,CAAA;AAWlD;AAaA;;;;;UA/EiB,kBAAA;;8CAE6B,cAAc;;;MACV;;;;;;cAIpC;;;;;;;UAQG,oBAAA;;sBAIO;;;MAInB;;;;;;;;;;;;;;UAeY,4BAAA;;4BAEW,QAAQ;;;;;;mCAOzB,wCAEJ,QAAQ;;;;;;8BAMe;;;;;;;;;;UAWb,oBAAA;;sBAIO;;;;MAKnB;;;KAIO,oBAAA;;;MAGN"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","names":["cachedLlama: LlamaInstance | null","cachedChatSessionCtor: LlamaChatSessionCtor | null","mod: { getLlama?: unknown }","mod: { LlamaChatSession?: unknown }"],"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * Loose structural shapes covering the slice of `node-llama-cpp` we\n * use. Re-declared here to avoid pulling the heavy native peer at\n * type-check time.\n *\n * @internal\n */\n\n/**\n * `Llama` engine instance (returned by `getLlama()`).\n *\n * @internal\n */\nexport interface LlamaInstance {\n loadModel(args: { modelPath: string; gpuLayers?: number | 'auto' }): Promise<LlamaModelInstance>;\n}\n\n/**\n * Loaded GGUF model.\n *\n * @internal\n */\nexport interface LlamaModelInstance {\n readonly trainContextSize?: number;\n tokenize(text: string): readonly number[] | Uint32Array | Uint8Array;\n createContext(args?: { contextSize?: number }): Promise<{\n getSequence(): { dispose?: () => void };\n dispose?: () => void;\n }>;\n dispose?(): Promise<void>;\n}\n\n/**\n * Loaded chat session capable of streaming responses.\n *\n * @internal\n */\nexport interface LlamaSessionInstance {\n promptStreamingResponse(\n prompt: string,\n options?: {\n readonly signal?: AbortSignal;\n readonly maxTokens?: number;\n readonly temperature?: number;\n },\n ): AsyncIterable<string>;\n}\n\n/**\n * Test-only shape for injecting fixture-driven runtime behaviour.\n *\n * @stable\n */\nexport interface LlamaCppNodeRuntimeOverrides {\n /** Returns a `LlamaInstance` (the result of `getLlama()`). */\n readonly getLlama?: () => Promise<LlamaInstance>;\n /**\n * Build a streaming chat session against an already-loaded model\n * instance. Used by the adapter to wire `model.tokenize` and\n * `session.promptStreamingResponse` to the per-test fixture.\n */\n readonly createSession?: (\n model: LlamaModelInstance,\n system?: string,\n ) => Promise<LlamaSessionInstance>;\n /**\n * Override the `LlamaChatSession` constructor used by the REAL\n * default session factory (PS-3). Tests stub it; production loads it\n * from the `node-llama-cpp` peer.\n */\n readonly LlamaChatSession?: LlamaChatSessionCtor;\n}\n\n/**\n * Structural slice of the peer's `LlamaChatSession` class used by the\n * default session factory (PS-3): `prompt(text, { onTextChunk })`\n * resolves with the full response while streaming chunks through the\n * callback.\n *\n * @internal\n */\nexport interface LlamaChatSessionPeer {\n prompt(\n text: string,\n options?: {\n readonly signal?: AbortSignal;\n readonly maxTokens?: number;\n readonly temperature?: number;\n readonly onTextChunk?: (chunk: string) => void;\n },\n ): Promise<string>;\n}\n\n/** @internal */\nexport type LlamaChatSessionCtor = new (args: {\n readonly contextSequence: unknown;\n readonly systemPrompt?: string;\n}) => LlamaChatSessionPeer;\n\nlet cachedLlama: LlamaInstance | null = null;\nlet cachedChatSessionCtor: LlamaChatSessionCtor | null = null;\n\n/**\n * Lazily load the `node-llama-cpp` peer and return the `Llama` engine\n * instance. Cached per process.\n *\n * @internal\n */\nexport async function loadLlamaModule(\n overrides: LlamaCppNodeRuntimeOverrides | undefined,\n): Promise<LlamaInstance> {\n if (overrides?.getLlama !== undefined) return overrides.getLlama();\n if (cachedLlama !== null) return cachedLlama;\n let mod: { getLlama?: unknown };\n try {\n mod = (await import('node-llama-cpp')) as { getLlama?: unknown };\n } catch (cause) {\n throw new Error(\n \"[graphorin/provider-llamacpp-node] missing peer dependency 'node-llama-cpp'. \" +\n 'Install it with `pnpm add node-llama-cpp`.',\n { cause },\n );\n }\n if (typeof mod.getLlama !== 'function') {\n throw new Error(\n '[graphorin/provider-llamacpp-node] installed node-llama-cpp does not expose getLlama().',\n );\n }\n const instance = (await (mod.getLlama as () => Promise<LlamaInstance>)()) as LlamaInstance;\n cachedLlama = instance;\n return instance;\n}\n\n/**\n * Lazily resolve the peer's `LlamaChatSession` constructor for the\n * real default session factory (PS-3). Cached per process; the\n * override wins for tests.\n *\n * @internal\n */\nexport async function loadLlamaChatSessionCtor(\n overrides: LlamaCppNodeRuntimeOverrides | undefined,\n): Promise<LlamaChatSessionCtor> {\n if (overrides?.LlamaChatSession !== undefined) return overrides.LlamaChatSession;\n if (cachedChatSessionCtor !== null) return cachedChatSessionCtor;\n let mod: { LlamaChatSession?: unknown };\n try {\n mod = (await import('node-llama-cpp')) as { LlamaChatSession?: unknown };\n } catch (cause) {\n throw new Error(\n \"[graphorin/provider-llamacpp-node] missing peer dependency 'node-llama-cpp'. \" +\n 'Install it with `pnpm add node-llama-cpp`.',\n { cause },\n );\n }\n if (typeof mod.LlamaChatSession !== 'function') {\n throw new Error(\n '[graphorin/provider-llamacpp-node] installed node-llama-cpp does not expose LlamaChatSession.',\n );\n }\n cachedChatSessionCtor = mod.LlamaChatSession as LlamaChatSessionCtor;\n return cachedChatSessionCtor;\n}\n\n/**\n * Test-only hook that resets the cached `getLlama()` result.\n *\n * @internal\n */\nexport function __resetLlamaCache(): void {\n cachedLlama = null;\n cachedChatSessionCtor = null;\n}\n"],"mappings":";AAmGA,IAAIA,cAAoC;AACxC,IAAIC,wBAAqD;;;;;;;AAQzD,eAAsB,gBACpB,WACwB;AACxB,KAAI,WAAW,aAAa,OAAW,QAAO,UAAU,UAAU;AAClE,KAAI,gBAAgB,KAAM,QAAO;CACjC,IAAIC;AACJ,KAAI;AACF,QAAO,MAAM,OAAO;UACb,OAAO;AACd,QAAM,IAAI,MACR,2HAEA,EAAE,OAAO,CACV;;AAEH,KAAI,OAAO,IAAI,aAAa,WAC1B,OAAM,IAAI,MACR,0FACD;CAEH,MAAM,WAAY,MAAO,IAAI,UAA2C;AACxE,eAAc;AACd,QAAO;;;;;;;;;AAUT,eAAsB,yBACpB,WAC+B;AAC/B,KAAI,WAAW,qBAAqB,OAAW,QAAO,UAAU;AAChE,KAAI,0BAA0B,KAAM,QAAO;CAC3C,IAAIC;AACJ,KAAI;AACF,QAAO,MAAM,OAAO;UACb,OAAO;AACd,QAAM,IAAI,MACR,2HAEA,EAAE,OAAO,CACV;;AAEH,KAAI,OAAO,IAAI,qBAAqB,WAClC,OAAM,IAAI,MACR,gGACD;AAEH,yBAAwB,IAAI;AAC5B,QAAO"}
1
+ {"version":3,"file":"runtime.js","names":["cachedLlama: LlamaInstance | null","cachedChatSessionCtor: LlamaChatSessionCtor | null","mod: { getLlama?: unknown }","mod: { LlamaChatSession?: unknown }"],"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * Loose structural shapes covering the slice of `node-llama-cpp` we\n * use. Re-declared here to avoid pulling the heavy native peer at\n * type-check time.\n *\n * @internal\n */\n\n/**\n * `Llama` engine instance (returned by `getLlama()`).\n *\n * @internal\n */\nexport interface LlamaInstance {\n loadModel(args: { modelPath: string; gpuLayers?: number | 'auto' }): Promise<LlamaModelInstance>;\n}\n\n/**\n * Loaded GGUF model.\n *\n * @internal\n */\nexport interface LlamaModelInstance {\n readonly trainContextSize?: number;\n tokenize(text: string): readonly number[] | Uint32Array | Uint8Array;\n createContext(args?: { contextSize?: number }): Promise<{\n getSequence(): { dispose?: () => void };\n dispose?: () => void;\n }>;\n dispose?(): Promise<void>;\n}\n\n/**\n * Loaded chat session capable of streaming responses.\n *\n * @internal\n */\nexport interface LlamaSessionInstance {\n promptStreamingResponse(\n prompt: string,\n options?: {\n readonly signal?: AbortSignal;\n readonly maxTokens?: number;\n readonly temperature?: number;\n },\n ): AsyncIterable<string>;\n /**\n * Release the per-request context / sequence backing this session.\n * node-llama-cpp contexts hold KV-cache memory (hundreds of MB at\n * large context sizes); the adapter calls this in a `finally` after\n * every stream so long-running agents do not leak until OOM.\n */\n dispose?(): void;\n}\n\n/**\n * Test-only shape for injecting fixture-driven runtime behaviour.\n *\n * @stable\n */\nexport interface LlamaCppNodeRuntimeOverrides {\n /** Returns a `LlamaInstance` (the result of `getLlama()`). */\n readonly getLlama?: () => Promise<LlamaInstance>;\n /**\n * Build a streaming chat session against an already-loaded model\n * instance. Used by the adapter to wire `model.tokenize` and\n * `session.promptStreamingResponse` to the per-test fixture.\n */\n readonly createSession?: (\n model: LlamaModelInstance,\n system?: string,\n ) => Promise<LlamaSessionInstance>;\n /**\n * Override the `LlamaChatSession` constructor used by the REAL\n * default session factory (PS-3). Tests stub it; production loads it\n * from the `node-llama-cpp` peer.\n */\n readonly LlamaChatSession?: LlamaChatSessionCtor;\n}\n\n/**\n * Structural slice of the peer's `LlamaChatSession` class used by the\n * default session factory (PS-3): `prompt(text, { onTextChunk })`\n * resolves with the full response while streaming chunks through the\n * callback.\n *\n * @internal\n */\nexport interface LlamaChatSessionPeer {\n prompt(\n text: string,\n options?: {\n readonly signal?: AbortSignal;\n readonly maxTokens?: number;\n readonly temperature?: number;\n readonly onTextChunk?: (chunk: string) => void;\n },\n ): Promise<string>;\n}\n\n/** @internal */\nexport type LlamaChatSessionCtor = new (args: {\n readonly contextSequence: unknown;\n readonly systemPrompt?: string;\n}) => LlamaChatSessionPeer;\n\nlet cachedLlama: LlamaInstance | null = null;\nlet cachedChatSessionCtor: LlamaChatSessionCtor | null = null;\n\n/**\n * Lazily load the `node-llama-cpp` peer and return the `Llama` engine\n * instance. Cached per process.\n *\n * @internal\n */\nexport async function loadLlamaModule(\n overrides: LlamaCppNodeRuntimeOverrides | undefined,\n): Promise<LlamaInstance> {\n if (overrides?.getLlama !== undefined) return overrides.getLlama();\n if (cachedLlama !== null) return cachedLlama;\n let mod: { getLlama?: unknown };\n try {\n mod = (await import('node-llama-cpp')) as { getLlama?: unknown };\n } catch (cause) {\n throw new Error(\n \"[graphorin/provider-llamacpp-node] missing peer dependency 'node-llama-cpp'. \" +\n 'Install it with `pnpm add node-llama-cpp`.',\n { cause },\n );\n }\n if (typeof mod.getLlama !== 'function') {\n throw new Error(\n '[graphorin/provider-llamacpp-node] installed node-llama-cpp does not expose getLlama().',\n );\n }\n const instance = (await (mod.getLlama as () => Promise<LlamaInstance>)()) as LlamaInstance;\n cachedLlama = instance;\n return instance;\n}\n\n/**\n * Lazily resolve the peer's `LlamaChatSession` constructor for the\n * real default session factory (PS-3). Cached per process; the\n * override wins for tests.\n *\n * @internal\n */\nexport async function loadLlamaChatSessionCtor(\n overrides: LlamaCppNodeRuntimeOverrides | undefined,\n): Promise<LlamaChatSessionCtor> {\n if (overrides?.LlamaChatSession !== undefined) return overrides.LlamaChatSession;\n if (cachedChatSessionCtor !== null) return cachedChatSessionCtor;\n let mod: { LlamaChatSession?: unknown };\n try {\n mod = (await import('node-llama-cpp')) as { LlamaChatSession?: unknown };\n } catch (cause) {\n throw new Error(\n \"[graphorin/provider-llamacpp-node] missing peer dependency 'node-llama-cpp'. \" +\n 'Install it with `pnpm add node-llama-cpp`.',\n { cause },\n );\n }\n if (typeof mod.LlamaChatSession !== 'function') {\n throw new Error(\n '[graphorin/provider-llamacpp-node] installed node-llama-cpp does not expose LlamaChatSession.',\n );\n }\n cachedChatSessionCtor = mod.LlamaChatSession as LlamaChatSessionCtor;\n return cachedChatSessionCtor;\n}\n\n/**\n * Test-only hook that resets the cached `getLlama()` result.\n *\n * @internal\n */\nexport function __resetLlamaCache(): void {\n cachedLlama = null;\n cachedChatSessionCtor = null;\n}\n"],"mappings":";AA0GA,IAAIA,cAAoC;AACxC,IAAIC,wBAAqD;;;;;;;AAQzD,eAAsB,gBACpB,WACwB;AACxB,KAAI,WAAW,aAAa,OAAW,QAAO,UAAU,UAAU;AAClE,KAAI,gBAAgB,KAAM,QAAO;CACjC,IAAIC;AACJ,KAAI;AACF,QAAO,MAAM,OAAO;UACb,OAAO;AACd,QAAM,IAAI,MACR,2HAEA,EAAE,OAAO,CACV;;AAEH,KAAI,OAAO,IAAI,aAAa,WAC1B,OAAM,IAAI,MACR,0FACD;CAEH,MAAM,WAAY,MAAO,IAAI,UAA2C;AACxE,eAAc;AACd,QAAO;;;;;;;;;AAUT,eAAsB,yBACpB,WAC+B;AAC/B,KAAI,WAAW,qBAAqB,OAAW,QAAO,UAAU;AAChE,KAAI,0BAA0B,KAAM,QAAO;CAC3C,IAAIC;AACJ,KAAI;AACF,QAAO,MAAM,OAAO;UACb,OAAO;AACd,QAAM,IAAI,MACR,2HAEA,EAAE,OAAO,CACV;;AAEH,KAAI,OAAO,IAAI,qBAAqB,WAClC,OAAM,IAAI,MACR,gGACD;AAEH,yBAAwB,IAAI;AAC5B,QAAO"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@graphorin/provider-llamacpp-node",
3
- "version": "0.5.0",
4
- "description": "In-process GGUF execution adapter for the Graphorin framework. Wraps node-llama-cpp@^3.5 to load .gguf model files directly into the same Node process no daemon, no port to manage, no GPU contention with other processes. Declares trust: 'loopback' permanently because the model lives in the same trust boundary as the host process. Ships LlamaCppNativeCounter that wraps model.tokenize() from the loaded GGUF for byte-exact token counts.",
3
+ "version": "0.6.0",
4
+ "description": "In-process GGUF execution adapter for the Graphorin framework. Wraps node-llama-cpp@^3.5 to load .gguf model files directly into the same Node process - no daemon, no port to manage, no GPU contention with other processes. Declares trust: 'loopback' permanently because the model lives in the same trust boundary as the host process. Ships LlamaCppNativeCounter that wraps model.tokenize() from the loaded GGUF for byte-exact token counts.",
5
5
  "license": "MIT",
6
6
  "author": "Oleksiy Stepurenko",
7
7
  "homepage": "https://github.com/o-stepper/graphorin/tree/main/packages/provider-llamacpp-node",
@@ -48,8 +48,8 @@
48
48
  "LICENSE"
49
49
  ],
50
50
  "dependencies": {
51
- "@graphorin/core": "0.5.0",
52
- "@graphorin/provider": "0.5.0"
51
+ "@graphorin/core": "0.6.0",
52
+ "@graphorin/provider": "0.6.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "node-llama-cpp": "^3.5.0"