pi-orchestrator-config 4.8.2 → 4.8.4

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pidiff-ui",
3
3
  "private": true,
4
- "version": "4.8.2",
4
+ "version": "4.8.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -7,6 +7,12 @@
7
7
  */
8
8
 
9
9
  import { createLogger } from "./logger.js";
10
+ import {
11
+ isPricingKnown,
12
+ isPricingUnknown,
13
+ markPricingKnown,
14
+ markPricingUnknown,
15
+ } from "./pricing-provenance.js";
10
16
 
11
17
  import type {
12
18
  Api,
@@ -29,6 +35,9 @@ export const AMBIENT_AUTH_KEY = "ambient"; // pragma: allowlist secret
29
35
 
30
36
  export const DEFAULT_RUNTIME_BASE_URL = "https://localhost";
31
37
 
38
+ /** Pricing classification for models built here (never logs prices). */
39
+ const pricingLog = createLogger("runtime-model-pricing");
40
+
32
41
  export interface BuildRuntimeModelOptions {
33
42
  id: string;
34
43
  name: string;
@@ -45,7 +54,7 @@ export interface BuildRuntimeModelOptions {
45
54
 
46
55
  /** Build a full Model with api/provider/baseUrl required by createProvider. */
47
56
  export function buildRuntimeModel(opts: BuildRuntimeModelOptions): Model<Api> {
48
- return {
57
+ const model: Model<Api> = {
49
58
  id: opts.id,
50
59
  name: opts.name,
51
60
  api: opts.api,
@@ -58,6 +67,17 @@ export function buildRuntimeModel(opts: BuildRuntimeModelOptions): Model<Api> {
58
67
  contextWindow: opts.contextWindow ?? 200_000,
59
68
  maxTokens: opts.maxTokens ?? 32_768,
60
69
  };
70
+ // A supplied cost came from a source that published prices, so its zeros are
71
+ // authoritative (a free models.dev entry stays free). A default cost is Pi's
72
+ // numeric placeholder for "this source published no price", which is unknown.
73
+ // A marker already on the options (models.dev priced nothing) carries through.
74
+ const placeholder = !opts.cost;
75
+ if (placeholder || isPricingUnknown(opts)) {
76
+ pricingLog.debug("Runtime model has no published prices", { unknown: true });
77
+ return markPricingUnknown(model);
78
+ }
79
+ pricingLog.debug("Runtime model carries source-published prices", { unknown: false });
80
+ return isPricingKnown(opts) || opts.cost ? markPricingKnown(model) : model;
61
81
  }
62
82
 
63
83
  export interface AmbientLoginAuthOptions {
@@ -13,6 +13,7 @@ import { homedir } from "node:os";
13
13
  import { dirname, join } from "node:path";
14
14
  import { createLogger } from "./logger.js";
15
15
  import type { BuildRuntimeModelOptions } from "./create-runtime-provider.js";
16
+ import { classifyCostRecord, markPricingKnown, markPricingUnknown, priceOrFallback, safeModelRef } from "./pricing-provenance.js";
16
17
 
17
18
  const log = createLogger("models-dev");
18
19
 
@@ -404,12 +405,24 @@ export function fillRuntimeModelFromCatalog(
404
405
  }
405
406
 
406
407
  if (next.cost == null && entry?.cost) {
408
+ // Normalize with the same validator the provenance decision uses, so an invalid
409
+ // component can never pass through as a price and a zero-filled placeholder is
410
+ // never mistaken for a published zero.
407
411
  next.cost = {
408
- input: entry.cost.input ?? 0,
409
- output: entry.cost.output ?? 0,
410
- cacheRead: entry.cost.cache_read ?? 0,
411
- cacheWrite: entry.cost.cache_write ?? 0,
412
+ input: priceOrFallback(entry.cost.input, 0),
413
+ output: priceOrFallback(entry.cost.output, 0),
414
+ cacheRead: priceOrFallback(entry.cost.cache_read, 0),
415
+ cacheWrite: priceOrFallback(entry.cost.cache_write, 0),
412
416
  };
417
+ // models.dev is a real price source, so its prices are authoritative when every
418
+ // supplied component is valid — zeros included, which makes a genuinely free
419
+ // CLI/ACPX model free. A record mixing a valid price with an invalid one is
420
+ // malformed, so the model stays unknown-priced rather than inheriting a zero.
421
+ if (classifyCostRecord(entry.cost, ["input", "output", "cache_read", "cache_write"], { source: "models.dev", agent, model: safeModelRef(discoveredId) }) === "known") {
422
+ markPricingKnown(next);
423
+ } else {
424
+ markPricingUnknown(next);
425
+ }
413
426
  }
414
427
 
415
428
  if (next.input == null) {
@@ -5,6 +5,14 @@ import { homedir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import type { Api, Model, ProviderHeaders } from "@earendil-works/pi-ai";
7
7
  import { createLogger } from "./logger.js";
8
+ import {
9
+ classifyCostRecord,
10
+ isPricingUnknown,
11
+ markPricingKnown,
12
+ markPricingUnknown,
13
+ priceOrFallback,
14
+ safeModelRef,
15
+ } from "./pricing-provenance.js";
8
16
 
9
17
  const log = createLogger("openai-compatible-discovery");
10
18
 
@@ -48,10 +56,6 @@ const PI_STATIC_MODEL_DEFAULTS = {
48
56
  maxTokens: 16_384,
49
57
  } as const;
50
58
 
51
- function finiteNumber(value: unknown, fallback: number): number {
52
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
53
- }
54
-
55
59
  function positiveFiniteNumber(value: unknown, fallback: number): number {
56
60
  return typeof value === "number" && Number.isFinite(value) && value > 0
57
61
  ? value
@@ -69,16 +73,24 @@ function materializeInput(value: unknown): ("text" | "image")[] {
69
73
  return ["text", "image"];
70
74
  }
71
75
 
72
- function materializeCost(value: unknown): Model<Api>["cost"] {
76
+ function materializeCost(value: unknown, context?: Record<string, unknown>): Model<Api>["cost"] {
73
77
  const source = value && typeof value === "object" && !Array.isArray(value)
74
78
  ? value as Record<string, unknown>
75
79
  : {};
76
- return {
77
- input: finiteNumber(source.input, PI_STATIC_MODEL_DEFAULTS.cost.input),
78
- output: finiteNumber(source.output, PI_STATIC_MODEL_DEFAULTS.cost.output),
79
- cacheRead: finiteNumber(source.cacheRead, PI_STATIC_MODEL_DEFAULTS.cost.cacheRead),
80
- cacheWrite: finiteNumber(source.cacheWrite, PI_STATIC_MODEL_DEFAULTS.cost.cacheWrite),
80
+ // Invalid components (non-finite, negative) become the placeholder zero, which
81
+ // keeps the materialized cost consistent with classifyCostRecord()'s decision:
82
+ // a record priced only with invalid values contributes no valid price and is
83
+ // therefore marked unknown-priced. A record with at least one valid component
84
+ // published its prices, so its zeros are authoritative even though the model
85
+ // is absent from pi-ai's generated catalog.
86
+ const cost = {
87
+ input: priceOrFallback(source.input, PI_STATIC_MODEL_DEFAULTS.cost.input),
88
+ output: priceOrFallback(source.output, PI_STATIC_MODEL_DEFAULTS.cost.output),
89
+ cacheRead: priceOrFallback(source.cacheRead, PI_STATIC_MODEL_DEFAULTS.cost.cacheRead),
90
+ cacheWrite: priceOrFallback(source.cacheWrite, PI_STATIC_MODEL_DEFAULTS.cost.cacheWrite),
81
91
  };
92
+ log.debug("Materialized discovery cost", { components: Object.keys(cost).length, priced: classifyCostRecord(source, ["input", "output", "cacheRead", "cacheWrite"], context) === "known", ...context });
93
+ return cost;
82
94
  }
83
95
  export interface CachedOpenAiCompatibleDiscovery<T> {
84
96
  models: readonly T[];
@@ -509,7 +521,7 @@ export function materializeOpenAiCompatibleModels(
509
521
  for (const record of records) {
510
522
  if (typeof record.id !== "string" || seenIds.has(record.id)) continue;
511
523
  seenIds.add(record.id);
512
- models.push({
524
+ const model: Model<Api> = {
513
525
  id: record.id,
514
526
  name: typeof record.name === "string" ? record.name : record.id,
515
527
  api: "openai-completions",
@@ -519,7 +531,7 @@ export function materializeOpenAiCompatibleModels(
519
531
  ? record.reasoning
520
532
  : PI_STATIC_MODEL_DEFAULTS.reasoning,
521
533
  input: materializeInput(record.input),
522
- cost: materializeCost(record.cost),
534
+ cost: materializeCost(record.cost, { provider: providerId, model: safeModelRef(record.id) }),
523
535
  // Native Pi metadata wins. OpenAI-compatible capability APIs may expose
524
536
  // generic input/output capacities, whose sum is Pi's context window.
525
537
  contextWindow: positiveFiniteNumber(
@@ -535,7 +547,18 @@ export function materializeOpenAiCompatibleModels(
535
547
  record.maxTokens,
536
548
  positiveFiniteNumber(record.max_output_tokens, PI_STATIC_MODEL_DEFAULTS.maxTokens),
537
549
  ),
538
- });
550
+ };
551
+ // A record whose every supplied component is valid published real prices —
552
+ // zeros included — which pi-ai's catalog cannot vouch for, since these models
553
+ // are absent from it, so that provenance is marked explicitly. A record with no
554
+ // valid component, or one mixing a valid price with an invalid one, is a
555
+ // placeholder or malformed and stays unknown-priced.
556
+ if (classifyCostRecord(record.cost, ["input", "output", "cacheRead", "cacheWrite"], { source: "openai-compatible", provider: providerId, model: safeModelRef(record.id) }) === "known") {
557
+ markPricingKnown(model);
558
+ } else {
559
+ markPricingUnknown(model);
560
+ }
561
+ models.push(model);
539
562
  }
540
563
  return models;
541
564
  }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Pricing provenance marker.
3
+ *
4
+ * Pi requires numeric prices, so a model whose source supplied none is registered
5
+ * with zeros — and those zeros mean *unknown*, not *free*. A catalog all-zero cost
6
+ * means the opposite: the price is known to be zero (an OpenRouter `:free`
7
+ * variant, for example). The number alone cannot tell the two apart, so every
8
+ * site that fabricates a placeholder cost marks the model and consumers read the
9
+ * marker instead of guessing from the value.
10
+ *
11
+ * Shared with packages/pi-sidecar through the global symbol registry: the sidecar
12
+ * cannot import extensions/shared (separate tsconfig rootDir). Same pattern as
13
+ * Symbol.for("pi-config.ambientLoginAuth") and the session-cwd ALS.
14
+ */
15
+ import { createHash } from "node:crypto";
16
+ import { createLogger } from "./logger.js";
17
+
18
+ const log = createLogger("pricing-provenance");
19
+
20
+ export const PRICING_UNKNOWN = Symbol.for("pi-config.pricingUnknown");
21
+
22
+ /**
23
+ * Counterpart marker for sources that *did* publish prices, zeros included.
24
+ *
25
+ * A models.dev entry or an OpenAI-compatible `/v1/models` record that states a
26
+ * zero price is authoritative even though the model is absent from pi-ai's
27
+ * generated catalog. Without this marker the sidecar cannot tell that explicit
28
+ * zero from Pi's own zero-filled placeholder, and would report a genuinely free
29
+ * call as unknown spend.
30
+ */
31
+ export const PRICING_KNOWN = Symbol.for("pi-config.pricingKnown");
32
+
33
+ /**
34
+ * Mark a model whose prices were defaulted rather than supplied by its source.
35
+ *
36
+ * Enumerable on purpose: symbol keys stay out of Object.keys and JSON, but they
37
+ * do survive object spread, so a model copied downstream keeps its provenance.
38
+ */
39
+ export function markPricingUnknown<T extends object>(model: T): T {
40
+ (model as Record<symbol, unknown>)[PRICING_UNKNOWN] = true;
41
+ // Never log the model or its prices — only the decision.
42
+ log.debug("Marked model pricing as unknown", { marked: true });
43
+ return model;
44
+ }
45
+
46
+ /** Whether a model was marked as having unknown (not zero) prices. */
47
+ export function isPricingUnknown(model: unknown): boolean {
48
+ const unknown = !!model && typeof model === "object" && Reflect.get(model, PRICING_UNKNOWN) === true;
49
+ log.debug("Read model pricing provenance", { unknown });
50
+ return unknown;
51
+ }
52
+
53
+ /**
54
+ * Mark a model whose prices came from a source that published them — including
55
+ * authoritative zeros. Unknown wins if both markers are somehow present.
56
+ */
57
+ export function markPricingKnown<T extends object>(model: T): T {
58
+ (model as Record<symbol, unknown>)[PRICING_KNOWN] = true;
59
+ log.debug("Marked model pricing as source-published", { marked: true });
60
+ return model;
61
+ }
62
+
63
+ /** Whether a source published this model's prices (zeros included). */
64
+ export function isPricingKnown(model: unknown): boolean {
65
+ const known = !!model && typeof model === "object" && Reflect.get(model, PRICING_KNOWN) === true;
66
+ log.debug("Read source-published pricing provenance", { known });
67
+ return known;
68
+ }
69
+
70
+ /**
71
+ * A price is authoritative only when it is a finite, non-negative number. NaN,
72
+ * Infinity, and negatives describe a broken source, not a price — treating them
73
+ * as prices would classify an invalid quote as a known (possibly free) one.
74
+ */
75
+ export function isValidPrice(value: unknown): value is number {
76
+ const valid = typeof value === "number" && Number.isFinite(value) && value >= 0;
77
+ if (!valid) log.debug("Rejected price component as invalid", { type: typeof value });
78
+ return valid;
79
+ }
80
+
81
+ /** Normalize one price component, falling back when the source value is invalid. */
82
+ export function priceOrFallback(value: unknown, fallback: number): number {
83
+ const price = isValidPrice(value) ? value : fallback;
84
+ log.debug("Normalized price component", { valid: price === value });
85
+ return price;
86
+ }
87
+
88
+ /**
89
+ * Non-reversible reference for an externally supplied model id.
90
+ *
91
+ * A `/v1/models` response can echo a configured credential inside an id (the
92
+ * sidecar's own listing path filters exactly that case), so ids arriving from an
93
+ * external source are never logged verbatim. The digest still identifies which
94
+ * record a pricing decision came from, without exposing the value.
95
+ */
96
+ export function safeModelRef(id: unknown): string {
97
+ const text = typeof id === "string" ? id : String(id ?? "");
98
+ const ref = `model#${createHash("sha256").update(text).digest("hex").slice(0, 12)}`;
99
+ log.debug("Derived model reference for logging", { ref });
100
+ return ref;
101
+ }
102
+
103
+ /**
104
+ * Whether a value is a usable cost record.
105
+ *
106
+ * `operation` and `context` exist so a malformed or absent price can be traced to
107
+ * the classification that rejected it: which pricing source, which model, and
108
+ * which field set. Only non-sensitive identifiers belong in `context` — never
109
+ * price values or credentials, and pass externally supplied ids through
110
+ * `safeModelRef()` rather than verbatim.
111
+ */
112
+ function asRecord(value: unknown, operation: string, context?: Record<string, unknown>): Record<string, unknown> | undefined {
113
+ const record = value && typeof value === "object" && !Array.isArray(value)
114
+ ? value as Record<string, unknown>
115
+ : undefined;
116
+ log.debug("Checked cost record shape", { operation, usable: !!record, ...context });
117
+ return record;
118
+ }
119
+
120
+ /**
121
+ * Classify a raw cost record's provenance: did this source publish prices we can
122
+ * trust, zeros included?
123
+ *
124
+ * "known" requires *every supplied* component to be a valid price and at least one
125
+ * of them to be present. An absent or undefined key is ignored — the source never
126
+ * claimed it — but an explicit `null` counts as supplied-and-invalid, because
127
+ * `priceOrFallback()` normalizes it to a placeholder zero just like a negative or
128
+ * non-numeric value. A record mixing a valid zero with an invalid component is
129
+ * therefore malformed, and the model must stay unknown-priced rather than
130
+ * reporting a driver's placeholder zero as a complete $0.
131
+ *
132
+ * `context` carries non-sensitive identifiers (pricing source, provider, model)
133
+ * so the decision can be traced from the log.
134
+ */
135
+ export function classifyCostRecord(cost: unknown, keys: readonly string[], context?: Record<string, unknown>): "known" | "unknown" {
136
+ const entry = asRecord(cost, "classify-cost-record", context);
137
+ // Only absent (undefined) keys are skipped; an explicit null is a supplied
138
+ // component that failed validation.
139
+ const supplied = entry ? keys.filter((key) => entry[key] !== undefined) : [];
140
+ const priced = supplied.some((key) => isValidPrice(entry![key]));
141
+ const allValid = supplied.length > 0 && supplied.every((key) => isValidPrice(entry![key]));
142
+ const verdict = priced && allValid ? "known" : "unknown";
143
+ log.debug("Classified cost record provenance", { verdict, supplied: supplied.length, priced, allValid, fields: keys.length, ...context });
144
+ return verdict;
145
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-orchestrator-config",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Orchestrator pattern for pi: specialist subagents, enforcement, code review loop, workflow commands",
5
5
  "keywords": [
6
6
  "pi-package"