@synapseia-network/node 0.8.5

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 (40) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +232 -0
  3. package/dist/bid-responder-Q725ZIUC.js +86 -0
  4. package/dist/bootstrap.js +22 -0
  5. package/dist/chain-info-lightweight-2UWAQZBF.js +303 -0
  6. package/dist/chat-stream-handler-BSHSGMFF.js +127 -0
  7. package/dist/chunk-2X7MSWD4.js +270 -0
  8. package/dist/chunk-3BHRQWSM.js +531 -0
  9. package/dist/chunk-5QFTU52A.js +442 -0
  10. package/dist/chunk-5ZAJBIAV.js +25 -0
  11. package/dist/chunk-7FLDR5NT.js +186 -0
  12. package/dist/chunk-C5XRYLYP.js +137 -0
  13. package/dist/chunk-D7ADMHK2.js +36 -0
  14. package/dist/chunk-DXUYWRO7.js +23 -0
  15. package/dist/chunk-F5UDK56Z.js +289 -0
  16. package/dist/chunk-NEHR6XY7.js +111 -0
  17. package/dist/chunk-NMJVODKH.js +453 -0
  18. package/dist/chunk-PRVT22SM.js +324 -0
  19. package/dist/chunk-T2ZRG5CX.js +1380 -0
  20. package/dist/chunk-V2L5SXTL.js +88 -0
  21. package/dist/chunk-XL2NJWFY.js +702 -0
  22. package/dist/embedding-C6GE3WVM.js +16 -0
  23. package/dist/hardware-ITQQJ5YI.js +37 -0
  24. package/dist/index.js +16836 -0
  25. package/dist/inference-server-CIGRJ36H.js +25 -0
  26. package/dist/local-cors-J6RWNMMD.js +44 -0
  27. package/dist/model-catalog-C53SDFMG.js +15 -0
  28. package/dist/model-discovery-LA6YMT3I.js +10 -0
  29. package/dist/ollama-XVXA3A37.js +9 -0
  30. package/dist/rewards-vault-cli-HW7H4EMD.js +147 -0
  31. package/dist/scripts/create_nodes.sh +6 -0
  32. package/dist/scripts/diloco_train.py +319 -0
  33. package/dist/scripts/train_lora.py +237 -0
  34. package/dist/scripts/train_micro.py +586 -0
  35. package/dist/trainer-HQMV2ZAR.js +21 -0
  36. package/package.json +128 -0
  37. package/scripts/create_nodes.sh +6 -0
  38. package/scripts/diloco_train.py +319 -0
  39. package/scripts/train_lora.py +237 -0
  40. package/scripts/train_micro.py +586 -0
