modelrig 0.9.0 → 0.11.0

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 (231) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/README.md +51 -27
  3. package/data/PROVENANCE.md +14 -0
  4. package/data/pricing-map.json +26 -0
  5. package/dist/adapters/attempt-meta-map.d.ts +8 -1
  6. package/dist/adapters/attempt-meta-map.d.ts.map +1 -1
  7. package/dist/adapters/attempt-meta-map.js +4 -0
  8. package/dist/adapters/build-adapters.js +2 -2
  9. package/dist/adapters/gemini.d.ts +28 -1
  10. package/dist/adapters/gemini.d.ts.map +1 -1
  11. package/dist/adapters/gemini.js +38 -9
  12. package/dist/adapters/grok.d.ts.map +1 -1
  13. package/dist/adapters/grok.js +53 -4
  14. package/dist/adapters/openai-compat.d.ts +8 -2
  15. package/dist/adapters/openai-compat.d.ts.map +1 -1
  16. package/dist/adapters/openai-compat.js +8 -1
  17. package/dist/adapters/openai-stream.d.ts +10 -2
  18. package/dist/adapters/openai-stream.d.ts.map +1 -1
  19. package/dist/adapters/openai-stream.js +5 -1
  20. package/dist/adapters/openai.d.ts +10 -1
  21. package/dist/adapters/openai.d.ts.map +1 -1
  22. package/dist/adapters/openai.js +57 -7
  23. package/dist/adapters/responses-api.d.ts +61 -0
  24. package/dist/adapters/responses-api.d.ts.map +1 -0
  25. package/dist/adapters/responses-api.js +124 -0
  26. package/dist/artifacts/artifact.d.ts +20 -0
  27. package/dist/artifacts/artifact.d.ts.map +1 -1
  28. package/dist/artifacts/artifact.js +207 -171
  29. package/dist/artifacts/index.d.ts +3 -1
  30. package/dist/artifacts/index.d.ts.map +1 -1
  31. package/dist/artifacts/index.js +8 -1
  32. package/dist/bakeoff/groundedness.d.ts +58 -0
  33. package/dist/bakeoff/groundedness.d.ts.map +1 -0
  34. package/dist/bakeoff/groundedness.js +165 -0
  35. package/dist/bakeoff/quality.d.ts +14 -1
  36. package/dist/bakeoff/quality.d.ts.map +1 -1
  37. package/dist/bakeoff/quality.js +10 -0
  38. package/dist/bakeoff/replay.d.ts.map +1 -1
  39. package/dist/bakeoff/replay.js +4 -0
  40. package/dist/cli.d.ts.map +1 -1
  41. package/dist/cli.js +8 -0
  42. package/dist/config.d.ts +31 -0
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +23 -0
  45. package/dist/doctor-cli.d.ts +7 -0
  46. package/dist/doctor-cli.d.ts.map +1 -0
  47. package/dist/doctor-cli.js +68 -0
  48. package/dist/doctor-preflight.d.ts +64 -0
  49. package/dist/doctor-preflight.d.ts.map +1 -0
  50. package/dist/doctor-preflight.js +167 -0
  51. package/dist/feedback/coverage.d.ts.map +1 -1
  52. package/dist/feedback/coverage.js +9 -2
  53. package/dist/feedback/grade.d.ts +8 -1
  54. package/dist/feedback/grade.d.ts.map +1 -1
  55. package/dist/feedback/grade.js +4 -0
  56. package/dist/gateway/http-protocol.d.ts +19 -0
  57. package/dist/gateway/http-protocol.d.ts.map +1 -1
  58. package/dist/gateway/serve-client.d.ts +99 -0
  59. package/dist/gateway/serve-client.d.ts.map +1 -0
  60. package/dist/gateway/serve-client.js +360 -0
  61. package/dist/index.d.ts +31 -3
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +71 -8
  64. package/dist/prompts/manifest.d.ts +128 -0
  65. package/dist/prompts/manifest.d.ts.map +1 -0
  66. package/dist/prompts/manifest.js +214 -0
  67. package/dist/prompts/prompt-fetch.d.ts +84 -0
  68. package/dist/prompts/prompt-fetch.d.ts.map +1 -0
  69. package/dist/prompts/prompt-fetch.js +120 -0
  70. package/dist/prompts/prompt-resolve.d.ts +76 -0
  71. package/dist/prompts/prompt-resolve.d.ts.map +1 -0
  72. package/dist/prompts/prompt-resolve.js +93 -0
  73. package/dist/prompts/slot-resolver.d.ts +91 -0
  74. package/dist/prompts/slot-resolver.d.ts.map +1 -0
  75. package/dist/prompts/slot-resolver.js +135 -0
  76. package/dist/prompts/tools-register.d.ts +60 -0
  77. package/dist/prompts/tools-register.d.ts.map +1 -0
  78. package/dist/prompts/tools-register.js +48 -0
  79. package/dist/prompts/version-hash.d.ts +29 -0
  80. package/dist/prompts/version-hash.d.ts.map +1 -0
  81. package/dist/prompts/version-hash.js +54 -0
  82. package/dist/prompts-export-cli.d.ts +21 -0
  83. package/dist/prompts-export-cli.d.ts.map +1 -0
  84. package/dist/prompts-export-cli.js +130 -0
  85. package/dist/registry/cache-policy.d.ts.map +1 -1
  86. package/dist/registry/cache-policy.js +22 -0
  87. package/dist/registry/pricing-shape.d.ts +38 -8
  88. package/dist/registry/pricing-shape.d.ts.map +1 -1
  89. package/dist/registry/pricing-shape.js +40 -12
  90. package/dist/registry/pricing.d.ts +6 -5
  91. package/dist/registry/pricing.d.ts.map +1 -1
  92. package/dist/registry/pricing.js +9 -5
  93. package/dist/routes/destination-loader.d.ts +16 -0
  94. package/dist/routes/destination-loader.d.ts.map +1 -0
  95. package/dist/routes/destination-loader.js +29 -0
  96. package/dist/routes/draft.d.ts +7 -0
  97. package/dist/routes/draft.d.ts.map +1 -1
  98. package/dist/routes/draft.js +12 -0
  99. package/dist/routes/loader.d.ts.map +1 -1
  100. package/dist/routes/loader.js +27 -54
  101. package/dist/routes/output-loader.d.ts +42 -0
  102. package/dist/routes/output-loader.d.ts.map +1 -0
  103. package/dist/routes/output-loader.js +112 -0
  104. package/dist/routes/prompt-loader.d.ts +49 -0
  105. package/dist/routes/prompt-loader.d.ts.map +1 -0
  106. package/dist/routes/prompt-loader.js +113 -0
  107. package/dist/routes/resolved-bundle.d.ts +19 -0
  108. package/dist/routes/resolved-bundle.d.ts.map +1 -1
  109. package/dist/routes/resolved-bundle.js +24 -0
  110. package/dist/routes/screen-loader.d.ts +1 -1
  111. package/dist/routes/screen-loader.d.ts.map +1 -1
  112. package/dist/routes/screen-loader.js +8 -1
  113. package/dist/run/accept.d.ts +3 -0
  114. package/dist/run/accept.d.ts.map +1 -1
  115. package/dist/run/accept.js +1 -0
  116. package/dist/run/cost.d.ts +31 -2
  117. package/dist/run/cost.d.ts.map +1 -1
  118. package/dist/run/cost.js +57 -2
  119. package/dist/run/destination.d.ts +39 -0
  120. package/dist/run/destination.d.ts.map +1 -0
  121. package/dist/run/destination.js +45 -0
  122. package/dist/run/dispatch.d.ts +25 -3
  123. package/dist/run/dispatch.d.ts.map +1 -1
  124. package/dist/run/dispatch.js +26 -4
  125. package/dist/run/raw-defaults.d.ts.map +1 -1
  126. package/dist/run/raw-defaults.js +6 -1
  127. package/dist/run/raw-finalize.d.ts +96 -0
  128. package/dist/run/raw-finalize.d.ts.map +1 -0
  129. package/dist/run/raw-finalize.js +283 -0
  130. package/dist/run/raw-knobs.d.ts +36 -0
  131. package/dist/run/raw-knobs.d.ts.map +1 -0
  132. package/dist/run/raw-knobs.js +99 -0
  133. package/dist/run/raw-request.d.ts +24 -0
  134. package/dist/run/raw-request.d.ts.map +1 -0
  135. package/dist/run/raw-request.js +69 -0
  136. package/dist/run/raw-schema.d.ts +76 -0
  137. package/dist/run/raw-schema.d.ts.map +1 -0
  138. package/dist/run/raw-schema.js +166 -0
  139. package/dist/run/raw-search-cost.d.ts +49 -0
  140. package/dist/run/raw-search-cost.d.ts.map +1 -0
  141. package/dist/run/raw-search-cost.js +79 -0
  142. package/dist/run/raw-support.d.ts +64 -6
  143. package/dist/run/raw-support.d.ts.map +1 -1
  144. package/dist/run/raw-support.js +84 -11
  145. package/dist/run/raw.d.ts +25 -26
  146. package/dist/run/raw.d.ts.map +1 -1
  147. package/dist/run/raw.js +30 -351
  148. package/dist/run/record-attempt.d.ts +8 -0
  149. package/dist/run/record-attempt.d.ts.map +1 -1
  150. package/dist/run/record-attempt.js +26 -12
  151. package/dist/run/record-call.d.ts +104 -0
  152. package/dist/run/record-call.d.ts.map +1 -0
  153. package/dist/run/record-call.js +277 -0
  154. package/dist/run/repair-attempt.d.ts +68 -0
  155. package/dist/run/repair-attempt.d.ts.map +1 -0
  156. package/dist/run/repair-attempt.js +180 -0
  157. package/dist/run/resolve-destination.d.ts +49 -0
  158. package/dist/run/resolve-destination.d.ts.map +1 -0
  159. package/dist/run/resolve-destination.js +76 -0
  160. package/dist/run/routing-docs.d.ts.map +1 -1
  161. package/dist/run/routing-docs.js +9 -4
  162. package/dist/run/run.d.ts +30 -1
  163. package/dist/run/run.d.ts.map +1 -1
  164. package/dist/run/run.js +75 -148
  165. package/dist/run/telemetry-row.d.ts +92 -0
  166. package/dist/run/telemetry-row.d.ts.map +1 -0
  167. package/dist/run/telemetry-row.js +81 -0
  168. package/dist/run/validate.d.ts +12 -1
  169. package/dist/run/validate.d.ts.map +1 -1
  170. package/dist/run/validate.js +31 -0
  171. package/dist/run/web-search-pricing.d.ts +43 -0
  172. package/dist/run/web-search-pricing.d.ts.map +1 -0
  173. package/dist/run/web-search-pricing.js +56 -0
  174. package/dist/settings/grading-guide.d.ts +27 -1
  175. package/dist/settings/grading-guide.d.ts.map +1 -1
  176. package/dist/status.d.ts +6 -0
  177. package/dist/status.d.ts.map +1 -1
  178. package/dist/status.js +4 -1
  179. package/dist/telemetry/artifact-store.d.ts.map +1 -1
  180. package/dist/telemetry/artifact-store.js +11 -0
  181. package/dist/telemetry/attempt-meta.d.ts +66 -1
  182. package/dist/telemetry/attempt-meta.d.ts.map +1 -1
  183. package/dist/telemetry/attempt-meta.js +27 -1
  184. package/dist/telemetry/exporter-setup.d.ts +2 -0
  185. package/dist/telemetry/exporter-setup.d.ts.map +1 -1
  186. package/dist/telemetry/exporter-setup.js +6 -1
  187. package/dist/telemetry/grade-mappers.d.ts +5 -2
  188. package/dist/telemetry/grade-mappers.d.ts.map +1 -1
  189. package/dist/telemetry/grade-mappers.js +8 -2
  190. package/dist/telemetry/grades-sql.d.ts +4 -1
  191. package/dist/telemetry/grades-sql.d.ts.map +1 -1
  192. package/dist/telemetry/grades-sql.js +26 -3
  193. package/dist/telemetry/ingest-protocol.d.ts +26 -2
  194. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  195. package/dist/telemetry/ingest-protocol.js +32 -29
  196. package/dist/telemetry/lanes.d.ts +31 -0
  197. package/dist/telemetry/lanes.d.ts.map +1 -0
  198. package/dist/telemetry/lanes.js +41 -0
  199. package/dist/telemetry/org-identity.d.ts +47 -0
  200. package/dist/telemetry/org-identity.d.ts.map +1 -0
  201. package/dist/telemetry/org-identity.js +100 -0
  202. package/dist/telemetry/route-registration.d.ts +48 -0
  203. package/dist/telemetry/route-registration.d.ts.map +1 -0
  204. package/dist/telemetry/route-registration.js +77 -0
  205. package/dist/telemetry/sinks.d.ts +10 -1
  206. package/dist/telemetry/sinks.d.ts.map +1 -1
  207. package/dist/telemetry/sinks.js +52 -6
  208. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  209. package/dist/telemetry/store-migrations.js +13 -0
  210. package/dist/tenancy/api-key-format.d.ts +16 -1
  211. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  212. package/dist/tenancy/api-key-format.js +17 -0
  213. package/dist/types/provider.d.ts +20 -3
  214. package/dist/types/provider.d.ts.map +1 -1
  215. package/dist/types/provider.js +1 -0
  216. package/dist/types/route.d.ts +76 -0
  217. package/dist/types/route.d.ts.map +1 -1
  218. package/dist/types/telemetry.d.ts +30 -2
  219. package/dist/types/telemetry.d.ts.map +1 -1
  220. package/docs/agent-skill/SKILL.md +75 -5
  221. package/docs/instrumentation.md +98 -36
  222. package/docs/llms.txt +96 -16
  223. package/docs/managed-serving.md +150 -0
  224. package/docs/mcp-oracle.md +75 -2
  225. package/docs/migration-playbook.md +5 -4
  226. package/docs/quickstart.md +169 -83
  227. package/package.json +3 -2
  228. package/registry/registry.json +82 -0
  229. package/dist/feedback/feedback.d.ts +0 -35
  230. package/dist/feedback/feedback.d.ts.map +0 -1
  231. package/dist/feedback/feedback.js +0 -39
