@pwguler/pi-pengepul-provider 0.2.0 → 0.2.1

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.
@@ -9,8 +9,9 @@
9
9
  *
10
10
  * The relay advertises id/owned_by and, since pengepul 0.6.0, optional
11
11
  * per-model metadata: `context_window`, `max_output_tokens`,
12
- * `input_modalities`, and `pricing`. That is the first-party truth for what
13
- * this relay actually serves, so it wins. The rollout is partial (some ids
12
+ * `input_modalities`, `pricing`, and `reasoning`. That is the first-party
13
+ * truth for what this relay actually serves, so it wins. The rollout is
14
+ * partial (some ids
14
15
  * still come back with ids only), so two fallbacks remain: pi's builtin
15
16
  * catalog - pengepul forwards the same ids upstream, so pi's numbers are the
16
17
  * next best source - and then family heuristics. The catalog lookup is
@@ -32,8 +33,8 @@ export const DEFAULT_MODELS_TIMEOUT_MS = 10_000
32
33
  const DEFAULT_CONTEXT_WINDOW = 200_000
33
34
  const DEFAULT_MAX_TOKENS = 64_000
34
35
  const ZERO_COST: ModelCostRates = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
35
- /** v2 cached pre-multi-catalog lookups (commandcode ids missed reasoning); reject it. */
36
- const MODEL_CACHE_VERSION = 4
36
+ /** v5 cached entries predate the relay-uniform off/minimal overlay (foreign catalog maps offered levels the relay 400s); reject it. */
37
+ const MODEL_CACHE_VERSION = 6
37
38
 
38
39
  export type ModelInput = ("text" | "image")[]
39
40
 
@@ -74,7 +75,7 @@ export interface PengepulModel {
74
75
  cost: ModelCostRates
75
76
  contextWindow: number
76
77
  maxTokens: number
77
- /** Level-to-wire mapping inherited from the catalog; undefined = pi's default. */
78
+ /** Level-to-wire mapping (inherited, relay-unsafe levels nulled); undefined = pi's default. */
78
79
  thinkingLevelMap?: Record<string, string | null>
79
80
  }
80
81
 
@@ -117,6 +118,8 @@ function heuristicMeta(id: string): BuiltinModelMeta {
117
118
  /**
118
119
  * Metadata pengepul itself advertises for a model (pengepul >= 0.6.0).
119
120
  * Every field is optional: the rollout is partial and older relays send none.
121
+ * `reasoning` is the relay's first-party say on whether the upstream accepts
122
+ * reasoning params; absent or non-boolean falls back to the catalog.
120
123
  * Returns undefined when the entry carries no usable metadata at all.
121
124
  */
122
125
  export function metaFromRelayEntry(
@@ -126,16 +129,19 @@ export function metaFromRelayEntry(
126
129
  const maxTokens = optionalPositiveNumber(entry["max_output_tokens"])
127
130
  const input = optionalInputModalities(entry["input_modalities"])
128
131
  const cost = optionalPricing(entry["pricing"])
132
+ const reasoning = optionalBoolean(entry["reasoning"])
129
133
 
130
134
  if (
131
135
  contextWindow === undefined &&
132
136
  maxTokens === undefined &&
133
137
  input === undefined &&
134
- cost === undefined
138
+ cost === undefined &&
139
+ reasoning === undefined
135
140
  ) {
136
141
  return undefined
137
142
  }
138
143
  return {
144
+ ...(reasoning !== undefined ? { reasoning } : {}),
139
145
  ...(contextWindow !== undefined ? { contextWindow } : {}),
140
146
  ...(maxTokens !== undefined ? { maxTokens } : {}),
141
147
  ...(input !== undefined ? { input } : {}),
@@ -147,6 +153,10 @@ function optionalPositiveNumber(value: unknown): number | undefined {
147
153
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined
148
154
  }
149
155
 
156
+ function optionalBoolean(value: unknown): boolean | undefined {
157
+ return typeof value === "boolean" ? value : undefined
158
+ }
159
+
150
160
  function optionalInputModalities(value: unknown): ModelInput | undefined {
151
161
  if (!Array.isArray(value)) return undefined
152
162
  const input = value.filter(
@@ -206,6 +216,22 @@ function toPengepulModel(
206
216
  // capable, even when pi's catalog does not know its exact id yet.
207
217
  const reasoning = meta.reasoning || ownedBy === "anthropic"
208
218
 
219
+ // The relay enforces reasoning_effort low|medium|high|xhigh|max at its
220
+ // request layer, uniformly across families: `minimal` 400s and its
221
+ // thinking toggle never actually disables thinking. That holds regardless
222
+ // of where the reasoning knowledge came from, so every openai-completions
223
+ // reasoning model gets off/minimal nulled — inherited catalogs were
224
+ // written for other upstreams and do offer the unsafe levels (observed:
225
+ // openrouter muse-spark `minimal: "minimal"`, opencode-go hy4-preview
226
+ // `off: "none"`). Overlay, never replace: inherited strings stay valid on
227
+ // the wire and inherited nulls keep their levels hidden. The Messages
228
+ // dialect needs no overlay: it folds `minimal` into `low` and already
229
+ // nulls `off` via forceAdaptiveThinking.
230
+ let thinkingLevelMap = meta.thinkingLevelMap ? { ...meta.thinkingLevelMap } : undefined
231
+ if (reasoning && dialect === "openai-completions") {
232
+ thinkingLevelMap = { ...(thinkingLevelMap ?? {}), off: null, minimal: null }
233
+ }
234
+
209
235
  return {
210
236
  id,
211
237
  name: displayName(id),
@@ -215,7 +241,7 @@ function toPengepulModel(
215
241
  cost: { ...meta.cost },
216
242
  contextWindow: meta.contextWindow,
217
243
  maxTokens: meta.maxTokens,
218
- ...(meta.thinkingLevelMap ? { thinkingLevelMap: { ...meta.thinkingLevelMap } } : {}),
244
+ ...(thinkingLevelMap ? { thinkingLevelMap } : {}),
219
245
  }
220
246
  }
221
247
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pwguler/pi-pengepul-provider",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "pi custom provider for pengepul, a local relay that pools your Claude/Codex subscriptions. Connects pi to http://127.0.0.1:8317 over the native Anthropic Messages and OpenAI Chat Completions wires.",
6
6
  "license": "MIT",