@@ -0,0 +1,1380 @@
1
+ import { fileURLToPath as __synFup } from "url";import { dirname as __synDn } from "path";const __filename = __synFup(import.meta.url);const __dirname = __synDn(__filename);
2
+ import {
3
+ OllamaHelper
4
+ } from "./chunk-2X7MSWD4.js";
5
+ import {
6
+ init_logger,
7
+ logger_default
8
+ } from "./chunk-V2L5SXTL.js";
9
+ import {
10
+ __name
11
+ } from "./chunk-D7ADMHK2.js";
12
+
13
+ // src/modules/llm/providers.ts
14
+ var CLOUD_PROVIDERS = [
15
+ {
16
+ id: "openai",
17
+ label: "OpenAI",
18
+ endpoint: "https://api.openai.com/v1/chat/completions",
19
+ apiKeyEnvVar: "OPENAI_API_KEY",
20
+ models: {
21
+ top: {
22
+ modelId: "gpt-5",
23
+ latencyMs: 600,
24
+ maxTokens: 4e5,
25
+ costPerCall: 5e-3
26
+ },
27
+ mid: {
28
+ modelId: "gpt-4o",
29
+ latencyMs: 400,
30
+ maxTokens: 128e3,
31
+ costPerCall: 25e-4
32
+ },
33
+ budget: {
34
+ modelId: "gpt-4o-mini",
35
+ latencyMs: 250,
36
+ maxTokens: 128e3,
37
+ costPerCall: 6e-4
38
+ }
39
+ }
40
+ },
41
+ {
42
+ id: "anthropic",
43
+ label: "Anthropic",
44
+ endpoint: "https://api.anthropic.com/v1/messages",
45
+ apiKeyEnvVar: "ANTHROPIC_API_KEY",
46
+ models: {
47
+ top: {
48
+ modelId: "claude-opus-4-7",
49
+ latencyMs: 700,
50
+ maxTokens: 2e5,
51
+ costPerCall: 0.015
52
+ },
53
+ mid: {
54
+ modelId: "claude-sonnet-4-6",
55
+ latencyMs: 300,
56
+ maxTokens: 2e5,
57
+ costPerCall: 3e-3
58
+ },
59
+ budget: {
60
+ modelId: "claude-haiku-4-5",
61
+ latencyMs: 200,
62
+ maxTokens: 2e5,
63
+ costPerCall: 8e-4
64
+ }
65
+ }
66
+ },
67
+ {
68
+ id: "google",
69
+ label: "Google (Gemini)",
70
+ endpoint: "https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent",
71
+ apiKeyEnvVar: "GEMINI_API_KEY",
72
+ models: {
73
+ top: {
74
+ modelId: "gemini-2.5-pro",
75
+ latencyMs: 600,
76
+ maxTokens: 1048576,
77
+ costPerCall: 35e-4
78
+ },
79
+ mid: {
80
+ modelId: "gemini-2.5-flash",
81
+ latencyMs: 300,
82
+ maxTokens: 1048576,
83
+ costPerCall: 8e-4
84
+ },
85
+ budget: {
86
+ modelId: "gemini-2.5-flash-lite",
87
+ latencyMs: 200,
88
+ maxTokens: 1048576,
89
+ costPerCall: 15e-5
90
+ }
91
+ }
92
+ },
93
+ {
94
+ id: "moonshot",
95
+ label: "Kimi (Moonshot)",
96
+ endpoint: "https://api.moonshot.ai/v1/chat/completions",
97
+ apiKeyEnvVar: "MOONSHOT_API_KEY",
98
+ models: {
99
+ top: {
100
+ modelId: "kimi-k2.6",
101
+ latencyMs: 500,
102
+ maxTokens: 256e3,
103
+ costPerCall: 2e-3
104
+ },
105
+ mid: {
106
+ modelId: "kimi-k2-0711-preview",
107
+ latencyMs: 350,
108
+ maxTokens: 131072,
109
+ costPerCall: 1e-3
110
+ },
111
+ budget: {
112
+ modelId: "moonshot-v1-32k",
113
+ latencyMs: 300,
114
+ maxTokens: 32768,
115
+ costPerCall: 5e-4
116
+ }
117
+ }
118
+ },
119
+ {
120
+ id: "minimax",
121
+ label: "MiniMax",
122
+ endpoint: "https://api.minimax.io/v1/chat/completions",
123
+ apiKeyEnvVar: "MINIMAX_API_KEY",
124
+ models: {
125
+ top: {
126
+ modelId: "MiniMax-M2.7",
127
+ latencyMs: 400,
128
+ maxTokens: 245760,
129
+ costPerCall: 15e-4
130
+ },
131
+ mid: {
132
+ modelId: "abab7-chat-preview",
133
+ latencyMs: 350,
134
+ maxTokens: 245760,
135
+ costPerCall: 8e-4
136
+ },
137
+ budget: {
138
+ modelId: "abab6.5s-chat",
139
+ latencyMs: 300,
140
+ maxTokens: 245760,
141
+ costPerCall: 3e-4
142
+ }
143
+ }
144
+ },
145
+ {
146
+ id: "zhipu",
147
+ label: "Zhipu (GLM)",
148
+ endpoint: "https://open.bigmodel.cn/api/paas/v4/chat/completions",
149
+ apiKeyEnvVar: "ZHIPU_API_KEY",
150
+ models: {
151
+ top: {
152
+ modelId: "glm-4.6",
153
+ latencyMs: 500,
154
+ maxTokens: 2e5,
155
+ costPerCall: 15e-4
156
+ },
157
+ mid: {
158
+ modelId: "glm-4-plus",
159
+ latencyMs: 400,
160
+ maxTokens: 128e3,
161
+ costPerCall: 8e-4
162
+ },
163
+ budget: {
164
+ modelId: "glm-4-flash",
165
+ latencyMs: 250,
166
+ maxTokens: 128e3,
167
+ costPerCall: 1e-4
168
+ }
169
+ }
170
+ }
171
+ ];
172
+ var CLOUD_PROVIDERS_BY_ID = new Map(CLOUD_PROVIDERS.map((p) => [
173
+ p.id,
174
+ p
175
+ ]));
176
+ var OLLAMA_DEFAULT_MODELS = [
177
+ {
178
+ modelId: "qwen2.5:0.5b",
179
+ latencyMs: 300,
180
+ maxTokens: 4096
181
+ },
182
+ {
183
+ modelId: "qwen2.5:3b",
184
+ latencyMs: 800,
185
+ maxTokens: 8192
186
+ },
187
+ {
188
+ modelId: "gemma3:4b",
189
+ latencyMs: 1200,
190
+ maxTokens: 8192
191
+ },
192
+ {
193
+ modelId: "llama3.2:3b",
194
+ latencyMs: 900,
195
+ maxTokens: 8192
196
+ }
197
+ ];
198
+ function resolveSlug(slug) {
199
+ const slash = slug.indexOf("/");
200
+ if (slash <= 0) return null;
201
+ const provider = slug.slice(0, slash);
202
+ const modelId = slug.slice(slash + 1);
203
+ if (!modelId) return null;
204
+ if (provider === "ollama") {
205
+ const descriptor2 = OLLAMA_DEFAULT_MODELS.find((m) => m.modelId === modelId);
206
+ return {
207
+ provider: "ollama",
208
+ modelId,
209
+ endpoint: "",
210
+ descriptor: descriptor2
211
+ };
212
+ }
213
+ if (provider === "synapseia") {
214
+ return {
215
+ provider: "synapseia",
216
+ modelId,
217
+ endpoint: ""
218
+ };
219
+ }
220
+ const entry = CLOUD_PROVIDERS_BY_ID.get(provider);
221
+ if (!entry) return null;
222
+ const descriptor = Object.values(entry.models).find((m) => m.modelId === modelId);
223
+ if (!descriptor) return null;
224
+ return {
225
+ provider: entry.id,
226
+ modelId,
227
+ endpoint: entry.endpoint,
228
+ descriptor
229
+ };
230
+ }
231
+ __name(resolveSlug, "resolveSlug");
232
+ var FALLBACK_MODEL_SLUG = "anthropic/claude-sonnet-4-6";
233
+ function topSlugFor(provider) {
234
+ const entry = CLOUD_PROVIDERS_BY_ID.get(provider);
235
+ if (!entry) return FALLBACK_MODEL_SLUG;
236
+ return `${entry.id}/${entry.models.top.modelId}`;
237
+ }
238
+ __name(topSlugFor, "topSlugFor");
239
+
240
+ // src/modules/llm/llm-provider.ts
241
+ import { Injectable as Injectable2, Optional } from "@nestjs/common";
242
+
243
+ // src/shared/sanitize-llm-output.ts
244
+ var REASONING_PATTERNS = [
245
+ /<(think|thinking|reasoning|scratchpad)\b[^>]*>[\s\S]*?<\/\1>/gi,
246
+ /<\|(?:start_of_thinking|reasoning|scratchpad|thought)\|>[\s\S]*?<\|end_of_(?:thinking|reasoning|scratchpad|thought)\|>/gi
247
+ ];
248
+ var UNCLOSED_REASONING_PATTERNS = [
249
+ /<(think|thinking|reasoning|scratchpad)\b[^>]*>[\s\S]*$/i,
250
+ /<\|(?:start_of_thinking|reasoning|scratchpad|thought)\|>[\s\S]*$/i
251
+ ];
252
+ function stripReasoning(input) {
253
+ if (input == null) return "";
254
+ const text = typeof input === "string" ? input : String(input);
255
+ let out = text;
256
+ for (const re of REASONING_PATTERNS) out = out.replace(re, "");
257
+ for (const re of UNCLOSED_REASONING_PATTERNS) out = out.replace(re, "");
258
+ return out.trim();
259
+ }
260
+ __name(stripReasoning, "stripReasoning");
261
+
262
+ // src/modules/llm/synapseia-serving-client.ts
263
+ init_logger();
264
+ import { Injectable } from "@nestjs/common";
265
+ function _ts_decorate(decorators, target, key, desc) {
266
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
267
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
268
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
269
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
270
+ }
271
+ __name(_ts_decorate, "_ts_decorate");
272
+ function _ts_metadata(k, v) {
273
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
274
+ }
275
+ __name(_ts_metadata, "_ts_metadata");
276
+ var DEFAULT_URL = "http://127.0.0.1:8080";
277
+ var DEFAULT_TIMEOUT_MS = 6e4;
278
+ var SynapseiaServingClient = class {
279
+ static {
280
+ __name(this, "SynapseiaServingClient");
281
+ }
282
+ /**
283
+ * The serving URL + the Synapseia version currently loaded. Set by
284
+ * `ActiveModelSubscriber` at boot and after every hot-reload.
285
+ */
286
+ servingUrl;
287
+ activeVersion = null;
288
+ constructor() {
289
+ this.servingUrl = process.env.SYNAPSEIA_SERVING_URL ?? DEFAULT_URL;
290
+ }
291
+ setActiveVersion(version, url) {
292
+ this.activeVersion = version;
293
+ if (url) this.servingUrl = url;
294
+ logger_default.info(`[SynapseiaServing] now serving ${version} at ${this.servingUrl}`);
295
+ }
296
+ getActiveVersion() {
297
+ return this.activeVersion;
298
+ }
299
+ /**
300
+ * Returns `true` when the local runtime answers `GET /health` (or
301
+ * `GET /v1/models`) within 2s. Used by the bidder to decide whether
302
+ * to advertise a Synapseia bid this auction.
303
+ */
304
+ async isAvailable() {
305
+ try {
306
+ const res = await fetch(`${this.servingUrl}/v1/models`, {
307
+ signal: AbortSignal.timeout(2e3)
308
+ });
309
+ return res.ok;
310
+ } catch {
311
+ return false;
312
+ }
313
+ }
314
+ async generate(opts) {
315
+ if (!this.activeVersion) {
316
+ throw new Error("Synapseia serving not initialized (no active version)");
317
+ }
318
+ const started = Date.now();
319
+ const res = await fetch(`${this.servingUrl}/v1/chat/completions`, {
320
+ method: "POST",
321
+ headers: {
322
+ "content-type": "application/json"
323
+ },
324
+ body: JSON.stringify({
325
+ model: this.activeVersion,
326
+ messages: opts.messages,
327
+ temperature: opts.temperature ?? 0.2,
328
+ max_tokens: opts.maxTokens ?? 2048
329
+ }),
330
+ signal: AbortSignal.timeout(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS)
331
+ });
332
+ if (!res.ok) {
333
+ const body = await res.text();
334
+ throw new Error(`Synapseia serving HTTP ${res.status}: ${body.slice(0, 200)}`);
335
+ }
336
+ const data = await res.json();
337
+ const content = data.choices?.[0]?.message?.content ?? "";
338
+ if (!content) {
339
+ throw new Error("Synapseia serving returned empty content");
340
+ }
341
+ return {
342
+ content,
343
+ modelVersion: this.activeVersion,
344
+ latencyMs: Date.now() - started
345
+ };
346
+ }
347
+ };
348
+ SynapseiaServingClient = _ts_decorate([
349
+ Injectable(),
350
+ _ts_metadata("design:type", Function),
351
+ _ts_metadata("design:paramtypes", [])
352
+ ], SynapseiaServingClient);
353
+
354
+ // src/modules/llm/llm-provider.ts
355
+ init_logger();
356
+
357
+ // src/modules/llm/adapters/llm-response-adapter.ts
358
+ function readString(obj, ...path) {
359
+ let cur = obj;
360
+ for (const k of path) {
361
+ if (cur == null || typeof cur !== "object") return void 0;
362
+ cur = cur[k];
363
+ }
364
+ return typeof cur === "string" ? cur : void 0;
365
+ }
366
+ __name(readString, "readString");
367
+ function readNumber(obj, ...path) {
368
+ let cur = obj;
369
+ for (const k of path) {
370
+ if (cur == null || typeof cur !== "object") return void 0;
371
+ cur = cur[k];
372
+ }
373
+ return typeof cur === "number" ? cur : void 0;
374
+ }
375
+ __name(readNumber, "readNumber");
376
+ function readArray(obj, ...path) {
377
+ let cur = obj;
378
+ for (const k of path) {
379
+ if (cur == null || typeof cur !== "object") return [];
380
+ cur = cur[k];
381
+ }
382
+ return Array.isArray(cur) ? cur : [];
383
+ }
384
+ __name(readArray, "readArray");
385
+ function defaultErrorMessage(httpStatus, body, fallbackText) {
386
+ const err = readString(body, "error", "message") ?? readString(body, "message") ?? readString(body, "error");
387
+ if (err) return `HTTP ${httpStatus}: ${err}`;
388
+ if (fallbackText) {
389
+ const snippet = fallbackText.replace(/\s+/g, " ").trim().slice(0, 200);
390
+ return `HTTP ${httpStatus}: ${snippet}`;
391
+ }
392
+ return `HTTP ${httpStatus}`;
393
+ }
394
+ __name(defaultErrorMessage, "defaultErrorMessage");
395
+
396
+ // src/modules/llm/adapters/openai.adapter.ts
397
+ var FINISH_REASON_MAP = {
398
+ stop: "stop",
399
+ length: "length",
400
+ content_filter: "content_filter",
401
+ tool_calls: "tool_calls",
402
+ function_call: "tool_calls"
403
+ };
404
+ var OpenAIAdapter = class {
405
+ static {
406
+ __name(this, "OpenAIAdapter");
407
+ }
408
+ providerId = "openai";
409
+ buildRequest(req) {
410
+ const entry = CLOUD_PROVIDERS_BY_ID.get("openai");
411
+ if (!entry) throw new Error("openai provider not registered");
412
+ const body = {
413
+ model: req.model,
414
+ messages: [
415
+ {
416
+ role: "user",
417
+ content: req.prompt
418
+ }
419
+ ]
420
+ };
421
+ if (req.hyperparams?.temperature !== void 0) body.temperature = req.hyperparams.temperature;
422
+ if (req.hyperparams?.maxTokens !== void 0) body.max_tokens = req.hyperparams.maxTokens;
423
+ if (req.hyperparams?.forceJson) body.response_format = {
424
+ type: "json_object"
425
+ };
426
+ return {
427
+ url: entry.endpoint,
428
+ init: {
429
+ method: "POST",
430
+ headers: {
431
+ "Content-Type": "application/json",
432
+ "Authorization": `Bearer ${req.apiKey}`
433
+ },
434
+ body: JSON.stringify(body)
435
+ }
436
+ };
437
+ }
438
+ parseResponse(_httpStatus, body) {
439
+ const choices = readArray(body, "choices");
440
+ if (choices.length === 0) {
441
+ throw new Error("OpenAI response had no choices[]");
442
+ }
443
+ const choice = choices[0];
444
+ const refusal = readString(choice, "message", "refusal");
445
+ if (refusal) {
446
+ throw new Error(`OpenAI refused to answer: ${refusal}`);
447
+ }
448
+ const content = readString(choice, "message", "content");
449
+ if (typeof content !== "string" || content.length === 0) {
450
+ const finish = readString(choice, "finish_reason") ?? "unknown";
451
+ throw new Error(`OpenAI returned no message.content (finish_reason=${finish}); tool_calls/multimodal responses are not supported by this node`);
452
+ }
453
+ const finishStr = readString(choice, "finish_reason") ?? "stop";
454
+ return {
455
+ text: content,
456
+ finishReason: FINISH_REASON_MAP[finishStr] ?? "unknown",
457
+ usage: {
458
+ promptTokens: readNumber(body, "usage", "prompt_tokens"),
459
+ completionTokens: readNumber(body, "usage", "completion_tokens"),
460
+ totalTokens: readNumber(body, "usage", "total_tokens")
461
+ },
462
+ raw: body
463
+ };
464
+ }
465
+ parseError(httpStatus, body, fallbackText) {
466
+ return new Error(defaultErrorMessage(httpStatus, body, fallbackText));
467
+ }
468
+ };
469
+
470
+ // src/modules/llm/adapters/anthropic.adapter.ts
471
+ var STOP_REASON_MAP = {
472
+ end_turn: "stop",
473
+ stop_sequence: "stop",
474
+ max_tokens: "length",
475
+ tool_use: "tool_calls",
476
+ refusal: "content_filter"
477
+ };
478
+ var ANTHROPIC_VERSION = "2023-06-01";
479
+ var AnthropicAdapter = class {
480
+ static {
481
+ __name(this, "AnthropicAdapter");
482
+ }
483
+ providerId = "anthropic";
484
+ buildRequest(req) {
485
+ const entry = CLOUD_PROVIDERS_BY_ID.get("anthropic");
486
+ if (!entry) throw new Error("anthropic provider not registered");
487
+ const body = {
488
+ model: req.model,
489
+ max_tokens: req.hyperparams?.maxTokens ?? 4096,
490
+ messages: [
491
+ {
492
+ role: "user",
493
+ content: req.prompt
494
+ }
495
+ ]
496
+ };
497
+ if (req.hyperparams?.temperature !== void 0) body.temperature = req.hyperparams.temperature;
498
+ return {
499
+ url: entry.endpoint,
500
+ init: {
501
+ method: "POST",
502
+ headers: {
503
+ "Content-Type": "application/json",
504
+ "x-api-key": req.apiKey,
505
+ "anthropic-version": ANTHROPIC_VERSION,
506
+ // Required when the request runs from a browser-like environment;
507
+ // Tauri / Electron embedded fetch occasionally tags itself as such.
508
+ "anthropic-dangerous-direct-browser-access": "true"
509
+ },
510
+ body: JSON.stringify(body)
511
+ }
512
+ };
513
+ }
514
+ parseResponse(_httpStatus, body) {
515
+ const blocks = readArray(body, "content");
516
+ if (blocks.length === 0) {
517
+ throw new Error("Anthropic response had no content[] blocks");
518
+ }
519
+ const textParts = [];
520
+ for (const block of blocks) {
521
+ const type = readString(block, "type");
522
+ if (type === "text") {
523
+ const t = readString(block, "text");
524
+ if (t) textParts.push(t);
525
+ }
526
+ }
527
+ if (textParts.length === 0) {
528
+ const stop = readString(body, "stop_reason") ?? "unknown";
529
+ throw new Error(`Anthropic returned no text blocks (stop_reason=${stop}); tool_use / thinking-only responses are not consumable by this node`);
530
+ }
531
+ const stopStr = readString(body, "stop_reason") ?? "end_turn";
532
+ return {
533
+ text: textParts.join(""),
534
+ finishReason: STOP_REASON_MAP[stopStr] ?? "unknown",
535
+ usage: {
536
+ promptTokens: readNumber(body, "usage", "input_tokens"),
537
+ completionTokens: readNumber(body, "usage", "output_tokens"),
538
+ // Anthropic doesn't publish a total — it's input + output and we
539
+ // expose it pre-computed for parity with OpenAI usage telemetry.
540
+ totalTokens: (readNumber(body, "usage", "input_tokens") ?? 0) + (readNumber(body, "usage", "output_tokens") ?? 0) || void 0
541
+ },
542
+ raw: body
543
+ };
544
+ }
545
+ parseError(httpStatus, body, fallbackText) {
546
+ const msg = readString(body, "error", "message") ?? readString(body, "message");
547
+ if (msg) return new Error(`HTTP ${httpStatus}: ${msg}`);
548
+ return new Error(defaultErrorMessage(httpStatus, body, fallbackText));
549
+ }
550
+ };
551
+
552
+ // src/modules/llm/adapters/google.adapter.ts
553
+ var FINISH_REASON_MAP2 = {
554
+ STOP: "stop",
555
+ MAX_TOKENS: "length",
556
+ SAFETY: "content_filter",
557
+ RECITATION: "content_filter",
558
+ PROHIBITED_CONTENT: "content_filter",
559
+ SPII: "content_filter",
560
+ BLOCKLIST: "content_filter",
561
+ LANGUAGE: "content_filter",
562
+ OTHER: "unknown",
563
+ TOOL_CODE_FAILURE: "tool_calls"
564
+ };
565
+ var GoogleAdapter = class {
566
+ static {
567
+ __name(this, "GoogleAdapter");
568
+ }
569
+ providerId = "google";
570
+ buildRequest(req) {
571
+ const entry = CLOUD_PROVIDERS_BY_ID.get("google");
572
+ if (!entry) throw new Error("google provider not registered");
573
+ const url = entry.endpoint.replace("{model}", encodeURIComponent(req.model));
574
+ const generationConfig = {};
575
+ if (req.hyperparams?.temperature !== void 0) {
576
+ generationConfig.temperature = req.hyperparams.temperature;
577
+ }
578
+ if (req.hyperparams?.maxTokens !== void 0) {
579
+ generationConfig.maxOutputTokens = req.hyperparams.maxTokens;
580
+ }
581
+ if (req.hyperparams?.forceJson) {
582
+ generationConfig.responseMimeType = "application/json";
583
+ }
584
+ const body = {
585
+ contents: [
586
+ {
587
+ role: "user",
588
+ parts: [
589
+ {
590
+ text: req.prompt
591
+ }
592
+ ]
593
+ }
594
+ ]
595
+ };
596
+ if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
597
+ return {
598
+ url,
599
+ init: {
600
+ method: "POST",
601
+ headers: {
602
+ "Content-Type": "application/json",
603
+ "x-goog-api-key": req.apiKey
604
+ },
605
+ body: JSON.stringify(body)
606
+ }
607
+ };
608
+ }
609
+ parseResponse(_httpStatus, body) {
610
+ const blockReason = readString(body, "promptFeedback", "blockReason");
611
+ if (blockReason) {
612
+ throw new Error(`Google blocked the prompt: blockReason=${blockReason}`);
613
+ }
614
+ const candidates = readArray(body, "candidates");
615
+ if (candidates.length === 0) {
616
+ throw new Error("Google response had no candidates[]");
617
+ }
618
+ const candidate = candidates[0];
619
+ const finishStr = readString(candidate, "finishReason") ?? "STOP";
620
+ const parts = readArray(candidate, "content", "parts");
621
+ const textParts = [];
622
+ for (const part of parts) {
623
+ if (readString(part, "functionCall", "name")) continue;
624
+ const isThought = part?.thought === true;
625
+ if (isThought) continue;
626
+ const t = readString(part, "text");
627
+ if (t) textParts.push(t);
628
+ }
629
+ if (textParts.length === 0) {
630
+ throw new Error(`Google returned no text parts (finishReason=${finishStr}); function-call / thought-only responses are not consumable by this node`);
631
+ }
632
+ return {
633
+ text: textParts.join(""),
634
+ finishReason: FINISH_REASON_MAP2[finishStr] ?? "unknown",
635
+ usage: {
636
+ promptTokens: readNumber(body, "usageMetadata", "promptTokenCount"),
637
+ completionTokens: readNumber(body, "usageMetadata", "candidatesTokenCount"),
638
+ totalTokens: readNumber(body, "usageMetadata", "totalTokenCount")
639
+ },
640
+ raw: body
641
+ };
642
+ }
643
+ parseError(httpStatus, body, fallbackText) {
644
+ const msg = readString(body, "error", "message");
645
+ if (msg) return new Error(`HTTP ${httpStatus}: ${msg}`);
646
+ return new Error(defaultErrorMessage(httpStatus, body, fallbackText));
647
+ }
648
+ };
649
+
650
+ // src/modules/llm/adapters/openai-compat.adapter.ts
651
+ var FINISH_REASON_MAP3 = {
652
+ stop: "stop",
653
+ length: "length",
654
+ content_filter: "content_filter",
655
+ tool_calls: "tool_calls",
656
+ function_call: "tool_calls"
657
+ };
658
+ var OpenAICompatAdapter = class {
659
+ static {
660
+ __name(this, "OpenAICompatAdapter");
661
+ }
662
+ /** Subclasses can extend if they need extra Authorization shapes. */
663
+ buildHeaders(req) {
664
+ return {
665
+ "Content-Type": "application/json",
666
+ "Authorization": `Bearer ${req.apiKey}`
667
+ };
668
+ }
669
+ buildRequest(req) {
670
+ const body = {
671
+ model: req.model,
672
+ messages: [
673
+ {
674
+ role: "user",
675
+ content: req.prompt
676
+ }
677
+ ]
678
+ };
679
+ if (req.hyperparams?.temperature !== void 0) body.temperature = req.hyperparams.temperature;
680
+ if (req.hyperparams?.maxTokens !== void 0) body.max_tokens = req.hyperparams.maxTokens;
681
+ if (req.hyperparams?.forceJson) body.response_format = {
682
+ type: "json_object"
683
+ };
684
+ return {
685
+ url: this.endpoint,
686
+ init: {
687
+ method: "POST",
688
+ headers: this.buildHeaders(req),
689
+ body: JSON.stringify(body)
690
+ }
691
+ };
692
+ }
693
+ parseResponse(_httpStatus, body) {
694
+ if (this.validateResponseBody) this.validateResponseBody(body);
695
+ const choices = readArray(body, "choices");
696
+ if (choices.length === 0) {
697
+ throw new Error(`${this.providerId} response had no choices[]`);
698
+ }
699
+ const choice = choices[0];
700
+ const content = readString(choice, "message", "content");
701
+ if (typeof content !== "string" || content.length === 0) {
702
+ const finish = readString(choice, "finish_reason") ?? "unknown";
703
+ throw new Error(`${this.providerId} returned no message.content (finish_reason=${finish})`);
704
+ }
705
+ const finishStr = readString(choice, "finish_reason") ?? "stop";
706
+ return {
707
+ text: content,
708
+ finishReason: FINISH_REASON_MAP3[finishStr] ?? "unknown",
709
+ usage: {
710
+ promptTokens: readNumber(body, "usage", "prompt_tokens"),
711
+ completionTokens: readNumber(body, "usage", "completion_tokens"),
712
+ totalTokens: readNumber(body, "usage", "total_tokens")
713
+ },
714
+ raw: body
715
+ };
716
+ }
717
+ parseError(httpStatus, body, fallbackText) {
718
+ return new Error(defaultErrorMessage(httpStatus, body, fallbackText));
719
+ }
720
+ };
721
+
722
+ // src/modules/llm/adapters/moonshot.adapter.ts
723
+ var MoonshotAdapter = class extends OpenAICompatAdapter {
724
+ static {
725
+ __name(this, "MoonshotAdapter");
726
+ }
727
+ providerId = "moonshot";
728
+ endpoint = (() => {
729
+ const e = CLOUD_PROVIDERS_BY_ID.get("moonshot")?.endpoint;
730
+ if (!e) throw new Error("moonshot provider missing from CLOUD_PROVIDERS table");
731
+ return e;
732
+ })();
733
+ };
734
+
735
+ // src/modules/llm/adapters/minimax.adapter.ts
736
+ var MINIMAX_TRANSIENT_CODES = /* @__PURE__ */ new Set([
737
+ 1002,
738
+ 1004,
739
+ 1027,
740
+ 2013,
741
+ 2064
742
+ ]);
743
+ var MinimaxAdapter = class extends OpenAICompatAdapter {
744
+ static {
745
+ __name(this, "MinimaxAdapter");
746
+ }
747
+ providerId = "minimax";
748
+ endpoint = (() => {
749
+ const e = CLOUD_PROVIDERS_BY_ID.get("minimax")?.endpoint;
750
+ if (!e) throw new Error("minimax provider missing from CLOUD_PROVIDERS table");
751
+ return e;
752
+ })();
753
+ validateResponseBody(body) {
754
+ const code = readNumber(body, "base_resp", "status_code");
755
+ if (code !== void 0 && code !== 0) {
756
+ const msg = readString(body, "base_resp", "status_msg") ?? "unknown";
757
+ const transient = MINIMAX_TRANSIENT_CODES.has(code);
758
+ throw new Error(`MiniMax base_resp ${code}: ${msg}` + (transient ? " (transient)" : ""));
759
+ }
760
+ }
761
+ isTransientError(err) {
762
+ const msg = String(err?.message ?? err ?? "");
763
+ if (msg.includes("(transient)")) return true;
764
+ for (const code of MINIMAX_TRANSIENT_CODES) {
765
+ if (msg.includes(`base_resp ${code}`)) return true;
766
+ }
767
+ return false;
768
+ }
769
+ };
770
+
771
+ // src/modules/llm/adapters/zhipu.adapter.ts
772
+ var ZhipuAdapter = class extends OpenAICompatAdapter {
773
+ static {
774
+ __name(this, "ZhipuAdapter");
775
+ }
776
+ providerId = "zhipu";
777
+ endpoint = (() => {
778
+ const e = CLOUD_PROVIDERS_BY_ID.get("zhipu")?.endpoint;
779
+ if (!e) throw new Error("zhipu provider missing from CLOUD_PROVIDERS table");
780
+ return e;
781
+ })();
782
+ };
783
+
784
+ // src/modules/llm/adapters/index.ts
785
+ var ADAPTERS = /* @__PURE__ */ new Map([
786
+ [
787
+ "openai",
788
+ new OpenAIAdapter()
789
+ ],
790
+ [
791
+ "anthropic",
792
+ new AnthropicAdapter()
793
+ ],
794
+ [
795
+ "google",
796
+ new GoogleAdapter()
797
+ ],
798
+ [
799
+ "moonshot",
800
+ new MoonshotAdapter()
801
+ ],
802
+ [
803
+ "minimax",
804
+ new MinimaxAdapter()
805
+ ],
806
+ [
807
+ "zhipu",
808
+ new ZhipuAdapter()
809
+ ]
810
+ ]);
811
+ function getAdapter(providerId) {
812
+ const adapter = ADAPTERS.get(providerId);
813
+ if (!adapter) {
814
+ throw new Error(`No LLM response adapter registered for provider '${providerId}'`);
815
+ }
816
+ return adapter;
817
+ }
818
+ __name(getAdapter, "getAdapter");
819
+
820
+ // src/modules/llm/llm-provider.ts
821
+ function _ts_decorate2(decorators, target, key, desc) {
822
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
823
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
824
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
825
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
826
+ }
827
+ __name(_ts_decorate2, "_ts_decorate");
828
+ function _ts_metadata2(k, v) {
829
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
830
+ }
831
+ __name(_ts_metadata2, "_ts_metadata");
832
+ function _ts_param(paramIndex, decorator) {
833
+ return function(target, key) {
834
+ decorator(target, key, paramIndex);
835
+ };
836
+ }
837
+ __name(_ts_param, "_ts_param");
838
+ function buildSupportedModels() {
839
+ const out = {};
840
+ for (const entry of CLOUD_PROVIDERS) {
841
+ for (const tier of [
842
+ "top",
843
+ "mid",
844
+ "budget"
845
+ ]) {
846
+ const desc = entry.models[tier];
847
+ out[`${entry.id}/${desc.modelId}`] = {
848
+ provider: "cloud",
849
+ providerId: entry.id,
850
+ modelId: desc.modelId
851
+ };
852
+ }
853
+ }
854
+ for (const m of OLLAMA_DEFAULT_MODELS) {
855
+ out[`ollama/${m.modelId}`] = {
856
+ provider: "ollama",
857
+ providerId: "",
858
+ modelId: m.modelId
859
+ };
860
+ }
861
+ return out;
862
+ }
863
+ __name(buildSupportedModels, "buildSupportedModels");
864
+ function buildModelMetadata() {
865
+ const out = {};
866
+ for (const entry of CLOUD_PROVIDERS) {
867
+ for (const tier of [
868
+ "top",
869
+ "mid",
870
+ "budget"
871
+ ]) {
872
+ const d = entry.models[tier];
873
+ out[d.modelId] = d;
874
+ }
875
+ }
876
+ for (const m of OLLAMA_DEFAULT_MODELS) {
877
+ out[m.modelId] = m;
878
+ }
879
+ return out;
880
+ }
881
+ __name(buildModelMetadata, "buildModelMetadata");
882
+ var SUPPORTED_MODELS = buildSupportedModels();
883
+ var MODEL_METADATA = buildModelMetadata();
884
+ function isTransientLlmError(err) {
885
+ const msg = String(err?.message ?? err ?? "").toLowerCase();
886
+ return msg.includes("2064") || msg.includes("high load") || msg.includes("overloaded") || msg.includes("rate limit") || msg.includes("rate-limit") || msg.includes("too many requests") || msg.includes("429") || msg.includes("503") || msg.includes("502") || msg.includes("504") || msg.includes("timeout") || msg.includes("econn") || msg.includes("etimedout") || msg.includes("enotfound") || msg.includes("socket hang up") || msg.includes("runner process") || msg.includes("%!w") || msg.includes("unexpected eof") || msg.includes("try again") || msg.includes("(transient)");
887
+ }
888
+ __name(isTransientLlmError, "isTransientLlmError");
889
+ var RETRY_SCHEDULE_MS = [
890
+ 1e3,
891
+ 3e3,
892
+ 8e3
893
+ ];
894
+ var LlmProviderHelper = class {
895
+ static {
896
+ __name(this, "LlmProviderHelper");
897
+ }
898
+ synapseia;
899
+ ollamaHelper = new OllamaHelper();
900
+ // F3-C7 — optional Synapseia client. When Nest wires it, any model
901
+ // with `provider: 'synapseia'` is dispatched through it; otherwise we
902
+ // fall back to cloud/ollama so the node keeps serving even pre-F3.
903
+ constructor(synapseia) {
904
+ this.synapseia = synapseia;
905
+ }
906
+ // ── Public methods ────────────────────────────────────────────────────────
907
+ toErrorMessage(error) {
908
+ try {
909
+ return String(error?.message ?? "Unknown error");
910
+ } catch {
911
+ return "Unknown error";
912
+ }
913
+ }
914
+ /**
915
+ * Resolve a slug against the whitelist. Returns null for anything we
916
+ * don't recognise so callers (CLI, config validation) can decide
917
+ * whether to migrate or hard-fail.
918
+ */
919
+ parseModel(modelStr) {
920
+ const known = SUPPORTED_MODELS[modelStr];
921
+ if (known) return known;
922
+ const slash = modelStr.indexOf("/");
923
+ if (slash <= 0) return null;
924
+ const provider = modelStr.slice(0, slash);
925
+ const modelId = modelStr.slice(slash + 1);
926
+ if (!modelId) return null;
927
+ if (provider === "ollama") {
928
+ return {
929
+ provider: "ollama",
930
+ providerId: "",
931
+ modelId
932
+ };
933
+ }
934
+ if (provider === "synapseia") {
935
+ return {
936
+ provider: "synapseia",
937
+ providerId: "",
938
+ modelId
939
+ };
940
+ }
941
+ if (CLOUD_PROVIDERS_BY_ID.has(provider)) {
942
+ return {
943
+ provider: "cloud",
944
+ providerId: provider,
945
+ modelId
946
+ };
947
+ }
948
+ return null;
949
+ }
950
+ async checkLLM(model, config) {
951
+ if (model.provider === "ollama") return this.checkOllamaLLM(model);
952
+ if (model.provider === "cloud") return this.checkCloudLLM(model, config);
953
+ if (model.provider === "synapseia") return this.checkSynapseiaLLM(model);
954
+ return {
955
+ available: false,
956
+ model,
957
+ estimatedLatencyMs: 0,
958
+ error: "Unknown provider"
959
+ };
960
+ }
961
+ async generateLLM(model, prompt, config, hyperparams) {
962
+ let raw;
963
+ let lastErr;
964
+ for (let attempt = 0; attempt <= RETRY_SCHEDULE_MS.length; attempt++) {
965
+ try {
966
+ if (model.provider === "ollama") {
967
+ raw = await this.generateOllamaLLM(model, prompt, hyperparams);
968
+ } else if (model.provider === "cloud") {
969
+ raw = await this.generateCloudLLM(model, prompt, config, hyperparams);
970
+ } else if (model.provider === "synapseia") {
971
+ raw = await this.generateSynapseiaLLM(model, prompt, hyperparams);
972
+ } else {
973
+ throw new Error("Unknown provider");
974
+ }
975
+ break;
976
+ } catch (err) {
977
+ lastErr = err;
978
+ const adapterTransient = model.provider === "cloud" && model.providerId ? this.adapterIsTransient(model.providerId, err) : false;
979
+ if (attempt >= RETRY_SCHEDULE_MS.length || !isTransientLlmError(err) && !adapterTransient) {
980
+ throw err;
981
+ }
982
+ const wait = RETRY_SCHEDULE_MS[attempt];
983
+ logger_default.warn(`[LLM] transient error on attempt ${attempt + 1}/${RETRY_SCHEDULE_MS.length + 1} (${this.toErrorMessage(err)}) \u2014 retrying in ${wait}ms`);
984
+ await new Promise((resolve) => setTimeout(resolve, wait));
985
+ }
986
+ }
987
+ if (raw === void 0) throw lastErr ?? new Error("LLM generation failed");
988
+ return stripReasoning(raw);
989
+ }
990
+ checkOllama() {
991
+ return this.ollamaHelper.checkOllama();
992
+ }
993
+ generateOllama(prompt, modelId) {
994
+ return this.ollamaHelper.generate(prompt, modelId);
995
+ }
996
+ get supportedModels() {
997
+ return SUPPORTED_MODELS;
998
+ }
999
+ get modelMetadata() {
1000
+ return MODEL_METADATA;
1001
+ }
1002
+ // ── Private: Ollama ───────────────────────────────────────────────────────
1003
+ async checkOllamaLLM(model) {
1004
+ try {
1005
+ const status = await this.ollamaHelper.checkOllama();
1006
+ if (!status.available) {
1007
+ return {
1008
+ available: false,
1009
+ model,
1010
+ estimatedLatencyMs: 0,
1011
+ error: status.error || "Ollama not available"
1012
+ };
1013
+ }
1014
+ const meta = MODEL_METADATA[model.modelId];
1015
+ const modelAvailable = status.models.includes(model.modelId);
1016
+ if (!modelAvailable) {
1017
+ return {
1018
+ available: false,
1019
+ model,
1020
+ estimatedLatencyMs: meta?.latencyMs ?? 500,
1021
+ error: `Model ${model.modelId} not found. Pull with: ollama pull ${model.modelId}`
1022
+ };
1023
+ }
1024
+ return {
1025
+ available: true,
1026
+ model,
1027
+ estimatedLatencyMs: meta?.latencyMs ?? 500,
1028
+ maxTokens: meta?.maxTokens
1029
+ };
1030
+ } catch (error) {
1031
+ return {
1032
+ available: false,
1033
+ model,
1034
+ estimatedLatencyMs: 0,
1035
+ error: this.toErrorMessage(error)
1036
+ };
1037
+ }
1038
+ }
1039
+ async generateOllamaLLM(model, prompt, hyperparams) {
1040
+ return this.ollamaHelper.generate(prompt, model.modelId, void 0, hyperparams);
1041
+ }
1042
+ // ── Private: Cloud routing ────────────────────────────────────────────────
1043
+ async checkCloudLLM(model, config) {
1044
+ if (!config?.apiKey) {
1045
+ return {
1046
+ available: false,
1047
+ model,
1048
+ estimatedLatencyMs: 0,
1049
+ error: "API key required for cloud provider"
1050
+ };
1051
+ }
1052
+ if (config.baseUrl) {
1053
+ const safeUrl = config.baseUrl.split("?")[0];
1054
+ logger_default.warn(`[LLM] config.baseUrl is set ('${safeUrl}') but is ignored \u2014 endpoints are hardcoded per provider`);
1055
+ }
1056
+ if (!model.providerId || !CLOUD_PROVIDERS_BY_ID.has(model.providerId)) {
1057
+ return {
1058
+ available: false,
1059
+ model,
1060
+ estimatedLatencyMs: 0,
1061
+ error: `Unknown cloud provider '${model.providerId}'`
1062
+ };
1063
+ }
1064
+ const meta = MODEL_METADATA[model.modelId];
1065
+ try {
1066
+ await this.runAdapterRequest(model, "Hi", config.apiKey, {
1067
+ maxTokens: 16
1068
+ });
1069
+ return {
1070
+ available: true,
1071
+ model,
1072
+ estimatedLatencyMs: meta?.latencyMs ?? 400,
1073
+ estimatedCostPerCall: meta?.costPerCall,
1074
+ maxTokens: meta?.maxTokens
1075
+ };
1076
+ } catch (error) {
1077
+ return {
1078
+ available: false,
1079
+ model,
1080
+ estimatedLatencyMs: 0,
1081
+ error: this.toErrorMessage(error)
1082
+ };
1083
+ }
1084
+ }
1085
+ async generateCloudLLM(model, prompt, config, hyperparams) {
1086
+ if (!config?.apiKey) throw new Error("API key required for cloud provider");
1087
+ if (!model.providerId || !CLOUD_PROVIDERS_BY_ID.has(model.providerId)) {
1088
+ throw new Error(`Unknown cloud provider '${model.providerId}'`);
1089
+ }
1090
+ if (config.baseUrl) {
1091
+ const safeUrl = config.baseUrl.split("?")[0];
1092
+ logger_default.warn(`[LLM] config.baseUrl is set ('${safeUrl}') but is ignored \u2014 endpoints are hardcoded per provider`);
1093
+ }
1094
+ return this.runAdapterRequest(model, prompt, config.apiKey, hyperparams);
1095
+ }
1096
+ async runAdapterRequest(model, prompt, apiKey, hyperparams) {
1097
+ const adapter = getAdapter(model.providerId);
1098
+ const chatReq = {
1099
+ model: model.modelId,
1100
+ prompt,
1101
+ apiKey,
1102
+ hyperparams: hyperparams ? {
1103
+ temperature: hyperparams.temperature,
1104
+ maxTokens: hyperparams.maxTokens,
1105
+ forceJson: hyperparams.forceJson
1106
+ } : void 0
1107
+ };
1108
+ const { url, init } = adapter.buildRequest(chatReq);
1109
+ const response = await fetch(url, init);
1110
+ const text = await response.text().catch(() => "");
1111
+ let body = null;
1112
+ if (text) {
1113
+ try {
1114
+ body = JSON.parse(text);
1115
+ } catch {
1116
+ body = null;
1117
+ }
1118
+ }
1119
+ if (!response.ok) {
1120
+ throw adapter.parseError(response.status, body, text);
1121
+ }
1122
+ const normalized = adapter.parseResponse(response.status, body);
1123
+ return normalized.text;
1124
+ }
1125
+ adapterIsTransient(providerId, err) {
1126
+ try {
1127
+ const adapter = getAdapter(providerId);
1128
+ return Boolean(adapter.isTransientError?.(err));
1129
+ } catch {
1130
+ return false;
1131
+ }
1132
+ }
1133
+ // ── Private: Synapseia (F3-C7) ────────────────────────────────────────────
1134
+ async checkSynapseiaLLM(model) {
1135
+ if (!this.synapseia) {
1136
+ return {
1137
+ available: false,
1138
+ model,
1139
+ estimatedLatencyMs: 0,
1140
+ error: "Synapseia client not wired"
1141
+ };
1142
+ }
1143
+ const ok = await this.synapseia.isAvailable();
1144
+ return {
1145
+ available: ok,
1146
+ model,
1147
+ estimatedLatencyMs: 600,
1148
+ error: ok ? void 0 : "local serving runtime not reachable"
1149
+ };
1150
+ }
1151
+ async generateSynapseiaLLM(model, prompt, hyperparams) {
1152
+ if (!this.synapseia) {
1153
+ throw new Error("Synapseia client not wired \u2014 operator must launch llama.cpp + register swap hook");
1154
+ }
1155
+ const expected = model.synapseiaVersion;
1156
+ const active = this.synapseia.getActiveVersion();
1157
+ if (expected && active && expected !== active) {
1158
+ throw new Error(`Synapseia version mismatch: caller asked ${expected}, node is serving ${active}`);
1159
+ }
1160
+ const result = await this.synapseia.generate({
1161
+ messages: [
1162
+ {
1163
+ role: "user",
1164
+ content: prompt
1165
+ }
1166
+ ],
1167
+ temperature: hyperparams?.temperature,
1168
+ maxTokens: hyperparams?.maxTokens
1169
+ });
1170
+ return result.content;
1171
+ }
1172
+ };
1173
+ LlmProviderHelper = _ts_decorate2([
1174
+ Injectable2(),
1175
+ _ts_param(0, Optional()),
1176
+ _ts_metadata2("design:type", Function),
1177
+ _ts_metadata2("design:paramtypes", [
1178
+ typeof SynapseiaServingClient === "undefined" ? Object : SynapseiaServingClient
1179
+ ])
1180
+ ], LlmProviderHelper);
1181
+
1182
+ // src/modules/p2p/stream-codec.ts
1183
+ var MAX_FRAME_BYTES = 1 << 20;
1184
+ async function sendJsonOverStream(stream, obj) {
1185
+ const json = new TextEncoder().encode(JSON.stringify(obj));
1186
+ if (json.byteLength > MAX_FRAME_BYTES) {
1187
+ throw new Error(`stream-codec: payload ${json.byteLength} > ${MAX_FRAME_BYTES}`);
1188
+ }
1189
+ const full = new Uint8Array(4 + json.byteLength);
1190
+ new DataView(full.buffer, full.byteOffset, 4).setUint32(0, json.byteLength, true);
1191
+ full.set(json, 4);
1192
+ const ok = stream.send(full);
1193
+ if (!ok) {
1194
+ await new Promise((resolve, reject) => {
1195
+ const onDrain = /* @__PURE__ */ __name(() => {
1196
+ cleanup();
1197
+ resolve();
1198
+ }, "onDrain");
1199
+ const onClose = /* @__PURE__ */ __name(() => {
1200
+ cleanup();
1201
+ reject(new Error("stream closed before drain"));
1202
+ }, "onClose");
1203
+ const cleanup = /* @__PURE__ */ __name(() => {
1204
+ stream.removeEventListener?.("drain", onDrain);
1205
+ stream.removeEventListener?.("close", onClose);
1206
+ stream.removeEventListener?.("remoteCloseRead", onClose);
1207
+ }, "cleanup");
1208
+ stream.addEventListener?.("drain", onDrain, {
1209
+ once: true
1210
+ });
1211
+ stream.addEventListener?.("close", onClose, {
1212
+ once: true
1213
+ });
1214
+ stream.addEventListener?.("remoteCloseRead", onClose, {
1215
+ once: true
1216
+ });
1217
+ });
1218
+ }
1219
+ try {
1220
+ await stream.closeWrite?.();
1221
+ } catch {
1222
+ }
1223
+ }
1224
+ __name(sendJsonOverStream, "sendJsonOverStream");
1225
+ async function readJsonFromStream(stream) {
1226
+ const buffers = [];
1227
+ let total = 0;
1228
+ let expected = null;
1229
+ for await (const chunk of stream) {
1230
+ const bytes = chunk instanceof Uint8Array ? chunk : chunk.subarray();
1231
+ buffers.push(bytes);
1232
+ total += bytes.byteLength;
1233
+ if (expected === null && total >= 4) {
1234
+ const head = coalesceFirstN(buffers, 4);
1235
+ expected = new DataView(head.buffer, head.byteOffset, 4).getUint32(0, true);
1236
+ if (expected > MAX_FRAME_BYTES) {
1237
+ throw new Error(`stream-codec: frame length ${expected} > ${MAX_FRAME_BYTES}`);
1238
+ }
1239
+ }
1240
+ if (expected !== null && total >= 4 + expected) break;
1241
+ }
1242
+ if (expected === null) throw new Error("stream-codec: stream ended before header");
1243
+ const all = coalesce(buffers);
1244
+ if (all.byteLength < 4 + expected) {
1245
+ throw new Error(`stream-codec: stream ended mid-frame (got ${all.byteLength - 4} of ${expected})`);
1246
+ }
1247
+ const payload = all.subarray(4, 4 + expected);
1248
+ return JSON.parse(new TextDecoder().decode(payload));
1249
+ }
1250
+ __name(readJsonFromStream, "readJsonFromStream");
1251
+ async function sendJsonFrame(stream, obj) {
1252
+ const json = new TextEncoder().encode(JSON.stringify(obj));
1253
+ if (json.byteLength > MAX_FRAME_BYTES) {
1254
+ throw new Error(`stream-codec: payload ${json.byteLength} > ${MAX_FRAME_BYTES}`);
1255
+ }
1256
+ const full = new Uint8Array(4 + json.byteLength);
1257
+ new DataView(full.buffer, full.byteOffset, 4).setUint32(0, json.byteLength, true);
1258
+ full.set(json, 4);
1259
+ const ok = stream.send(full);
1260
+ if (!ok) {
1261
+ await new Promise((resolve, reject) => {
1262
+ const onDrain = /* @__PURE__ */ __name(() => {
1263
+ cleanup();
1264
+ resolve();
1265
+ }, "onDrain");
1266
+ const onClose = /* @__PURE__ */ __name(() => {
1267
+ cleanup();
1268
+ reject(new Error("stream closed before drain"));
1269
+ }, "onClose");
1270
+ const cleanup = /* @__PURE__ */ __name(() => {
1271
+ stream.removeEventListener?.("drain", onDrain);
1272
+ stream.removeEventListener?.("close", onClose);
1273
+ stream.removeEventListener?.("remoteCloseRead", onClose);
1274
+ }, "cleanup");
1275
+ stream.addEventListener?.("drain", onDrain, {
1276
+ once: true
1277
+ });
1278
+ stream.addEventListener?.("close", onClose, {
1279
+ once: true
1280
+ });
1281
+ stream.addEventListener?.("remoteCloseRead", onClose, {
1282
+ once: true
1283
+ });
1284
+ });
1285
+ }
1286
+ }
1287
+ __name(sendJsonFrame, "sendJsonFrame");
1288
+ async function endJsonStream(stream) {
1289
+ try {
1290
+ await stream.closeWrite?.();
1291
+ } catch {
1292
+ }
1293
+ }
1294
+ __name(endJsonStream, "endJsonStream");
1295
+ async function readJsonFramesUntilDone(stream, onFrame, isDone, hardCapFrames = 5e6) {
1296
+ const buffers = [];
1297
+ let total = 0;
1298
+ let expected = null;
1299
+ let frames = 0;
1300
+ let lastDone = null;
1301
+ for await (const chunk of stream) {
1302
+ const bytes = chunk instanceof Uint8Array ? chunk : chunk.subarray();
1303
+ buffers.push(bytes);
1304
+ total += bytes.byteLength;
1305
+ while (true) {
1306
+ if (expected === null) {
1307
+ if (total < 4) break;
1308
+ const head = coalesceFirstN(buffers, 4);
1309
+ expected = new DataView(head.buffer, head.byteOffset, 4).getUint32(0, true);
1310
+ if (expected > MAX_FRAME_BYTES) {
1311
+ throw new Error(`stream-codec: frame length ${expected} > ${MAX_FRAME_BYTES}`);
1312
+ }
1313
+ }
1314
+ if (total < 4 + expected) break;
1315
+ const all = coalesce(buffers);
1316
+ const payload = all.subarray(4, 4 + expected);
1317
+ const frame = JSON.parse(new TextDecoder().decode(payload));
1318
+ const remainder = all.subarray(4 + expected);
1319
+ buffers.length = 0;
1320
+ total = 0;
1321
+ if (remainder.byteLength > 0) {
1322
+ buffers.push(remainder);
1323
+ total = remainder.byteLength;
1324
+ }
1325
+ expected = null;
1326
+ frames++;
1327
+ if (frames > hardCapFrames) {
1328
+ throw new Error(`stream-codec: exceeded ${hardCapFrames} frames`);
1329
+ }
1330
+ if (isDone(frame)) {
1331
+ lastDone = frame;
1332
+ return lastDone;
1333
+ }
1334
+ await onFrame(frame);
1335
+ }
1336
+ }
1337
+ if (lastDone) return lastDone;
1338
+ throw new Error("stream-codec: stream ended before done frame");
1339
+ }
1340
+ __name(readJsonFramesUntilDone, "readJsonFramesUntilDone");
1341
+ function coalesce(chunks) {
1342
+ let total = 0;
1343
+ for (const c of chunks) total += c.byteLength;
1344
+ const out = new Uint8Array(total);
1345
+ let off = 0;
1346
+ for (const c of chunks) {
1347
+ out.set(c, off);
1348
+ off += c.byteLength;
1349
+ }
1350
+ return out;
1351
+ }
1352
+ __name(coalesce, "coalesce");
1353
+ function coalesceFirstN(chunks, n) {
1354
+ const out = new Uint8Array(n);
1355
+ let off = 0;
1356
+ for (const c of chunks) {
1357
+ if (off >= n) break;
1358
+ const take = Math.min(c.byteLength, n - off);
1359
+ out.set(c.subarray(0, take), off);
1360
+ off += take;
1361
+ }
1362
+ return out;
1363
+ }
1364
+ __name(coalesceFirstN, "coalesceFirstN");
1365
+
1366
+ export {
1367
+ CLOUD_PROVIDERS,
1368
+ CLOUD_PROVIDERS_BY_ID,
1369
+ resolveSlug,
1370
+ FALLBACK_MODEL_SLUG,
1371
+ topSlugFor,
1372
+ stripReasoning,
1373
+ SynapseiaServingClient,
1374
+ LlmProviderHelper,
1375
+ sendJsonOverStream,
1376
+ readJsonFromStream,
1377
+ sendJsonFrame,
1378
+ endJsonStream,
1379
+ readJsonFramesUntilDone
1380
+ };