pi-system-one 1.1.0 → 1.1.2

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/package.json CHANGED
@@ -1,22 +1,41 @@
1
1
  {
2
2
  "name": "pi-system-one",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "System One decisions for Pi. Plug in TypeSafe Jev, Reflex, or your own provider.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
- "exports": { ".": "./src/extension.ts" },
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/iamaamir/system-one.git",
10
+ "directory": "pi-system-one"
11
+ },
12
+ "exports": {
13
+ ".": "./src/extension.ts"
14
+ },
8
15
  "scripts": {
9
16
  "test": "node --test --experimental-strip-types \"tests/*.test.ts\"",
10
17
  "typecheck": "tsc --noEmit"
11
18
  },
19
+ "keywords": [
20
+ "pi-package",
21
+ "pi-extension",
22
+ "model-router",
23
+ "model-routing",
24
+ "developer-tools",
25
+ "llm",
26
+ "ai",
27
+ "coding-agent"
28
+ ],
12
29
  "dependencies": {
13
- "system-one-core": "^0.1.1"
30
+ "system-one-core": "^0.2.0"
14
31
  },
15
32
  "peerDependencies": {
16
33
  "@earendil-works/pi-coding-agent": "*",
17
34
  "typebox": "*"
18
35
  },
19
36
  "pi": {
20
- "extensions": ["./src/extension.ts"]
37
+ "extensions": [
38
+ "./src/extension.ts"
39
+ ]
21
40
  }
22
41
  }
package/src/config.ts CHANGED
@@ -11,13 +11,31 @@ export interface SystemOneExtConfig {
11
11
  model?: string;
12
12
  timeoutMs: number;
13
13
  }
