@caupulican/pi-adaptative 0.81.15 → 0.81.17

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 (65) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/bundled-resources/runtimes/hf-transformers-openai-server.py +427 -0
  3. package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +18 -0
  4. package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +14 -7
  5. package/dist/core/agent-session.d.ts +15 -3
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +201 -25
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/local-runtime-controller.d.ts +17 -9
  10. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  11. package/dist/core/local-runtime-controller.js +124 -20
  12. package/dist/core/local-runtime-controller.js.map +1 -1
  13. package/dist/core/models/adaptation-store.d.ts +5 -0
  14. package/dist/core/models/adaptation-store.d.ts.map +1 -1
  15. package/dist/core/models/adaptation-store.js +16 -0
  16. package/dist/core/models/adaptation-store.js.map +1 -1
  17. package/dist/core/models/context-sizing.d.ts +30 -0
  18. package/dist/core/models/context-sizing.d.ts.map +1 -0
  19. package/dist/core/models/context-sizing.js +90 -0
  20. package/dist/core/models/context-sizing.js.map +1 -0
  21. package/dist/core/models/default-model-suggestions.d.ts +4 -4
  22. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  23. package/dist/core/models/default-model-suggestions.js +9 -0
  24. package/dist/core/models/default-model-suggestions.js.map +1 -1
  25. package/dist/core/models/local-registration.d.ts +12 -0
  26. package/dist/core/models/local-registration.d.ts.map +1 -1
  27. package/dist/core/models/local-registration.js +68 -0
  28. package/dist/core/models/local-registration.js.map +1 -1
  29. package/dist/core/models/local-runtime.d.ts +96 -1
  30. package/dist/core/models/local-runtime.d.ts.map +1 -1
  31. package/dist/core/models/local-runtime.js +338 -4
  32. package/dist/core/models/local-runtime.js.map +1 -1
  33. package/dist/core/models/model-ref.d.ts +4 -0
  34. package/dist/core/models/model-ref.d.ts.map +1 -1
  35. package/dist/core/models/model-ref.js +12 -3
  36. package/dist/core/models/model-ref.js.map +1 -1
  37. package/dist/core/models/perf-profile.d.ts +36 -0
  38. package/dist/core/models/perf-profile.d.ts.map +1 -0
  39. package/dist/core/models/perf-profile.js +163 -0
  40. package/dist/core/models/perf-profile.js.map +1 -0
  41. package/dist/core/models/runtime-arbiter.d.ts +57 -0
  42. package/dist/core/models/runtime-arbiter.d.ts.map +1 -0
  43. package/dist/core/models/runtime-arbiter.js +49 -0
  44. package/dist/core/models/runtime-arbiter.js.map +1 -0
  45. package/dist/core/tool-repair-health.d.ts.map +1 -1
  46. package/dist/core/tool-repair-health.js +2 -1
  47. package/dist/core/tool-repair-health.js.map +1 -1
  48. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  49. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  50. package/dist/modes/interactive/interactive-mode.js +4 -0
  51. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  52. package/dist/modes/interactive/local-model-commands.d.ts +3 -1
  53. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  54. package/dist/modes/interactive/local-model-commands.js +163 -22
  55. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  56. package/docs/models.md +19 -1
  57. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  58. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  59. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  60. package/examples/extensions/sandbox/package-lock.json +2 -2
  61. package/examples/extensions/sandbox/package.json +1 -1
  62. package/examples/extensions/with-deps/package-lock.json +2 -2
  63. package/examples/extensions/with-deps/package.json +1 -1
  64. package/npm-shrinkwrap.json +12 -12
  65. package/package.json +4 -4
@@ -1,8 +1,10 @@
1
1
  import { spawn } from "node:child_process";
2
- import { existsSync, mkdirSync } from "node:fs";
2
+ import { createWriteStream, existsSync, mkdirSync, rmSync } from "node:fs";
3
3
  import { homedir, arch as osArch, platform as osPlatform } from "node:os";
4
4
  import { delimiter, join } from "node:path";
5
+ import { pipeline } from "node:stream/promises";
5
6
  import * as nodeZlib from "node:zlib";
7
+ import { getBundledResourcesDir } from "../../config.js";
6
8
  import { spawnProcess, spawnProcessSync, waitForChildProcess } from "../../utils/child-process.js";
