@pentect/pi 0.0.29 → 0.0.31

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/README.md CHANGED
@@ -19,5 +19,10 @@ Set `PENTECT_PI_MODEL` before Pi starts to expose a different model. Set
19
19
  `PENTECT_PI_API=responses` for the OpenAI Responses API. `OPENAI_BASE_URL` and
20
20
  `OPENAI_API_KEY` configure the upstream.
21
21
 
22
+ For a custom upstream whose model limits differ from the defaults, set
23
+ `PENTECT_PI_CONTEXT_WINDOW`, `PENTECT_PI_MAX_TOKENS`,
24
+ `PENTECT_PI_INPUTS=text`, or `PENTECT_PI_REASONING=true|false`. Invalid values
25
+ stop startup instead of advertising incorrect capabilities to Pi.
26
+
22
27
  The JavaScript extension only manages Pi's provider lifecycle. Detection,
23
28
  handles, plugins, and network forwarding remain inside the Pentect binary.
@@ -6,6 +6,8 @@ const require = createRequire(import.meta.url);
6
6
  const STATE = Symbol.for("@pentect/pi/provider-state");
7
7
  const MAX_READY_BYTES = 16 * 1024;
8
8
  const START_TIMEOUT_MS = 10_000;
9
+ const DEFAULT_CONTEXT_WINDOW = 128_000;
10
+ const DEFAULT_MAX_TOKENS = 32_768;
9
11
 
10
12
  function sharedState() {
11
13
  if (!globalThis[STATE]) {
@@ -43,7 +45,34 @@ export function parseReady(line) {
43
45
  return ready;
44
46
  }
45
47
 
46
- export function providerDefinition(ready) {
48
+ function positiveInteger(env, name, fallback) {
49
+ const raw = env[name]?.trim();
50
+ if (!raw) return fallback;
51
+ const value = Number(raw);
52
+ if (!Number.isSafeInteger(value) || value <= 0) {
53
+ throw new Error(`${name} must be a positive integer`);
54
+ }
55
+ return value;
56
+ }
57
+
58
+ function reasoningSupport(env, api) {
59
+ const value = env.PENTECT_PI_REASONING?.trim().toLowerCase();
60
+ if (!value || value === "auto") return api === "openai-responses";
61
+ if (value === "true") return true;
62
+ if (value === "false") return false;
63
+ throw new Error("PENTECT_PI_REASONING must be auto, true, or false");
64
+ }
65
+
66
+ function inputSupport(env) {
67
+ const value = env.PENTECT_PI_INPUTS?.trim().toLowerCase();
68
+ if (!value || value === "text,image" || value === "image,text") {
69
+ return ["text", "image"];
70
+ }
71
+ if (value === "text") return ["text"];
72
+ throw new Error("PENTECT_PI_INPUTS must be text or text,image");
73
+ }
74
+
75
+ export function providerDefinition(ready, env = process.env) {
47
76
  return {
48
77
  name: "Pentect",
49
78
  baseUrl: ready.baseUrl,
@@ -54,11 +83,19 @@ export function providerDefinition(ready) {
54
83
  {
55
84
  id: ready.model,
56
85
  name: ready.model,
57
- reasoning: ready.api === "openai-responses",
58
- input: ["text", "image"],
86
+ reasoning: reasoningSupport(env, ready.api),
87
+ input: inputSupport(env),
59
88
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
60
- contextWindow: 128000,
61
- maxTokens: 32768,
89
+ contextWindow: positiveInteger(
90
+ env,
91
+ "PENTECT_PI_CONTEXT_WINDOW",
92
+ DEFAULT_CONTEXT_WINDOW,
93
+ ),
94
+ maxTokens: positiveInteger(
95
+ env,
96
+ "PENTECT_PI_MAX_TOKENS",
97
+ DEFAULT_MAX_TOKENS,
98
+ ),
62
99
  },
63
100
  ],
64
101
  };
@@ -101,11 +138,22 @@ async function startProvider() {
101
138
  return { child, ready: parseReady(line) };
102
139
  } catch (error) {
103
140
  child.kill();
141
+ restoreProviderCredentials(state, process.env);
104
142
  const detail = errors.trim();
105
143
  throw new Error(detail || error.message, { cause: error });
106
144
  }
107
145
  }
108
146
 
147
+ export function restoreProviderCredentials(state, env) {
148
+ if (state.openaiApiKey === undefined) delete env.OPENAI_API_KEY;
149
+ else env.OPENAI_API_KEY = state.openaiApiKey;
150
+ if (state.upstreamAuthorization === undefined) {
151
+ delete env.PENTECT_UPSTREAM_AUTHORIZATION;
152
+ } else {
153
+ env.PENTECT_UPSTREAM_AUTHORIZATION = state.upstreamAuthorization;
154
+ }
155
+ }
156
+
109
157
  function firstLine(child) {
110
158
  return new Promise((resolveLine, reject) => {
111
159
  let output = "";
@@ -149,7 +197,14 @@ function stopProvider(child) {
149
197
  }
150
198
 
151
199
  export default async function pentect(pi) {
200
+ const state = sharedState();
152
201
  const { child, ready } = await startProvider();
153
- pi.registerProvider("pentect", providerDefinition(ready));
202
+ try {
203
+ pi.registerProvider("pentect", providerDefinition(ready));
204
+ } catch (error) {
205
+ stopProvider(child);
206
+ restoreProviderCredentials(state, process.env);
207
+ throw error;
208
+ }
154
209
  pi.on("session_shutdown", () => stopProvider(child));
155
210
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pentect/pi",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Pentect provider extension for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "test": "node --test"
35
35
  },
36
36
  "dependencies": {
37
- "pentect": "0.0.29"
37
+ "pentect": "0.0.31"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=20.6.0"