14
- export function loadSystemOneConfig(env: SystemOneEnv = process.env): SystemOneExtConfig {
14
+ export function loadSystemOneConfig(
15
+ env: SystemOneEnv = process.env,
16
+ ): SystemOneExtConfig {
15
17
  const baseUrl = env.SYSTEM_ONE_BASE_URL;
16
- if (!baseUrl) throw new Error("SYSTEM_ONE_BASE_URL is required (e.g. http://localhost:8008 or https://api.typesafe.ai)");
17
- const timeoutMs = env.SYSTEM_ONE_TIMEOUT_MS ? Number(env.SYSTEM_ONE_TIMEOUT_MS) : 10_000;
18
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new Error("SYSTEM_ONE_TIMEOUT_MS must be a positive number");
19
- return { baseUrl, apiKey: env.SYSTEM_ONE_API_KEY || undefined, model: env.SYSTEM_ONE_MODEL || undefined, timeoutMs };
18
+ if (!baseUrl)
19
+ throw new Error(
20
+ "SYSTEM_ONE_BASE_URL is required (e.g. http://localhost:8008 or https://api.typesafe.ai)",
21
+ );
22
+ const timeoutMs = env.SYSTEM_ONE_TIMEOUT_MS
23
+ ? Number(env.SYSTEM_ONE_TIMEOUT_MS)
24
+ : 10_000;
25
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
26
+ throw new Error("SYSTEM_ONE_TIMEOUT_MS must be a positive number");
27
+ return {
28
+ baseUrl,
29
+ apiKey: env.SYSTEM_ONE_API_KEY || undefined,
30
+ model: env.SYSTEM_ONE_MODEL || undefined,
31
+ timeoutMs,
32
+ };
20
33
  }
21
34
  export function describeConfig(cfg: SystemOneExtConfig): string {
22
- return [`Provider endpoint: ${cfg.baseUrl}`, `API key: ${cfg.apiKey ? "configured" : "absent"}`, `Model: ${cfg.model ?? "server default"}`, `Timeout: ${cfg.timeoutMs}ms`].join("\n");
35
+ return [
36
+ `Provider endpoint: ${cfg.baseUrl}`,
37
+ `API key: ${cfg.apiKey ? "configured" : "absent"}`,
38
+ `Model: ${cfg.model ?? "server default"}`,
39
+ `Timeout: ${cfg.timeoutMs}ms`,
40
+ ].join("\n");
23
41
  }
package/src/render.ts CHANGED
@@ -1,14 +1,27 @@
1
+ // biome-ignore-all lint/suspicious/noExplicitAny: intentional dynamic boundary over JSON protocol values
1
2
  // pi-system-one/src/render.ts
2
- export function renderSystemOneResult(response: { answers: Record<string, any> }): string {
3
+ export function renderSystemOneResult(response: {
4
+ answers: Record<string, any>;
5
+ }): string {
3
6
  const lines = ["System One result", ""];
4
7
  for (const [id, a] of Object.entries(response.answers)) {
5
8
  if (a?.type === "choice") {
6
- lines.push(`${id}:`, ` choice: ${a.choice}`, ` confidence: ${a.confidence}`, ` probabilities:`);
7
- for (const [k, v] of Object.entries(a.probabilities ?? {})) lines.push(` ${k}: ${v}`);
9
+ lines.push(
10
+ `${id}:`,
11
+ ` choice: ${a.choice}`,
12
+ ` confidence: ${a.confidence}`,
13
+ ` probabilities:`,
14
+ );
15
+ for (const [k, v] of Object.entries(a.probabilities ?? {}))
16
+ lines.push(` ${k}: ${v}`);
8
17
  } else if (a?.type === "noul") {
9
18
  lines.push(`${id}:`, ` noul: ${a.noul}`);
10
19
  } else {
11
- lines.push(`${id}:`, ` score: ${a.score}`, ` confidence: ${a.confidence}`);
20
+ lines.push(
21
+ `${id}:`,
22
+ ` score: ${a.score}`,
23
+ ` confidence: ${a.confidence}`,
24
+ );
12
25
  }
13
26
  lines.push("");
14
27
  }
package/src/tool.ts CHANGED
@@ -1,7 +1,9 @@
1
+ // biome-ignore-all lint/suspicious/noExplicitAny: intentional dynamic boundary over JSON protocol values
1
2
  // pi-system-one/src/tool.ts
2
- import { Type } from "typebox";
3
- import type { Static } from "typebox";
3
+
4
4
  import { SystemOne, type SystemOneProvider } from "system-one-core";
5
+ import type { Static } from "typebox";
6
+ import { Type } from "typebox";
5
7
  import { renderSystemOneResult } from "./render.ts";
6
8
 
7
9
  const ChoiceQ = Type.Object({
@@ -12,7 +14,9 @@ const ChoiceQ = Type.Object({
12
14
  const NoulQ = Type.Object({
13
15
  type: Type.Literal("noul"),
14
16
  instructions: Type.Any(),
15
- criteria: Type.Optional(Type.Record(Type.String(), Type.Union([Type.Any(), Type.Null()]))),
17
+ criteria: Type.Optional(
18
+ Type.Record(Type.String(), Type.Union([Type.Any(), Type.Null()])),
19
+ ),
16
20
  });
17
21
  const ScoreQ = Type.Object({
18
22
  type: Type.Literal("score"),
@@ -20,7 +24,11 @@ const ScoreQ = Type.Object({
20
24
  criteria: Type.Array(Type.Any()),
21
25
  });
22
26
  export const systemOneParams = Type.Object({
23
- state: Type.Union([Type.String(), Type.Record(Type.String(), Type.Any()), Type.Array(Type.Any())]),
27
+ state: Type.Union([
28
+ Type.String(),
29
+ Type.Record(Type.String(), Type.Any()),
30
+ Type.Array(Type.Any()),
31
+ ]),
24
32
  questions: Type.Record(Type.String(), Type.Union([ChoiceQ, NoulQ, ScoreQ])),
25
33
  model: Type.Optional(Type.String()),
26
34
  });
@@ -36,15 +44,30 @@ export function buildSystemOneTool(deps: { provider: SystemOneProvider }) {
36
44
  "Use this when the answer space is known and a fast probabilistic decision is preferable to generating free-form text. " +
37
45
  "Multiple independent choice, noul, and score questions should be batched into one call when they share the same state.",
38
46
  parameters: systemOneParams,
39
- async execute(_toolCallId: string, params: SystemOneParams, signal: AbortSignal | undefined) {
47
+ async execute(
48
+ _toolCallId: string,
49
+ params: SystemOneParams,
50
+ signal: AbortSignal | undefined,
51
+ ) {
40
52
  // Throw on failure per Pi contract — never encode errors in content.
41
53
  // Safe to propagate unwrapped: core error messages/codes never contain secrets.
42
54
  const response = await systemOne.evaluate(
43
- { state: params.state as any, questions: params.questions as any, ...(params.model ? { model: params.model } : {}) },
44
- { signal }
55
+ {
56
+ state: params.state as any,
57
+ questions: params.questions as any,
58
+ ...(params.model ? { model: params.model } : {}),
59
+ },
60
+ { signal },
45
61
  );
46
62
  const text = renderSystemOneResult(response as any);
47
- return { content: [{ type: "text" as const, text }], details: { answers: response.answers, model: response.model, usage: response.usage } };
63
+ return {
64
+ content: [{ type: "text" as const, text }],
65
+ details: {
66
+ answers: response.answers,
67
+ model: response.model,
68
+ usage: response.usage,
69
+ },
70
+ };
48
71
  },
49
72
  };
50
73
  }
@@ -1,16 +1,24 @@
1
1
  // pi-system-one/tests/config.test.ts
2
- import { describe, it } from "node:test";
2
+
3
3
  import assert from "node:assert/strict";
4
- import { loadSystemOneConfig, describeConfig } from "../src/config.ts";
4
+ import { describe, it } from "node:test";
5
+ import { describeConfig, loadSystemOneConfig } from "../src/config.ts";
5
6
 
6
7
  describe("config", () => {
7
8
  it("loads from env with localhost default", () => {
8
- const cfg = loadSystemOneConfig({ SYSTEM_ONE_BASE_URL: "http://localhost:8008" });
9
+ const cfg = loadSystemOneConfig({
10
+ SYSTEM_ONE_BASE_URL: "http://localhost:8008",
11
+ });
9
12
  assert.equal(cfg.baseUrl, "http://localhost:8008");
10
13
  assert.equal(cfg.timeoutMs, 10_000);
11
14
  });
12
15
  it("never exposes the key", () => {
13
- const shown = describeConfig(loadSystemOneConfig({ SYSTEM_ONE_BASE_URL: "https://api.typesafe.ai", SYSTEM_ONE_API_KEY: "sk-live-123" }));
16
+ const shown = describeConfig(
17
+ loadSystemOneConfig({
18
+ SYSTEM_ONE_BASE_URL: "https://api.typesafe.ai",
19
+ SYSTEM_ONE_API_KEY: "sk-live-123",
20
+ }),
21
+ );
14
22
  assert.doesNotMatch(shown, /sk-live-123/);
15
23
  assert.match(shown, /configured/);
16
24
  });
@@ -1,15 +1,27 @@
1
1
  // pi-system-one/tests/render.test.ts
2
- import { describe, it } from "node:test";
2
+
3
3
  import assert from "node:assert/strict";
4
+ import { describe, it } from "node:test";
4
5
  import { renderSystemOneResult } from "../src/render.ts";
5
6
 
6
7
  describe("render", () => {
7
8
  it("renders mixed answers human-readably with full distributions", () => {
8
9
  const text = renderSystemOneResult({
9
10
  answers: {
10
- task: { type: "choice", choice: "coding", probabilities: { coding: 0.91, research: 0.06, writing: 0.03 }, confidence: 0.91 },
11
+ task: {
12
+ type: "choice",
13
+ choice: "coding",
14
+ probabilities: { coding: 0.91, research: 0.06, writing: 0.03 },
15
+ confidence: 0.91,
16
+ },
11
17
  complex: { type: "noul", noul: 0.82 },
12
- difficulty: { type: "score", score: 1.7, probabilities: { "0": 0.1, "1": 0.2, "2": 0.7 }, legend: { "0": "easy", "1": "medium", "2": "hard" }, confidence: 0.84 },
18
+ difficulty: {
19
+ type: "score",
20
+ score: 1.7,
21
+ probabilities: { "0": 0.1, "1": 0.2, "2": 0.7 },
22
+ legend: { "0": "easy", "1": "medium", "2": "hard" },
23
+ confidence: 0.84,
24
+ },
13
25
  },
14
26
  } as any);
15
27
  assert.match(text, /task:[\s\S]*choice: coding/);
@@ -1,26 +1,61 @@
1
1
  // pi-system-one/tests/tool.test.ts
2
- import { describe, it } from "node:test";
2
+
3
3
  import assert from "node:assert/strict";
4
- import { buildSystemOneTool } from "../src/tool.ts";
4
+ import { describe, it } from "node:test";
5
5
  import { MockSystemOneProvider } from "system-one-core";
6
+ import { buildSystemOneTool } from "../src/tool.ts";
6
7
 
7
8
  describe("system_one tool", () => {
8
9
  it("delegates a mixed batch and preserves details", async () => {
9
10
  const tool: any = buildSystemOneTool({
10
- provider: new MockSystemOneProvider({ answers: { t: { type: "choice", choice: "a", probabilities: { a: 1 }, confidence: 1 } } }),
11
+ provider: new MockSystemOneProvider({
12
+ answers: {
13
+ t: {
14
+ type: "choice",
15
+ choice: "a",
16
+ probabilities: { a: 1 },
17
+ confidence: 1,
18
+ },
19
+ },
20
+ }),
11
21
  });
12
- const res = await tool.execute("id-1", { state: "hi", questions: { t: { type: "choice", instructions: "W?", criteria: { a: null } } } }, undefined, undefined, {});
22
+ const res = await tool.execute(
23
+ "id-1",
24
+ {
25
+ state: "hi",
26
+ questions: {
27
+ t: { type: "choice", instructions: "W?", criteria: { a: null } },
28
+ },
29
+ },
30
+ undefined,
31
+ undefined,
32
+ {},
33
+ );
13
34
  const text = JSON.stringify(res);
14
35
  assert.match(text, /"a"/);
15
36
  assert.equal(res.details.answers.t.choice, "a");
16
37
  });
17
38
  it("propagates provider errors by throwing (never error-results)", async () => {
18
39
  const tool: any = buildSystemOneTool({
19
- provider: { id: "boom", async evaluate() { throw new Error("down"); } },
40
+ provider: {
41
+ id: "boom",
42
+ async evaluate() {
43
+ throw new Error("down");
44
+ },
45
+ },
20
46
  });
21
47
  await assert.rejects(
22
- tool.execute("id-2", { state: "x", questions: { q: { type: "noul", instructions: "Is it?" } } }, undefined, undefined, {}),
23
- /down/
48
+ tool.execute(
49
+ "id-2",
50
+ {
51
+ state: "x",
52
+ questions: { q: { type: "noul", instructions: "Is it?" } },
53
+ },
54
+ undefined,
55
+ undefined,
56
+ {},
57
+ ),
58
+ /down/,
24
59
  );
25
60
  });
26
61
  it("mixed heterogeneous batch with model+signal+usage", async () => {
@@ -33,9 +68,20 @@ describe("system_one tool", () => {
33
68
  seenOptions = opts;
34
69
  return {
35
70
  answers: {
36
- c: { type: "choice", choice: "a", probabilities: { a: 0.9, b: 0.1 }, confidence: 0.9 },
71
+ c: {
72
+ type: "choice",
73
+ choice: "a",
74
+ probabilities: { a: 0.9, b: 0.1 },
75
+ confidence: 0.9,
76
+ },
37
77
  n: { type: "noul", noul: 0.8 },
38
- s: { type: "score", score: 1.5, probabilities: { "0": 0.5, "1": 0.5 }, legend: { "0": "easy", "1": "hard" }, confidence: 0.9 },
78
+ s: {
79
+ type: "score",
80
+ score: 1.5,
81
+ probabilities: { "0": 0.5, "1": 0.5 },
82
+ legend: { "0": "easy", "1": "hard" },
83
+ confidence: 0.9,
84
+ },
39
85
  },
40
86
  usage: { inputTokens: 5, outputTokens: 2 },
41
87
  metadata: { provider: "stub" },
@@ -44,15 +90,27 @@ describe("system_one tool", () => {
44
90
  };
45
91
  const tool: any = buildSystemOneTool({ provider: stub as any });
46
92
  const controller = new AbortController();
47
- const res = await tool.execute("id-3", {
48
- state: "hi",
49
- model: "jev-latest",
50
- questions: {
51
- c: { type: "choice", instructions: "W?", criteria: { a: null, b: null } },
52
- n: { type: "noul", instructions: "Is it?" },
53
- s: { type: "score", instructions: "How hard?", criteria: ["easy", "hard"] },
93
+ const res = await tool.execute(
94
+ "id-3",
95
+ {
96
+ state: "hi",
97
+ model: "jev-latest",
98
+ questions: {
99
+ c: {
100
+ type: "choice",
101
+ instructions: "W?",
102
+ criteria: { a: null, b: null },
103
+ },
104
+ n: { type: "noul", instructions: "Is it?" },
105
+ s: {
106
+ type: "score",
107
+ instructions: "How hard?",
108
+ criteria: ["easy", "hard"],
109
+ },
110
+ },
54
111
  },
55
- }, controller.signal);
112
+ controller.signal,
113
+ );
56
114
  assert.equal(res.details.answers.c.choice, "a");
57
115
  assert.equal(res.details.answers.n.noul, 0.8);
58
116
  assert.equal(res.details.answers.s.score, 1.5);