7
9
  /**
8
10
  * `spawnProcess(..., { stdio: ["pipe", "pipe", "pipe"] })` always yields a non-null `stdin` at
@@ -45,6 +47,65 @@ const DEFAULT_BASE_URL = "http://127.0.0.1:11434";
45
47
  const HEALTH_TIMEOUT_MS = 2_000;
46
48
  const START_POLL_ATTEMPTS = 20;
47
49
  const START_POLL_INTERVAL_MS = 500;
50
+ const TRANSFORMERS_PORT_BASE = 18_100;
51
+ const TRANSFORMERS_PORT_SPAN = 1_000;
52
+ const TRANSFORMERS_START_POLL_ATTEMPTS = 240;
53
+ const TRANSFORMERS_RUNTIME_DIR_NAME = "hf-transformers";
54
+ const TRANSFORMERS_VENV_DIR_NAME = "venv";
55
+ const TRANSFORMERS_SERVER_RELATIVE_PATH = join("runtimes", "hf-transformers-openai-server.py");
56
+ const TRANSFORMERS_PINNED_PACKAGES = ["transformers==5.13.0", "huggingface-hub==1.22.0", "safetensors==0.8.0"];
57
+ const TORCH_PINNED_VERSION = "2.12.1";
58
+ async function runCommandDefault(command, args, options = {}) {
59
+ try {
60
+ const proc = spawnProcess(command, args, {
61
+ stdio: ["ignore", "pipe", "pipe"],
62
+ env: options.env ? { ...process.env, ...options.env } : process.env,
63
+ });
64
+ let stdout = "";
65
+ let stderr = "";
66
+ proc.stdout.setEncoding("utf8");
67
+ proc.stderr.setEncoding("utf8");
68
+ proc.stdout.on("data", (chunk) => {
69
+ stdout += chunk;
70
+ options.onOutput?.(chunk);
71
+ });
72
+ proc.stderr.on("data", (chunk) => {
73
+ stderr += chunk;
74
+ options.onOutput?.(chunk);
75
+ });
76
+ const code = await waitForChildProcess(proc);
77
+ return {
78
+ ok: code === 0,
79
+ stdout,
80
+ stderr,
81
+ code,
82
+ ...(code === 0 ? {} : { error: stderr.trim() || stdout.trim() || `exit code ${code ?? "unknown"}` }),
83
+ };
84
+ }
85
+ catch (error) {
86
+ return {
87
+ ok: false,
88
+ stdout: "",
89
+ stderr: "",
90
+ error: error instanceof Error ? error.message : String(error),
91
+ };
92
+ }
93
+ }
94
+ function sanitizeCommandOutput(value) {
95
+ return value?.trim().split("\n").slice(-3).join("\n") || "unknown error";
96
+ }
97
+ function hashString(value) {
98
+ let hash = 2_166_136_261;
99
+ for (let index = 0; index < value.length; index++) {
100
+ hash ^= value.charCodeAt(index);
101
+ hash = Math.imul(hash, 16_777_619) >>> 0;
102
+ }
103
+ return hash;
104
+ }
105
+ export function resolveTransformersBaseUrl(modelId) {
106
+ const port = TRANSFORMERS_PORT_BASE + (hashString(modelId) % TRANSFORMERS_PORT_SPAN);
107
+ return `http://127.0.0.1:${port}`;
108
+ }
48
109
  export class OllamaRuntime {
49
110
  _agentDir;
50
111
  _baseUrl;
@@ -246,8 +307,6 @@ export class OllamaRuntime {
246
307
  }
247
308
  async _extractZip(input, destDir) {
248
309
  // Zip's central directory needs seekable file access — buffer to a temp file first.
249
- const { createWriteStream } = await import("node:fs");
250
- const { pipeline } = await import("node:stream/promises");
251
310
  const zipPath = join(destDir, "..", `ollama-download-${process.pid}-${Date.now()}.zip`);
252
311
  await pipeline(input, createWriteStream(zipPath));
253
312
  try {
@@ -262,7 +321,6 @@ export class OllamaRuntime {
262
321
  }
263
322
  finally {
264
323
  try {
265
- const { rmSync } = await import("node:fs");
266
324
  rmSync(zipPath, { force: true });
267
325
  }
268
326
  catch {
@@ -304,6 +362,7 @@ export class OllamaRuntime {
304
362
  OLLAMA_FLASH_ATTENTION: "1",
305
363
  OLLAMA_KV_CACHE_TYPE: "q8_0",
306
364
  OLLAMA_NUM_PARALLEL: "1",
365
+ OLLAMA_KEEP_ALIVE: "30m",
307
366
  OLLAMA_MAX_LOADED_MODELS: "3",
308
367
  });
309
368
  }
@@ -324,6 +383,7 @@ export class OllamaRuntime {
324
383
  OLLAMA_FLASH_ATTENTION: "1",
325
384
  OLLAMA_KV_CACHE_TYPE: "q8_0",
326
385
  OLLAMA_NUM_PARALLEL: "1",
386
+ OLLAMA_KEEP_ALIVE: "30m",
327
387
  OLLAMA_MAX_LOADED_MODELS: "3",
328
388
  });
329
389
  }
@@ -349,6 +409,47 @@ export class OllamaRuntime {
349
409
  .filter((model) => typeof model.name === "string")
350
410
  .map((model) => ({ name: model.name, sizeBytes: model.size ?? 0 }));
351
411
  }
412
+ async show(ref) {
413
+ try {
414
+ const response = await this._fetch(`${this._baseUrl}/api/show`, {
415
+ method: "POST",
416
+ headers: { "content-type": "application/json" },
417
+ body: JSON.stringify({ model: ref }),
418
+ signal: AbortSignal.timeout(10_000),
419
+ });
420
+ if (!response.ok) {
421
+ return {
422
+ ok: false,
423
+ error: `show failed: HTTP ${response.status} ${await response.text().catch(() => "")}`,
424
+ };
425
+ }
426
+ const data = (await response.json());
427
+ return { ok: true, info: { modelInfo: data.model_info ?? {} } };
428
+ }
429
+ catch (error) {
430
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
431
+ }
432
+ }
433
+ async createFromModelfile(args) {
434
+ try {
435
+ const response = await this._fetch(`${this._baseUrl}/api/create`, {
436
+ method: "POST",
437
+ headers: { "content-type": "application/json" },
438
+ body: JSON.stringify({ model: args.name, modelfile: args.modelfile, stream: false }),
439
+ signal: AbortSignal.timeout(60_000),
440
+ });
441
+ if (!response.ok) {
442
+ return {
443
+ ok: false,
444
+ error: `create failed: HTTP ${response.status} ${await response.text().catch(() => "")}`,
445
+ };
446
+ }
447
+ return { ok: true };
448
+ }
449
+ catch (error) {
450
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
451
+ }
452
+ }
352
453
  /** Pull a model through the server API (weights land in the SERVER's models dir). */