@@ -0,0 +1,29 @@
1
+ /**
2
+ * PSR-2 — the content-hash id for a prompt version (`template_sha`).
3
+ *
4
+ * A prompt version is identified by the sha256 of its `template` + `manifest`
5
+ * (spec §2, the Humanloop content-hash pattern: identical content ⇒ no
6
+ * duplicate row). This is the ONE definition of that hash, used by:
7
+ * • the ingest + create-version writer (dedupe: an identical re-ingest resolves
8
+ * to the existing row rather than appending a byte-identical version), and
9
+ * • the served-projection check (does routes.bundle carry THIS version? — the
10
+ * "served this call" marker compares template_sha, PSR-2b).
11
+ *
12
+ * The manifest is CANONICALIZED first (object keys sorted recursively, array
13
+ * order preserved) so a semantically-identical manifest always hashes the same
14
+ * regardless of key order in the source artifact — the same discipline
15
+ * `resolved-bundle.ts` uses for `bundle_sha`. Callers MUST normalize the manifest
16
+ * ({@link normalizeManifest}) before hashing so incidental unknown keys never
17
+ * split the dedupe. Pure + dependency-free (node:crypto only).
18
+ */
19
+ import type { Manifest } from "./manifest";
20
+ /**
21
+ * The content-hash id of a prompt version — `sha256:<hex>`, self-describing so a
22
+ * future algorithm change is visible in the stored value rather than silent
23
+ * (parity with `hashResolvedBundle`). The template is hashed verbatim; the
24
+ * manifest is canonicalized. The two are joined with a NUL separator that cannot
25
+ * appear in the template, so no `(template, manifest)` pair can collide with a
26
+ * different pair by boundary ambiguity.
27
+ */
28
+ export declare function promptTemplateSha(template: string, manifest: Manifest): string;
29
+ //# sourceMappingURL=version-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-hash.d.ts","sourceRoot":"","sources":["../../src/prompts/version-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAkB3C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAO9E"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * PSR-2 — the content-hash id for a prompt version (`template_sha`).
4
+ *
5
+ * A prompt version is identified by the sha256 of its `template` + `manifest`
6
+ * (spec §2, the Humanloop content-hash pattern: identical content ⇒ no
7
+ * duplicate row). This is the ONE definition of that hash, used by:
8
+ * • the ingest + create-version writer (dedupe: an identical re-ingest resolves
9
+ * to the existing row rather than appending a byte-identical version), and
10
+ * • the served-projection check (does routes.bundle carry THIS version? — the
11
+ * "served this call" marker compares template_sha, PSR-2b).
12
+ *
13
+ * The manifest is CANONICALIZED first (object keys sorted recursively, array
14
+ * order preserved) so a semantically-identical manifest always hashes the same
15
+ * regardless of key order in the source artifact — the same discipline
16
+ * `resolved-bundle.ts` uses for `bundle_sha`. Callers MUST normalize the manifest
17
+ * ({@link normalizeManifest}) before hashing so incidental unknown keys never
18
+ * split the dedupe. Pure + dependency-free (node:crypto only).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.promptTemplateSha = promptTemplateSha;
22
+ const node_crypto_1 = require("node:crypto");
23
+ /** Deterministic JSON: object keys sorted recursively, array order preserved.
24
+ * The same value always produces the same bytes, so the hash is stable across
25
+ * processes. Mirrors resolved-bundle.ts's canonicalize (kept local so the two
26
+ * hashers stay independently auditable). */
27
+ function canonicalize(value) {
28
+ if (Array.isArray(value))
29
+ return value.map(canonicalize);
30
+ if (value !== null && typeof value === "object") {
31
+ const out = {};
32
+ for (const key of Object.keys(value).sort()) {
33
+ out[key] = canonicalize(value[key]);
34
+ }
35
+ return out;
36
+ }
37
+ return value;
38
+ }
39
+ /**
40
+ * The content-hash id of a prompt version — `sha256:<hex>`, self-describing so a
41
+ * future algorithm change is visible in the stored value rather than silent
42
+ * (parity with `hashResolvedBundle`). The template is hashed verbatim; the
43
+ * manifest is canonicalized. The two are joined with a NUL separator that cannot
44
+ * appear in the template, so no `(template, manifest)` pair can collide with a
45
+ * different pair by boundary ambiguity.
46
+ */
47
+ function promptTemplateSha(template, manifest) {
48
+ const canonicalManifest = JSON.stringify(canonicalize(manifest));
49
+ const hash = (0, node_crypto_1.createHash)("sha256");
50
+ hash.update(template, "utf8");
51
+ hash.update("\0", "utf8");
52
+ hash.update(canonicalManifest, "utf8");
53
+ return `sha256:${hash.digest("hex")}`;
54
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `modelrig prompts export` — PSR-3 (spec §3, the anti-lock-in gate).
3
+ *
4
+ * modelrig prompts export --route <route> [--out modelrig/routes]
5
+ * [--label prod | --version N] [--segment default] [--json]
6
+ *
7
+ * Fetches a prompt version from the ModelRig System-of-Record
8
+ * (`GET /v1/prompts/:route/export`, key-authed — a read needs only an org-scoped
9
+ * key) and writes its git files: the authored `template` to `<route>.md` and the
10
+ * assembly `manifest` to a sibling `<route>.manifest.json`, BYTE-FAITHFULLY. The
11
+ * command then PROVES the round-trip locally — it re-hashes the written files the
12
+ * way the store would (validate → normalize → hash) and checks the content id
13
+ * matches what ModelRig returned. ModelRig being your prompt's primary store
14
+ * never traps it in git.
15
+ *
16
+ * Goes through the API boundary (T12: the CLI never holds a DB client); the
17
+ * server scopes the read to the key's org (T11).
18
+ */
19
+ import type { Flags } from "./cli";
20
+ export declare function cmdPromptsExport(flags: Flags): Promise<number>;
21
+ //# sourceMappingURL=prompts-export-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts-export-cli.d.ts","sourceRoot":"","sources":["../src/prompts-export-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AA0CnC,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CA4EpE"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * `modelrig prompts export` — PSR-3 (spec §3, the anti-lock-in gate).
4
+ *
5
+ * modelrig prompts export --route <route> [--out modelrig/routes]
6
+ * [--label prod | --version N] [--segment default] [--json]
7
+ *
8
+ * Fetches a prompt version from the ModelRig System-of-Record
9
+ * (`GET /v1/prompts/:route/export`, key-authed — a read needs only an org-scoped
10
+ * key) and writes its git files: the authored `template` to `<route>.md` and the
11
+ * assembly `manifest` to a sibling `<route>.manifest.json`, BYTE-FAITHFULLY. The
12
+ * command then PROVES the round-trip locally — it re-hashes the written files the
13
+ * way the store would (validate → normalize → hash) and checks the content id
14
+ * matches what ModelRig returned. ModelRig being your prompt's primary store
15
+ * never traps it in git.
16
+ *
17
+ * Goes through the API boundary (T12: the CLI never holds a DB client); the
18
+ * server scopes the read to the key's org (T11).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.cmdPromptsExport = cmdPromptsExport;
22
+ const node_fs_1 = require("node:fs");
23
+ const node_path_1 = require("node:path");
24
+ const config_1 = require("./config");
25
+ const index_1 = require("./index");
26
+ const DEFAULT_OUT = "modelrig/routes";
27
+ function usage() {
28
+ return [
29
+ "usage: modelrig prompts export --route <route> [--out modelrig/routes]",
30
+ " [--label prod | --version N] [--segment default] [--json]",
31
+ "",
32
+ "Exports a prompt version from ModelRig back to git files (the anti-lock-in guarantee):",
33
+ " <route>.md the authored prompt template, verbatim",
34
+ " <route>.manifest.json the assembly manifest (structure only)",
35
+ "A version re-created from these files carries the SAME content hash — proven after write.",
36
+ "",
37
+ " MODELRIG_API_KEY a rig_sk_ key in your org (a read needs no special scope)",
38
+ " MODELRIG_INGEST_URL optional — the server base; defaults to the hosted API",
39
+ ].join("\n");
40
+ }
41
+ /** The content id a version re-created from the written files would carry — the
42
+ * store's write discipline (validate → normalize → hash) run locally, so the
43
+ * round-trip is proven without a second server call. */
44
+ function recreatedSha(templateFile, manifestFile) {
45
+ let parsed;
46
+ try {
47
+ parsed = JSON.parse(manifestFile);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ const validated = (0, index_1.validateManifest)(parsed);
53
+ if (!validated.ok)
54
+ return null;
55
+ return (0, index_1.promptTemplateSha)(templateFile, (0, index_1.normalizeManifest)(validated.manifest));
56
+ }
57
+ async function cmdPromptsExport(flags) {
58
+ // The subcommand token ("export") is flags.positional[0]; require it explicitly
59
+ // so `modelrig prompts` (no verb) prints usage rather than guessing.
60
+ if (flags.positional[0] !== "export") {
61
+ console.error(usage());
62
+ return 1;
63
+ }
64
+ const route = flags.named.get("route");
65
+ const out = flags.named.get("out") ?? DEFAULT_OUT;
66
+ const label = flags.named.get("label");
67
+ const versionRaw = flags.named.get("version");
68
+ const segment = flags.named.get("segment");
69
+ const asJson = flags.bare.has("json");
70
+ const exp = (0, config_1.loadConfigFromEnv)().export;
71
+ const api = exp !== undefined && exp.mode === "api" ? exp : undefined;
72
+ const base = api !== undefined ? new URL(api.ingestUrl).origin : "";
73
+ const key = api?.apiKey ?? "";
74
+ if (route === undefined || route === "" || base === "" || key === "") {
75
+ console.error(usage());
76
+ return 1;
77
+ }
78
+ const query = [];
79
+ if (versionRaw !== undefined && versionRaw !== "")
80
+ query.push(`version=${encodeURIComponent(versionRaw)}`);
81
+ else if (label !== undefined && label !== "")
82
+ query.push(`label=${encodeURIComponent(label)}`);
83
+ if (segment !== undefined && segment !== "")
84
+ query.push(`segment=${encodeURIComponent(segment)}`);
85
+ const suffix = query.length > 0 ? `?${query.join("&")}` : "";
86
+ let res;
87
+ try {
88
+ res = await fetch(`${base}/v1/prompts/${encodeURIComponent(route)}/export${suffix}`, {
89
+ headers: { authorization: `Bearer ${key}` },
90
+ });
91
+ }
92
+ catch (err) {
93
+ console.error(`prompts export: could not reach ${base}: ${err instanceof Error ? err.message : String(err)}`);
94
+ return 1;
95
+ }
96
+ const payload = (await res.json().catch(() => ({})));
97
+ if (!res.ok) {
98
+ console.error(`prompts export ${res.status}: ${[payload.error, payload.fix_hint].filter(Boolean).join(" — ") || "failed"}`);
99
+ return 1;
100
+ }
101
+ const files = payload.files ?? [];
102
+ if (files.length === 0 || payload.template_sha === undefined) {
103
+ console.error("prompts export: the server returned no files");
104
+ return 1;
105
+ }
106
+ // Write each file under --out, creating directories as needed.
107
+ const written = [];
108
+ for (const f of files) {
109
+ const dest = (0, node_path_1.join)(out, f.path);
110
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(dest), { recursive: true });
111
+ (0, node_fs_1.writeFileSync)(dest, f.content);
112
+ written.push(dest);
113
+ }
114
+ // Prove the round-trip from the bytes on disk (never trust the transfer): the
115
+ // template file + the manifest file must re-hash to the server's content id.
116
+ const md = files.find((f) => f.path.endsWith(".md"));
117
+ const mf = files.find((f) => f.path.endsWith(".manifest.json"));
118
+ const rehashed = md !== undefined && mf !== undefined ? recreatedSha(md.content, mf.content) : null;
119
+ const roundTripped = rehashed !== null && rehashed === payload.template_sha;
120
+ if (asJson) {
121
+ console.log(JSON.stringify({ route: payload.route, version: payload.version, template_sha: payload.template_sha, written, round_trip: roundTripped }, null, 2));
122
+ }
123
+ else {
124
+ console.log(`Exported ${route} version ${String(payload.version)} → ${written.join(", ")}`);
125
+ console.log(roundTripped
126
+ ? `Round-trip verified: re-creating from these files reproduces the same content hash (${payload.template_sha.slice(0, 12)}…). Your prompt is not locked in.`
127
+ : "WARNING: the written files did NOT re-hash to the server's content id — do not rely on this export; re-run.");
128
+ }
129
+ return roundTripped ? 0 : 1;
130
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"cache-policy.d.ts","sourceRoot":"","sources":["../../src/registry/cache-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;4CACwC;IACxC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC;;;2EAGuE;IACvE,QAAQ,CAAC,IAAI,EAAE,sBAAsB,GAAG,kBAAkB,GAAG,YAAY,CAAC;IAC1E,0EAA0E;IAC1E,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C;4CACwC;IACxC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C;sFACkF;IAClF,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC;8EAC0E;IAC1E,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C;mDAC+C;IAC/C,QAAQ,CAAC,eAAe,EAAE,kBAAkB,GAAG,eAAe,GAAG,eAAe,GAAG,IAAI,CAAC;IACxF;8EAC0E;IAC1E,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,qEAAqE;IACrE,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC,qDAAqD;IACrD,QAAQ,CAAC,aAAa,EAClB,WAAW,GACX,gBAAgB,GAChB,oBAAoB,GACpB,iBAAiB,GACjB,mBAAmB,CAAC;IACxB;;8BAE0B;IAC1B,QAAQ,CAAC,0BAA0B,EAAE,OAAO,CAAC;IAC7C,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AA8KD;;;;eAIe;AACf,wBAAgB,qBAAqB,IAAI,WAAW,CAiBnD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,WAAW,CAEhE;AAED,+EAA+E;AAC/E,wBAAgB,0BAA0B,IAAI,SAAS,UAAU,EAAE,CAElE"}
1
+ {"version":3,"file":"cache-policy.d.ts","sourceRoot":"","sources":["../../src/registry/cache-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;4CACwC;IACxC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC;;;2EAGuE;IACvE,QAAQ,CAAC,IAAI,EAAE,sBAAsB,GAAG,kBAAkB,GAAG,YAAY,CAAC;IAC1E,0EAA0E;IAC1E,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C;4CACwC;IACxC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C;sFACkF;IAClF,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC;8EAC0E;IAC1E,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C;mDAC+C;IAC/C,QAAQ,CAAC,eAAe,EAAE,kBAAkB,GAAG,eAAe,GAAG,eAAe,GAAG,IAAI,CAAC;IACxF;8EAC0E;IAC1E,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,qEAAqE;IACrE,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC,qDAAqD;IACrD,QAAQ,CAAC,aAAa,EAClB,WAAW,GACX,gBAAgB,GAChB,oBAAoB,GACpB,iBAAiB,GACjB,mBAAmB,CAAC;IACxB;;8BAE0B;IAC1B,QAAQ,CAAC,0BAA0B,EAAE,OAAO,CAAC;IAC7C,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAqMD;;;;eAIe;AACf,wBAAgB,qBAAqB,IAAI,WAAW,CAiBnD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,GAAG,WAAW,CAEhE;AAED,+EAA+E;AAC/E,wBAAgB,0BAA0B,IAAI,SAAS,UAAU,EAAE,CAElE"}
@@ -199,6 +199,28 @@ const POLICIES = {
199
199
  as_of: AS_OF,
200
200
  policy_version: AS_OF,
201
201
  },
202
+ // Z.ai / Zhipu (B4a) — GLM context caching is not verified first-party here
203
+ // (docs egress-blocked at build time), so fail-closed to cache_supported:false,
204
+ // identical L5 posture to DeepInfra. The raw-lane `cache.key` still maps to the
205
+ // OpenAI-dialect `prompt_cache_key` HINT (raw-support.ts) — that is call-shaping,
206
+ // not a claim that a warm cache exists on our path. Revisit when a first-party
207
+ // Z.ai caching page can be cited (then flip with a new as_of).
208
+ zai: {
209
+ cache_supported: false,
210
+ mode: "automatic_prefix",
211
+ default_ttl_seconds: null,
212
+ extended_ttl_seconds: null,
213
+ ttl_refresh_on_read: false,
214
+ min_cacheable_tokens: null,
215
+ cache_key_param: null,
216
+ reports_read_tokens: false,
217
+ reports_write_tokens: false,
218
+ usage_dialect: "openai_details",
219
+ provider_affinity_required: false,
220
+ source: "z.ai — cached rates unverified first-party (fail-closed, L5)",
221
+ as_of: AS_OF,
222
+ policy_version: AS_OF,
223
+ },
202
224
  };
203
225
  /** The fail-closed policy for any provider not seeded (spec §3): caching is
204
226
  * assumed OFF until a first-party source says otherwise — identical posture to
@@ -29,11 +29,32 @@ export interface TokenRates {
29
29
  * ⇒ the 1h write bucket bills at `cache_write` (fail-closed, dev-rule L5). */
30
30
  readonly cache_write_1h?: number | null;
31
31
  }
32
+ /**
33
+ * Per-component tier multipliers (issue #2). A vendor's tier does not always
34
+ * scale every component by one number: Google's Gemini `flex` (and batch) is
35
+ * 0.5× input + output while cache read is "Same as Standard" — so a single
36
+ * scalar over-discounts the cached portion. An ABSENT component here is EXEMPT
37
+ * (multiplier 1.0 — the standard rate), never a guess: write a component
38
+ * explicitly when the vendor publishes a different multiplier for it (L5).
39
+ */
40
+ export interface TierComponentMultipliers {
41
+ readonly input: number;
42
+ readonly output: number;
43
+ /** absent/null ⇒ 1.0 (exempt — bills at the standard cache-read rate). */
44
+ readonly cache_read?: number | null;
45
+ readonly cache_write?: number | null;
46
+ readonly cache_write_1h?: number | null;
47
+ }
48
+ /** A tier multiplier: one scalar over EVERY component (`1.25`), or a
49
+ * per-component block (`{ input: 0.5, output: 0.5, cache_read: 1.0 }`). */
50
+ export type TierMultiplier = number | TierComponentMultipliers;
32
51
  /** MULTIPLIERS on `base` (1.0 = base), NOT absolute rates. */
33
52
  export interface ServingTiers {
34
- readonly priority?: number | null;
35
- readonly fast?: number | null;
36
- readonly region_us?: number | null;
53
+ readonly priority?: TierMultiplier | null;
54
+ readonly fast?: TierMultiplier | null;
55
+ readonly region_us?: TierMultiplier | null;
56
+ /** Gemini flex (issue #2): `{ input: 0.5, output: 0.5, cache_read: 1.0 }`. */
57
+ readonly flex?: TierMultiplier | null;
37
58
  }
38
59
  /** A rate change that applies above a token threshold. */
39
60
  export interface ContextBand {
@@ -101,12 +122,21 @@ export declare const SNAPSHOT_BASIS = "base \u00B7 standard tier \u00B7 text";
101
122
  * the caller supplies provenance. `source` defaults to the snapshot lane.
102
123
  */
103
124
  export declare function synthesizeFromFlat(input: number, output: number, cache_read: number | null, asOf: string, source?: string): ModelPricingV2;
104
- export type ServingTierName = "priority" | "fast" | "region_us";
125
+ export type ServingTierName = "priority" | "fast" | "region_us" | "flex";
126
+ /** Resolve a tier multiplier to one multiplier PER component. A scalar scales
127
+ * every component (the original contract, byte-identical); a per-component
128
+ * block fills an absent component with 1.0 — EXEMPT, i.e. the standard rate. */
129
+ export declare function tierComponentMultipliers(mult: TierMultiplier): Required<{
130
+ [K in keyof TierComponentMultipliers]: number;
131
+ }>;
105
132
  /**
106
- * Absolute rates for a serving tier = `base × multiplier`. Returns `null` when
107
- * the tier's multiplier is absent — fail-closed, so no surface can render a
108
- * tier price the model never published. The multiplier scales every rate
109
- * component present on `base`.
133
+ * Absolute rates for a serving tier = `base × multiplier`, PER COMPONENT.
134
+ * Returns `null` when the tier's multiplier is absent — fail-closed, so no
135
+ * surface (and no cost row) can carry a tier price the model never published.
136
+ * A scalar multiplier scales every rate component present on `base`; a
137
+ * per-component block scales each component by its own multiplier, with an
138
+ * absent component exempt (1.0) — issue #2: Gemini flex discounts input and
139
+ * output but NOT cache read.
110
140
  */
111
141
  export declare function tierRates(pricing: ModelPricingV2, tier: ServingTierName): TokenRates | null;
112
142
  /** A display-ready projection of a price. `basis` is always present, so a
@@ -1 +1 @@
1
- {"version":3,"file":"pricing-shape.d.ts","sourceRoot":"","sources":["../../src/registry/pricing-shape.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pricing-shape.d.ts","sourceRoot":"","sources":["../../src/registry/pricing-shape.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.SNAPSHOT_BASIS = void 0;
24
24
  exports.synthesizeFromFlat = synthesizeFromFlat;
25
+ exports.tierComponentMultipliers = tierComponentMultipliers;
25
26
  exports.tierRates = tierRates;
26
27
  exports.describePricingV2 = describePricingV2;
27
28
  /** The basis string for a snapshot-synthesized (base-only, standard, text)
@@ -52,25 +53,44 @@ function synthesizeFromFlat(input, output, cache_read, asOf, source = "litellm-s
52
53
  function scale(rate, mult) {
53
54
  return rate === null || rate === undefined ? null : rate * mult;
54
55
  }
56
+ /** Resolve a tier multiplier to one multiplier PER component. A scalar scales
57
+ * every component (the original contract, byte-identical); a per-component
58
+ * block fills an absent component with 1.0 — EXEMPT, i.e. the standard rate. */
59
+ function tierComponentMultipliers(mult) {
60
+ if (typeof mult === "number") {
61
+ return { input: mult, output: mult, cache_read: mult, cache_write: mult, cache_write_1h: mult };
62
+ }
63
+ return {
64
+ input: mult.input,
65
+ output: mult.output,
66
+ cache_read: mult.cache_read ?? 1,
67
+ cache_write: mult.cache_write ?? 1,
68
+ cache_write_1h: mult.cache_write_1h ?? 1,
69
+ };
70
+ }
55
71
  /**
56
- * Absolute rates for a serving tier = `base × multiplier`. Returns `null` when
57
- * the tier's multiplier is absent — fail-closed, so no surface can render a
58
- * tier price the model never published. The multiplier scales every rate
59
- * component present on `base`.
72
+ * Absolute rates for a serving tier = `base × multiplier`, PER COMPONENT.
73
+ * Returns `null` when the tier's multiplier is absent — fail-closed, so no
74
+ * surface (and no cost row) can carry a tier price the model never published.
75
+ * A scalar multiplier scales every rate component present on `base`; a
76
+ * per-component block scales each component by its own multiplier, with an
77
+ * absent component exempt (1.0) — issue #2: Gemini flex discounts input and
78
+ * output but NOT cache read.
60
79
  */
61
80
  function tierRates(pricing, tier) {
62
81
  const mult = pricing.tiers?.[tier];
63
82
  if (mult === null || mult === undefined)
64
83
  return null;
84
+ const m = tierComponentMultipliers(mult);
65
85
  return {
66
- input: pricing.base.input * mult,
67
- output: pricing.base.output * mult,
68
- cache_read: scale(pricing.base.cache_read, mult),
69
- cache_write: scale(pricing.base.cache_write, mult),
70
- cache_write_1h: scale(pricing.base.cache_write_1h, mult),
86
+ input: pricing.base.input * m.input,
87
+ output: pricing.base.output * m.output,
88
+ cache_read: scale(pricing.base.cache_read, m.cache_read),
89
+ cache_write: scale(pricing.base.cache_write, m.cache_write),
90
+ cache_write_1h: scale(pricing.base.cache_write_1h, m.cache_write_1h),
71
91
  };
72
92
  }
73
- const TIER_ORDER = ["priority", "fast", "region_us"];
93
+ const TIER_ORDER = ["priority", "fast", "region_us", "flex"];
74
94
  /** USD-per-token → "$X.XX" per Mtok. Adaptive precision: a pricing-authority
75
95
  * surface must not misrepresent a rate — `$0.075/M` renders as `$0.075`, not a
76
96
  * rounded `$0.07`/`$0.08`. Shows up to 6 decimals, trailing zeros trimmed, with
@@ -94,8 +114,16 @@ function describePricingV2(pricing) {
94
114
  const tiers = [];
95
115
  for (const name of TIER_ORDER) {
96
116
  const rates = tierRates(pricing, name);
97
- if (rates !== null)
98
- tiers.push({ name, rate: inOut(rates) });
117
+ if (rates === null)
118
+ continue;
119
+ // Every scaled tier that publishes a cache-read rate states it explicitly,
120
+ // so a reader never infers the cache rate from the in/out multiplier
121
+ // (gemini flex: in/out 0.5× but cache read NOT discounted). Only a tier whose
122
+ // input equals base (a 1.0× tier) omits the note.
123
+ const cacheNote = rates.cache_read != null && pricing.base.cache_read != null && rates.input !== pricing.base.input
124
+ ? ` · cache read ${perMtok(rates.cache_read)}`
125
+ : "";
126
+ tiers.push({ name, rate: `${inOut(rates)}${cacheNote}` });
99
127
  }
100
128
  const contextBands = (pricing.context_bands ?? []).map((b) => `above ${b.above_tokens.toLocaleString()} tok: ${inOut(b.rates)}`);
101
129
  const modality = [];
@@ -25,11 +25,12 @@ export interface ModelPricing {
25
25
  }
26
26
  export declare function getPricing(provider: ProviderId, model: string): ModelPricing | null;
27
27
  /**
28
- * The richer `ModelPricingV2` a vendor overlay/manual entry carries, if any —
29
- * resolved by the SAME key precedence as {@link getPricing} (snapshot wins on a
30
- * shared key, and the snapshot never carries `pricing_v2`, so a shared key
31
- * yields null → the caller synthesizes from the flat rate). Returns null when
32
- * no richer shape is on file. Never guesses: absence is `base`-only downstream.
28
+ * The richer `ModelPricingV2` on file for a model, if any — resolved by the SAME
29
+ * key precedence as {@link getPricing} (snapshot wins on a shared key; a snapshot
30
+ * entry yields its hand-grafted `pricing_v2` when it has one — issue #2's gemini
31
+ * tiers — else null → the caller synthesizes from the flat rate; an overlay or
32
+ * manual entry yields its own block). Returns null when no richer shape is on
33
+ * file. Never guesses: absence is `base`-only downstream.
33
34
  */
34
35
  export declare function getPricingV2(provider: ProviderId, model: string): ModelPricingV2 | null;
35
36
  export declare function estimateCostUsd(pricing: ModelPricing, tokensIn: number, tokensOut: number, tokensCached: number,
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/registry/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C;;uEAEmE;IACnE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C;;sEAEkE;IAClE,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C;AAyDD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA6BnF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYvF;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM;AACpB;;;0CAG0C;AAC1C,gBAAgB,SAAI;AACpB,+EAA+E;AAC/E,kBAAkB,SAAI,GACrB,MAAM,CAsBR"}
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/registry/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C;;uEAEmE;IACnE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C;;sEAEkE;IAClE,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C;AA+DD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA6BnF;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYvF;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM;AACpB;;;0CAG0C;AAC1C,gBAAgB,SAAI;AACpB,+EAA+E;AAC/E,kBAAkB,SAAI,GACrB,MAAM,CAsBR"}
@@ -29,6 +29,9 @@ const KEY_PREFIXES = {
29
29
  // Fireworks pricing keys carry the accounts/… wire prefix.
30
30
  deepinfra: ["deepinfra/"],
31
31
  fireworks: ["fireworks_ai/accounts/fireworks/models/", "fireworks_ai/"],
32
+ // Z.ai (B4a): GLM models keyed under "zai/" in the snapshot. Registry entries
33
+ // carry their own declared pricing, so this only gaps-fills a future snapshot.
34
+ zai: ["zai/"],
32
35
  };
33
36
  let cachedMap = null;
34
37
  /** Host-swept gap-filler (scripts/pricing-sweep.ts): prices for models the
@@ -77,11 +80,12 @@ function getPricing(provider, model) {
77
80
  return null;
78
81
  }
79
82
  /**
80
- * The richer `ModelPricingV2` a vendor overlay/manual entry carries, if any —
81
- * resolved by the SAME key precedence as {@link getPricing} (snapshot wins on a
82
- * shared key, and the snapshot never carries `pricing_v2`, so a shared key
83
- * yields null → the caller synthesizes from the flat rate). Returns null when
84
- * no richer shape is on file. Never guesses: absence is `base`-only downstream.
83
+ * The richer `ModelPricingV2` on file for a model, if any — resolved by the SAME
84
+ * key precedence as {@link getPricing} (snapshot wins on a shared key; a snapshot
85
+ * entry yields its hand-grafted `pricing_v2` when it has one — issue #2's gemini
86
+ * tiers — else null → the caller synthesizes from the flat rate; an overlay or
87
+ * manual entry yields its own block). Returns null when no richer shape is on
88
+ * file. Never guesses: absence is `base`-only downstream.
85
89
  */
86
90
  function getPricingV2(provider, model) {
87
91
  if (model === "")
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Serving-`destination` route-field validation (Unified Route U2, ruling D-2).
3
+ * Extracted from loader.ts so loader.ts holds under the 700-LOC cap (same reason
4
+ * loader-warnings.ts / screen-loader.ts were carved out). Hand-written validation
5
+ * mirroring the loader's PROVIDERS/TIERS pattern: a route's `destination:` is
6
+ * checked against the DESTINATIONS vocabulary (the ONE source of truth in
7
+ * run/destination.ts — no second list to drift); an unknown value pushes a
8
+ * teaching problem (the caller turns it into a RouteConfigError). Absent ⇒
9
+ * undefined ⇒ resolves to `direct` at run time ⇒ BYTE-IDENTICAL.
10
+ */
11
+ import type { Destination } from "../run/destination";
12
+ /** Validate a route bundle's `destination:` value. Absent/null ⇒ undefined
13
+ * (defaults to `direct`); a known value ⇒ that `Destination`; an unknown value ⇒
14
+ * a pushed problem and undefined. */
15
+ export declare function parseDestination(raw: unknown, problems: string[]): Destination | undefined;
16
+ //# sourceMappingURL=destination-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"destination-loader.d.ts","sourceRoot":"","sources":["../../src/routes/destination-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAMtD;;qCAEqC;AACrC,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,WAAW,GAAG,SAAS,CAO1F"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /**
3
+ * Serving-`destination` route-field validation (Unified Route U2, ruling D-2).
4
+ * Extracted from loader.ts so loader.ts holds under the 700-LOC cap (same reason
5
+ * loader-warnings.ts / screen-loader.ts were carved out). Hand-written validation
6
+ * mirroring the loader's PROVIDERS/TIERS pattern: a route's `destination:` is
7
+ * checked against the DESTINATIONS vocabulary (the ONE source of truth in
8
+ * run/destination.ts — no second list to drift); an unknown value pushes a
9
+ * teaching problem (the caller turns it into a RouteConfigError). Absent ⇒
10
+ * undefined ⇒ resolves to `direct` at run time ⇒ BYTE-IDENTICAL.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.parseDestination = parseDestination;
14
+ const destination_1 = require("../run/destination");
15
+ /** The serving-`destination` vocabulary as a lookup set, mirroring the loader's
16
+ * PROVIDERS/TIERS ReadonlySets. */
17
+ const DESTINATIONS = new Set(destination_1.DESTINATIONS);
18
+ /** Validate a route bundle's `destination:` value. Absent/null ⇒ undefined
19
+ * (defaults to `direct`); a known value ⇒ that `Destination`; an unknown value ⇒
20
+ * a pushed problem and undefined. */
21
+ function parseDestination(raw, problems) {
22
+ if (raw === undefined || raw === null)
23
+ return undefined;
24
+ if (typeof raw !== "string" || !DESTINATIONS.has(raw)) {
25
+ problems.push(`destination "${String(raw)}" is not one of [${[...DESTINATIONS].join(", ")}]`);
26
+ return undefined;
27
+ }
28
+ return raw;
29
+ }
@@ -34,6 +34,13 @@ export interface RouteDraftSpec {
34
34
  readonly variables: readonly string[];
35
35
  /** JSON Schema for the output; null drafts an unstructured route. */
36
36
  readonly outputSchema: object | null;
37
+ /** `output: text` (issue #1) — a prose/markdown route, returned verbatim.
38
+ * Absent / `"json"` ⇒ nothing is emitted (the loader default). `text` with a
39
+ * non-null `outputSchema` is refused here, as the loader would refuse it. */
40
+ readonly outputKind?: "json" | "text";
41
+ /** `prompt.user` (issue #5, R1) — the per-call user-turn template, written
42
+ * to `prompts/<name>.user.md` and declared beside `prompt.system`. */
43
+ readonly userPrompt?: string;
37
44
  readonly require?: readonly string[];
38
45
  readonly prefer?: readonly string[];
39
46
  readonly policy?: {
@@ -1 +1 @@
1
- {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/routes/draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,CAAC;IAC/C;wCACoC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,qEAAqE;IACrE,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KACrD,CAAC;IACF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;6EACyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;CACtC;AAmBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAgGhE;AAMD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE;IACF,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1D,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D,EACD,IAAI,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,GAC7E,gBAAgB,CAclB"}
1
+ {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/routes/draft.ts"],"names":[],"mappings":"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"}
@@ -40,10 +40,15 @@ function buildRouteDraft(spec) {
40
40
  throw new Error(`route draft "${spec.route}" has no candidates — a route with nothing allowed to serve it ` +
41
41
  "cannot run. Name at least one provider/model.");
42
42
  }
43
+ if (spec.outputKind === "text" && spec.outputSchema !== null) {
44
+ throw new Error(`route draft "${spec.route}" declares output: text AND a schema — a prose route has no schema. ` +
45
+ "Drop the schema, or draft a JSON route.");
46
+ }
43
47
  const name = slug(spec.route);
44
48
  const files = [];
45
49
  const schemaRef = spec.outputSchema === null ? null : `./schemas/${name}.schema.json`;
46
50
  const promptRef = `./prompts/${name}.system.md`;
51
+ const userPromptRef = spec.userPrompt === undefined ? null : `./prompts/${name}.user.md`;
47
52
  const lines = [
48
53
  "# ModelRig route bundle — DRAFT. Nothing in your app calls this yet.",
49
54
  "# Read it, edit it, move it up one directory into modelrig/routes/, then",
@@ -55,6 +60,8 @@ function buildRouteDraft(spec) {
55
60
  lines.push(`# ${line}`);
56
61
  }
57
62
  lines.push("#", "# Candidates are ordered incumbent-first: the model you already use leads,", "# so the first run reproduces today's behavior. The cheaper probed", "# alternatives below it are what a bake-off would put to the test.", "#", "# `prefer` is empty ON PURPOSE. Adding `cost` re-sorts these candidates by", "# price, which would hand the first run to the cheapest model rather than", "# to your incumbent — the opposite of a like-for-like migration. Prove a", "# cheaper model with a bake-off, then switch to it deliberately.", `route: ${yamlString(spec.route)}`, "version: 1");
63
+ if (spec.outputKind === "text")
64
+ lines.push("output: text # prose/markdown, returned verbatim (no schema:)");
58
65
  if (schemaRef !== null)
59
66
  lines.push(`schema: ${schemaRef}`);
60
67
  lines.push("candidates:");
@@ -78,6 +85,8 @@ function buildRouteDraft(spec) {
78
85
  lines.push(`prefer: [${(spec.prefer ?? []).map(yamlString).join(", ")}]`);
79
86
  lines.push("prompt:");
80
87
  lines.push(` system: ${promptRef}`);
88
+ if (userPromptRef !== null)
89
+ lines.push(` user: ${userPromptRef} # per-call user turn (the delta next to a cache handle)`);
81
90
  lines.push(` variables: [${spec.variables.map(yamlString).join(", ")}]` +
82
91
  (spec.variables.length === 0 ? " # no {{variables}} detected — the prompt is static" : ""));
83
92
  const policy = spec.policy ?? {};
@@ -105,6 +114,9 @@ function buildRouteDraft(spec) {
105
114
  });
106
115
  }
107
116
  files.push({ path: `prompts/${name}.system.md`, contents: ensureTrailingNewline(spec.systemPrompt) });
117
+ if (spec.userPrompt !== undefined) {
118
+ files.push({ path: `prompts/${name}.user.md`, contents: ensureTrailingNewline(spec.userPrompt) });
119
+ }
108
120
  return { route: spec.route, files };
109
121
  }
110
122
  function ensureTrailingNewline(text) {