353
454
  async pull(ref, onProgress) {
354
455
  try {
@@ -428,4 +529,237 @@ export class OllamaRuntime {
428
529
  }
429
530
  }
430
531
  }
532
+ export class TransformersRuntime {
533
+ _agentDir;
534
+ _modelId;
535
+ _baseUrl;
536
+ _fetch;
537
+ _spawn;
538
+ _exists;
539
+ _sleep;
540
+ _platform;
541
+ _runCommand;
542
+ _serverScriptPath;
543
+ _proc;
544
+ constructor(args) {
545
+ this._agentDir = args.agentDir;
546
+ this._modelId = args.modelId;
547
+ this._baseUrl = args.baseUrl?.replace(/\/$/, "") ?? resolveTransformersBaseUrl(args.modelId);
548
+ this._fetch = args.deps?.fetchFn ?? fetch;
549
+ this._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, { env: options.env }));
550
+ this._exists = args.deps?.existsFn ?? existsSync;
551
+ this._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
552
+ this._platform = args.deps?.platform ?? osPlatform;
553
+ this._runCommand = args.deps?.runCommand ?? runCommandDefault;
554
+ this._serverScriptPath =
555
+ args.deps?.transformersServerScriptPath ?? join(getBundledResourcesDir(), TRANSFORMERS_SERVER_RELATIVE_PATH);
556
+ }
557
+ get baseUrl() {
558
+ return this._baseUrl;
559
+ }
560
+ get modelId() {
561
+ return this._modelId;
562
+ }
563
+ get runtimeDir() {
564
+ return join(this._agentDir, "runtimes", TRANSFORMERS_RUNTIME_DIR_NAME);
565
+ }
566
+ get venvDir() {
567
+ return join(this.runtimeDir, TRANSFORMERS_VENV_DIR_NAME);
568
+ }
569
+ get cacheDir() {
570
+ return join(this._agentDir, "models", "huggingface");
571
+ }
572
+ get pythonPath() {
573
+ return this._platform() === "win32"
574
+ ? join(this.venvDir, "Scripts", "python.exe")
575
+ : join(this.venvDir, "bin", "python");
576
+ }
577
+ get serverPort() {
578
+ const parsed = new URL(this._baseUrl);
579
+ return parsed.port || (parsed.protocol === "https:" ? "443" : "80");
580
+ }
581
+ get serverHost() {
582
+ return new URL(this._baseUrl).hostname;
583
+ }
584
+ runtimeEnv() {
585
+ const venvBin = this._platform() === "win32" ? join(this.venvDir, "Scripts") : join(this.venvDir, "bin");
586
+ return {
587
+ ...process.env,
588
+ VIRTUAL_ENV: this.venvDir,
589
+ PATH: `${venvBin}${delimiter}${process.env.PATH ?? ""}`,
590
+ PYTHONNOUSERSITE: "1",
591
+ HF_HOME: this.cacheDir,
592
+ HUGGINGFACE_HUB_CACHE: join(this.cacheDir, "hub"),
593
+ TRANSFORMERS_CACHE: join(this.cacheDir, "transformers"),
594
+ HF_HUB_DISABLE_TELEMETRY: "1",
595
+ };
596
+ }
597
+ async serverUp() {
598
+ try {
599
+ const response = await this._fetch(`${this._baseUrl}/health`, {
600
+ signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),
601
+ });
602
+ if (!response.ok)
603
+ return false;
604
+ const body = (await response.json().catch(() => undefined));
605
+ return body?.model === this._modelId;
606
+ }
607
+ catch {
608
+ return false;
609
+ }
610
+ }
611
+ async detect() {
612
+ return {
613
+ runtimeInstalled: this._exists(this.pythonPath),
614
+ serverUp: await this.serverUp(),
615
+ baseUrl: this._baseUrl,
616
+ modelId: this._modelId,
617
+ venvDir: this.venvDir,
618
+ cacheDir: this.cacheDir,
619
+ serverScriptPath: this._serverScriptPath,
620
+ };
621
+ }
622
+ pythonCandidates() {
623
+ return this._platform() === "win32"
624
+ ? [
625
+ { command: "py", args: ["-3"] },
626
+ { command: "python", args: [] },
627
+ ]
628
+ : [
629
+ { command: "python3", args: [] },
630
+ { command: "python", args: [] },
631
+ ];
632
+ }
633
+ async findPython() {
634
+ for (const candidate of this.pythonCandidates()) {
635
+ const result = await this._runCommand(candidate.command, [...candidate.args, "--version"]);
636
+ if (result.ok)
637
+ return candidate;
638
+ }
639
+ return undefined;
640
+ }
641
+ torchInstallArgs() {
642
+ if (this._platform() === "darwin") {
643
+ return ["-m", "pip", "install", `torch==${TORCH_PINNED_VERSION}`];
644
+ }
645
+ return [
646
+ "-m",
647
+ "pip",
648
+ "install",
649
+ `torch==${TORCH_PINNED_VERSION}+cpu`,
650
+ "--index-url",
651
+ "https://download.pytorch.org/whl/cpu",
652
+ ];
653
+ }
654
+ async runPython(args, onProgress) {
655
+ return this._runCommand(this.pythonPath, args, {
656
+ env: this.runtimeEnv(),
657
+ onOutput: onProgress
658
+ ? (chunk) => {
659
+ const line = chunk.trim();
660
+ if (line)
661
+ onProgress(line);
662
+ }
663
+ : undefined,
664
+ });
665
+ }
666
+ async installManaged(onProgress) {
667
+ mkdirSync(this.runtimeDir, { recursive: true });
668
+ mkdirSync(this.cacheDir, { recursive: true });
669
+ if (!this._exists(this.pythonPath)) {
670
+ const python = await this.findPython();
671
+ if (!python)
672
+ return { ok: false, error: "python-missing" };
673
+ onProgress?.(`creating isolated Python venv at ${this.venvDir}`);
674
+ const venv = await this._runCommand(python.command, [...python.args, "-m", "venv", this.venvDir]);
675
+ if (!venv.ok)
676
+ return { ok: false, error: `venv-fail: ${sanitizeCommandOutput(venv.error ?? venv.stderr)}` };
677
+ }
678
+ if (!this._exists(this.pythonPath)) {
679
+ return { ok: false, error: `venv-fail: ${this.pythonPath} was not created` };
680
+ }
681
+ onProgress?.("upgrading pip inside pi-managed venv");
682
+ const pip = await this.runPython(["-m", "pip", "install", "--upgrade", "pip"], onProgress);
683
+ if (!pip.ok)
684
+ return { ok: false, error: `pip-upgrade-fail: ${sanitizeCommandOutput(pip.error ?? pip.stderr)}` };
685
+ onProgress?.("installing pinned Transformers runtime packages");
686
+ const transformers = await this.runPython(["-m", "pip", "install", ...TRANSFORMERS_PINNED_PACKAGES], onProgress);
687
+ if (!transformers.ok) {
688
+ return {
689
+ ok: false,
690
+ error: `transformers-install-fail: ${sanitizeCommandOutput(transformers.error ?? transformers.stderr)}`,
691
+ };
692
+ }
693
+ onProgress?.("installing pinned CPU PyTorch backend");
694
+ const torch = await this.runPython(this.torchInstallArgs(), onProgress);
695
+ if (!torch.ok)
696
+ return { ok: false, error: `torch-install-fail: ${sanitizeCommandOutput(torch.error ?? torch.stderr)}` };
697
+ const verify = await this.runPython([
698
+ "-c",
699
+ "import torch, transformers, huggingface_hub; print(transformers.__version__)",
700
+ ]);
701
+ if (!verify.ok)
702
+ return { ok: false, error: `verify-fail: ${sanitizeCommandOutput(verify.error ?? verify.stderr)}` };
703
+ return { ok: true };
704
+ }
705
+ async downloadModel(onProgress) {
706
+ if (!this._exists(this.pythonPath))
707
+ return { ok: false, error: "runtime-missing" };
708
+ if (!this._exists(this._serverScriptPath))
709
+ return { ok: false, error: `server-script-missing: ${this._serverScriptPath}` };
710
+ mkdirSync(this.cacheDir, { recursive: true });
711
+ onProgress?.(`downloading ${this._modelId} into ${this.cacheDir}`);
712
+ const result = await this.runPython([this._serverScriptPath, "--model-id", this._modelId, "--cache-dir", this.cacheDir, "--download-only"], onProgress);
713
+ return result.ok
714
+ ? { ok: true }
715
+ : { ok: false, error: `download-fail: ${sanitizeCommandOutput(result.error ?? result.stderr)}` };
716
+ }
717
+ async start() {
718
+ if (await this.serverUp())
719
+ return { started: false, reason: "already_running" };
720
+ if (!this._exists(this.pythonPath))
721
+ return { started: false, reason: "runtime_missing" };
722
+ if (!this._exists(this._serverScriptPath))
723
+ return { started: false, reason: "server_script_missing" };
724
+ mkdirSync(this.cacheDir, { recursive: true });
725
+ const env = this.runtimeEnv();
726
+ this._proc = this._spawn(this.pythonPath, [
727
+ this._serverScriptPath,
728
+ "--model-id",
729
+ this._modelId,
730
+ "--host",
731
+ this.serverHost,
732
+ "--port",
733
+ this.serverPort,
734
+ "--cache-dir",
735
+ this.cacheDir,
736
+ ], { env });
737
+ this._proc.unref?.();
738
+ for (let attempt = 0; attempt < TRANSFORMERS_START_POLL_ATTEMPTS; attempt++) {
739
+ if (await this.serverUp())
740
+ return { started: true, reason: "started" };
741
+ await this._sleep(START_POLL_INTERVAL_MS);
742
+ }
743
+ this.stop();
744
+ return { started: false, reason: "health_check_timeout" };
745
+ }
746
+ stop() {
747
+ if (!this._proc)
748
+ return { stopped: false };
749
+ this._proc.kill();
750
+ this._proc = undefined;
751
+ return { stopped: true };
752
+ }
753
+ installGuide() {
754
+ return [
755
+ "Hugging Face Transformers runtime is not installed for this model.",
756
+ `Pi can install it into its own venv at ${this.venvDir} and cache weights under ${this.cacheDir}.`,
757
+ "Manual equivalent:",
758
+ ` python3 -m venv ${this.venvDir}`,
759
+ ` ${this.pythonPath} -m pip install --upgrade pip`,
760
+ ` ${this.pythonPath} -m pip install ${TRANSFORMERS_PINNED_PACKAGES.join(" ")}`,
761
+ ` ${this.pythonPath} ${this.torchInstallArgs().join(" ")}`,
762
+ ];
763
+ }
764
+ }
431
765
  //# sourceMappingURL=local-runtime.js.map