@bytesbrains/cruise-worker 0.0.0-stage → 0.12.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 (137) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +37 -2
  3. package/index.d.ts +14 -0
  4. package/index.mjs +41 -0
  5. package/migrations/0001_virtual_keys.sql +102 -0
  6. package/migrations/0002_seed_projects.sql +16 -0
  7. package/migrations/0003_budgets.sql +43 -0
  8. package/migrations/0004_tenant_isolation_and_integer_money.sql +49 -0
  9. package/migrations/0005_model_prices.sql +42 -0
  10. package/migrations/0006_budget_reservation.sql +38 -0
  11. package/migrations/0007_routing_table.sql +93 -0
  12. package/migrations/0008_measurement_provenance.sql +24 -0
  13. package/migrations/0009_conformance.sql +37 -0
  14. package/migrations/0010_conformance_key.sql +20 -0
  15. package/migrations/0011_correct_workers_ai_price.sql +30 -0
  16. package/migrations/0012_rate_limits.sql +28 -0
  17. package/migrations/0013_deepseek_and_mistral.sql +42 -0
  18. package/migrations/0014_cost_ledger.sql +104 -0
  19. package/migrations/0015_memberships.sql +33 -0
  20. package/migrations/0016_latency.sql +23 -0
  21. package/migrations/0017_ledger_provider_index.sql +8 -0
  22. package/migrations/0018_fallback_chains.sql +34 -0
  23. package/migrations/0019_rate_buckets.sql +27 -0
  24. package/migrations/0020_mistral_price_and_pin.sql +62 -0
  25. package/migrations/0021_mistral_price_date_is_utc.sql +35 -0
  26. package/migrations/0022_drop_unenforced_key_budget.sql +32 -0
  27. package/migrations/0023_model_limits.sql +48 -0
  28. package/migrations/0024_conditional_prices.sql +84 -0
  29. package/migrations/0025_catalogue_from_keyed_hosts.sql +172 -0
  30. package/migrations/0026_lanes.sql +83 -0
  31. package/migrations/0027_lane_alternatives.sql +36 -0
  32. package/migrations/0028_lanes_tenant_scope.sql +66 -0
  33. package/migrations/0029_lane_jobs.sql +146 -0
  34. package/migrations/0030_lane_requires.sql +38 -0
  35. package/migrations/0031_tenant_billable.sql +15 -0
  36. package/migrations/0032_cache_hit_billing.sql +35 -0
  37. package/migrations/0033_sell_markup.sql +37 -0
  38. package/migrations/0034_tiers_and_lane_quotas.sql +38 -0
  39. package/migrations/0035_wallet_scope.sql +68 -0
  40. package/migrations/0036_credit_grants.sql +53 -0
  41. package/migrations/0037_reconciliation.sql +45 -0
  42. package/migrations/0038_balance_service_keys.sql +35 -0
  43. package/migrations/0039_deepseek_peak_is_weekdays.sql +77 -0
  44. package/migrations/0040_glm_5_3_on_workers_ai.sql +63 -0
  45. package/migrations/0041_mistral_sweep_2026_08_29.sql +89 -0
  46. package/migrations/0042_provision_service_keys.sql +50 -0
  47. package/migrations/0043_tier_project_quota.sql +32 -0
  48. package/migrations/0044_audit_tenant_scope.sql +49 -0
  49. package/migrations/0045_audit_backfill_derivable.sql +42 -0
  50. package/migrations/0046_measurement_note.sql +24 -0
  51. package/migrations/0047_ledger_max_tokens.sql +21 -0
  52. package/migrations/0048_price_observations.sql +140 -0
  53. package/migrations/0049_openai_and_gemini.sql +239 -0
  54. package/migrations/0050_meta_direct_upstream.sql +127 -0
  55. package/migrations/0051_upstream_credential_and_operator.sql +74 -0
  56. package/migrations/0052_muse_spark_1_3.sql +71 -0
  57. package/migrations/0053_anthropic_groq_and_xai.sql +172 -0
  58. package/migrations/0054_tier_service_keys.sql +60 -0
  59. package/migrations/0055_output_defaults.sql +79 -0
  60. package/migrations/0056_model_modality.sql +37 -0
  61. package/migrations/0057_openai_image_models.sql +177 -0
  62. package/migrations/0058_price_components.sql +230 -0
  63. package/migrations/0059_speech.sql +151 -0
  64. package/migrations/0060_embeddings.sql +131 -0
  65. package/migrations/0061_data_retention_backfill.sql +121 -0
  66. package/migrations/0062_gemini_embedding_disabled.sql +125 -0
  67. package/migrations/0063_google_provider_path_prefix.sql +59 -0
  68. package/migrations/0064_economics_service_keys.sql +51 -0
  69. package/migrations/0065_lane_selection.sql +66 -0
  70. package/migrations/0066_tenant_default_selection.sql +42 -0
  71. package/migrations/0067_graduated_selection.sql +103 -0
  72. package/migrations/0068_cache_scope.sql +73 -0
  73. package/migrations/0069_lane_cache_settings.sql +50 -0
  74. package/migrations/0070_lane_member_quality_rank.sql +98 -0
  75. package/migrations/0071_key_cache_pool.sql +60 -0
  76. package/migrations/0072_cache_digest.sql +51 -0
  77. package/migrations/0073_lane_max_chain.sql +51 -0
  78. package/migrations/0074_hunter_builder_lanes.sql +149 -0
  79. package/migrations/0075_hunter_builder_frontline.sql +36 -0
  80. package/migrations/0076_deepseek_flash.sql +86 -0
  81. package/migrations/0077_gpt_6_astra.sql +35 -0
  82. package/migrations/0078_astra_deep_reasoning.sql +20 -0
  83. package/migrations/0079_gemini_flash_lite_extraction.sql +15 -0
  84. package/migrations/0080_priced_by.sql +24 -0
  85. package/migrations/0081_grok_imagine.sql +81 -0
  86. package/migrations/0082_markup_three_percent.sql +32 -0
  87. package/migrations/0083_markup_per_tenant_and_tier.sql +57 -0
  88. package/migrations/0084_coding_lanes_frontier.sql +93 -0
  89. package/migrations/0085_minute_period_and_demo_charge.sql +77 -0
  90. package/migrations/0086_project_sentry.sql +41 -0
  91. package/migrations/0087_lane_router.sql +71 -0
  92. package/migrations/0088_openai_frontier_coding_lanes.sql +54 -0
  93. package/migrations/0089_agentic_coding_drops_codestral.sql +44 -0
  94. package/migrations/0090_board_followups_2026_10.sql +196 -0
  95. package/migrations/0091_opus_5_5_coding_lanes.sql +43 -0
  96. package/migrations/0092_deepseek_peak_holidays.sql +83 -0
  97. package/migrations/0093_tool_choice_forced.sql +26 -0
  98. package/migrations/0094_board_followups_2026_10_08.sql +115 -0
  99. package/migrations/0095_request_compression.sql +43 -0
  100. package/migrations/0096_lane_compress.sql +36 -0
  101. package/migrations/0097_tenant_default_compress.sql +34 -0
  102. package/migrations/0098_ledger_session.sql +47 -0
  103. package/migrations/0099_wallet_low_water_backfill.sql +47 -0
  104. package/migrations/0100_rag_grounded_answers_lane.sql +92 -0
  105. package/migrations/0101_perplexity_agent_api.sql +125 -0
  106. package/migrations/0102_web_search_job_and_flag.sql +137 -0
  107. package/migrations/0103_openai_web_search_price.sql +44 -0
  108. package/migrations/0104_web_search_lane.sql +82 -0
  109. package/migrations/0105_web_search_lane_billed.sql +22 -0
  110. package/migrations/0106_reservations_journal.sql +64 -0
  111. package/migrations/0107_session_cap.sql +65 -0
  112. package/migrations/0108_ledger_attempts.sql +41 -0
  113. package/migrations/0109_gemini_retention.sql +39 -0
  114. package/migrations/0110_gemini_4_argon.sql +110 -0
  115. package/migrations/0111_gemini_4_argon_lanes.sql +80 -0
  116. package/migrations/0112_ledger_affinity.sql +42 -0
  117. package/migrations/0113_contributor_lane_namespace.sql +125 -0
  118. package/migrations/0114_meta_contributor_models.sql +55 -0
  119. package/migrations/0115_oss_chat_assistant_lane.sql +41 -0
  120. package/migrations/0116_promo_credits.sql +63 -0
  121. package/migrations/0117_oss_mirror_lanes.sql +85 -0
  122. package/migrations/0118_project_management_job.sql +112 -0
  123. package/migrations/0119_oss_project_management_lane.sql +29 -0
  124. package/migrations/0120_moonshot_k3_1_pro.sql +107 -0
  125. package/migrations/0121_jobs.sql +100 -0
  126. package/migrations/0122_batch_prices.sql +188 -0
  127. package/migrations/0123_batch_files.sql +50 -0
  128. package/migrations/0124_batch_run.sql +21 -0
  129. package/migrations/0125_anthropic_path_prefix.sql +22 -0
  130. package/package.json +37 -4
  131. package/worker/284906b51fc0a3173ea7d301234907f30e834f54-cruise-mark-64.png +0 -0
  132. package/worker/2c39de526314484775fdfc503fac50c437d21e4e-cruise-logo-240.png +0 -0
  133. package/worker/63fb63e2d8ae6d8b68228768ef6c91ab8f0c0100-cruise-logo-480.png +0 -0
  134. package/worker/VERSION +1 -0
  135. package/worker/b23bfbc792e8710d3b74284a0a1b8ab044c18897-logo-dark.png +0 -0
  136. package/worker/index.js +31451 -0
  137. package/wrangler.template.jsonc +48 -0
@@ -0,0 +1,188 @@
1
+ -- A price for work that may wait: the provider batch tier (#716, epic #712).
2
+ --
3
+ -- A batch is billed at a different rate from the same model called
4
+ -- synchronously — OpenAI's is half, on every model in this file. That is a
5
+ -- third condition on a price, beside the two 0024 added (prompt-size tier and
6
+ -- peak window), and it is unlike them in one way that decides the shape: **a
7
+ -- synchronous request must never be priced at it.** A peak row is chosen by
8
+ -- the clock and a tier row by the prompt, so the resolver picks between them
9
+ -- per request. The batch row is chosen by *which surface the request came in
10
+ -- on*, which the resolver has no way to see and must not be asked to guess.
11
+ --
12
+ -- ## So the condition is a column, and the existing view filters it out
13
+ --
14
+ -- `price_observations` gains `service_tier`: '' is the rate everything has
15
+ -- been priced at until now, 'batch' the provider's batch rate. It joins the
16
+ -- primary key, because the same model, date, tier and window are observed at
17
+ -- both rates, often in the same pass.
18
+ --
19
+ -- `model_prices` keeps its name and its columns and now reads **only** the ''
20
+ -- tier. Every reader of it today — the resolver, lane prices, the catalogue,
21
+ -- doctor, reports, the demo parity check — goes on seeing exactly the rows it
22
+ -- saw before this migration, and none of them can see a batch rate by
23
+ -- accident. The alternative, a `service_tier` column on the view that every
24
+ -- reader then filters, is one forgotten WHERE from billing a synchronous
25
+ -- request at half price, and a lane's price would read the minimum through a
26
+ -- maximum (CLAUDE.md: a lane's price is the maximum) the first time a reader
27
+ -- forgot.
28
+ --
29
+ -- `model_batch_prices` is the same view over the 'batch' tier, and is read by
30
+ -- the batch path alone (#716).
31
+ --
32
+ -- ## The rebuild
33
+ --
34
+ -- The primary key changes, so the table is rebuilt — the 0024 shape. Every
35
+ -- observation is carried with `service_tier` '', which is what every one of
36
+ -- them was. Nothing references `price_observations` but the view, which is
37
+ -- dropped first and recreated after. The demo's seed zeroes every observation
38
+ -- (`src/demo-catalogue.ts`), so the batch rows it inherits from a migration
39
+ -- are zeroed with the rest.
40
+
41
+ DROP VIEW model_prices;
42
+
43
+ CREATE TABLE price_observations_new (
44
+ observed_at TEXT NOT NULL DEFAULT (datetime('now')),
45
+ model TEXT NOT NULL,
46
+ effective_from TEXT NOT NULL,
47
+ prompt_tokens_from INTEGER NOT NULL DEFAULT 0,
48
+ peak_utc TEXT NOT NULL DEFAULT '',
49
+ service_tier TEXT NOT NULL DEFAULT '' CHECK (service_tier IN ('', 'batch')),
50
+ input_micros_per_mtok INTEGER NOT NULL,
51
+ output_micros_per_mtok INTEGER NOT NULL,
52
+ cached_input_micros_per_mtok INTEGER,
53
+ source TEXT NOT NULL,
54
+ note TEXT,
55
+ retracted INTEGER NOT NULL DEFAULT 0 CHECK (retracted IN (0, 1)),
56
+ PRIMARY KEY (model, effective_from, prompt_tokens_from, peak_utc, service_tier, observed_at)
57
+ );
58
+
59
+ INSERT INTO price_observations_new
60
+ (observed_at, model, effective_from, prompt_tokens_from, peak_utc,
61
+ input_micros_per_mtok, output_micros_per_mtok, cached_input_micros_per_mtok, source, note, retracted)
62
+ SELECT observed_at, model, effective_from, prompt_tokens_from, peak_utc,
63
+ input_micros_per_mtok, output_micros_per_mtok, cached_input_micros_per_mtok, source, note, retracted
64
+ FROM price_observations;
65
+
66
+ DROP TABLE price_observations;
67
+ ALTER TABLE price_observations_new RENAME TO price_observations;
68
+
69
+ CREATE INDEX idx_price_observations_lookup ON price_observations (model, effective_from);
70
+
71
+ -- Unchanged from 0048 but for the tier: the latest surviving observation per
72
+ -- price key, standard rate only.
73
+ CREATE VIEW model_prices AS
74
+ SELECT o.model,
75
+ o.effective_from,
76
+ o.prompt_tokens_from,
77
+ o.peak_utc,
78
+ o.input_micros_per_mtok,
79
+ o.output_micros_per_mtok,
80
+ o.cached_input_micros_per_mtok,
81
+ o.source,
82
+ o.observed_at AS recorded_at
83
+ FROM price_observations o
84
+ WHERE o.retracted = 0
85
+ AND o.service_tier = ''
86
+ AND o.observed_at = (
87
+ SELECT max(x.observed_at)
88
+ FROM price_observations x
89
+ WHERE x.model = o.model
90
+ AND x.effective_from = o.effective_from
91
+ AND x.prompt_tokens_from = o.prompt_tokens_from
92
+ AND x.peak_utc = o.peak_utc
93
+ AND x.service_tier = ''
94
+ );
95
+
96
+ -- The same, for the batch rate. Same columns, so the resolver reads either.
97
+ CREATE VIEW model_batch_prices AS
98
+ SELECT o.model,
99
+ o.effective_from,
100
+ o.prompt_tokens_from,
101
+ o.peak_utc,
102
+ o.input_micros_per_mtok,
103
+ o.output_micros_per_mtok,
104
+ o.cached_input_micros_per_mtok,
105
+ o.source,
106
+ o.observed_at AS recorded_at
107
+ FROM price_observations o
108
+ WHERE o.retracted = 0
109
+ AND o.service_tier = 'batch'
110
+ AND o.observed_at = (
111
+ SELECT max(x.observed_at)
112
+ FROM price_observations x
113
+ WHERE x.model = o.model
114
+ AND x.effective_from = o.effective_from
115
+ AND x.prompt_tokens_from = o.prompt_tokens_from
116
+ AND x.peak_utc = o.peak_utc
117
+ AND x.service_tier = 'batch'
118
+ );
119
+
120
+ -- ## OpenAI's batch rates
121
+ --
122
+ -- Fetched 2026-10-29 UTC from the host's own pricing page, Batch tab,
123
+ -- https://developers.openai.com/api/docs/pricing — not the board. The Batch
124
+ -- guide (https://developers.openai.com/api/docs/guides/batch) states the
125
+ -- discount as "50% cost discount compared to synchronous APIs" and a 24h
126
+ -- completion window; every figure below is the listed batch rate, and each is
127
+ -- half the standard row this catalogue already holds, which was checked
128
+ -- against the same page's Standard tab on the same date. No standard row
129
+ -- needed correcting.
130
+ --
131
+ -- The long-context tier applies to batch too: >272K input tokens, the same
132
+ -- `prompt_tokens_from` = 272001 boundary 0049 reads.
133
+ --
134
+ -- `effective_from` is each model's own first standard date, because the
135
+ -- batch rate was not introduced after the model — the page lists both, and a
136
+ -- model's batch rate starting later than its standard one would be a claim
137
+ -- nobody observed. It is also what lets a batch resolve on any day the model
138
+ -- has a standard price.
139
+ --
140
+ -- gpt-5.6-sol's standard $4 / $20 is promotional, "available at least through
141
+ -- November 21, 2026" (same page); its batch rate is half the promotional one.
142
+ -- When the promotion ends both tiers move, and both are observed then.
143
+ --
144
+ -- **Not written: embeddings.** text-embedding-3-small and -large are listed
145
+ -- as batch-capable on their model pages, and the pricing page's Batch table
146
+ -- has no row for either. A batch rate that is not stated is not one to infer,
147
+ -- so embeddings are not batchable on Cruise until one is: a model with no
148
+ -- batch row is refused at submit (#716).
149
+ INSERT INTO price_observations
150
+ (observed_at, model, effective_from, prompt_tokens_from, peak_utc, service_tier,
151
+ input_micros_per_mtok, output_micros_per_mtok, cached_input_micros_per_mtok, source, note)
152
+ VALUES
153
+ ('2026-10-29 00:00:00', 'openai/gpt-6-astra', '2026-09-13', 0, '', 'batch',
154
+ 5000000, 25000000, 500000, 'https://developers.openai.com/api/docs/pricing',
155
+ 'First batch observation (#716, 0122).'),
156
+ ('2026-10-29 00:00:00', 'openai/gpt-6-astra', '2026-09-13', 272001, '', 'batch',
157
+ 10000000, 37500000, 1000000, 'https://developers.openai.com/api/docs/pricing',
158
+ 'Batch long-context tier: >272K input tokens.'),
159
+ ('2026-10-29 00:00:00', 'openai/gpt-6.1-sol', '2026-10-02', 0, '', 'batch',
160
+ 1000000, 5000000, 50000, 'https://developers.openai.com/api/docs/pricing',
161
+ 'First batch observation (#716, 0122).'),
162
+ ('2026-10-29 00:00:00', 'openai/gpt-6.1-sol', '2026-10-02', 272001, '', 'batch',
163
+ 2000000, 7500000, 100000, 'https://developers.openai.com/api/docs/pricing',
164
+ 'Batch long-context tier: >272K input tokens.'),
165
+ ('2026-10-29 00:00:00', 'openai/gpt-6-luna', '2026-10-02', 0, '', 'batch',
166
+ 50000, 250000, 5000, 'https://developers.openai.com/api/docs/pricing',
167
+ 'First batch observation (#716, 0122).'),
168
+ ('2026-10-29 00:00:00', 'openai/gpt-6-luna', '2026-10-02', 272001, '', 'batch',
169
+ 100000, 375000, 10000, 'https://developers.openai.com/api/docs/pricing',
170
+ 'Batch long-context tier: >272K input tokens.'),
171
+ ('2026-10-29 00:00:00', 'openai/gpt-5.6-sol', '2026-09-02', 0, '', 'batch',
172
+ 2000000, 10000000, 200000, 'https://developers.openai.com/api/docs/pricing',
173
+ 'First batch observation (#716, 0122). Half the promotional standard rate, promotion at least through 2026-11-21.'),
174
+ ('2026-10-29 00:00:00', 'openai/gpt-5.6-sol', '2026-09-02', 272001, '', 'batch',
175
+ 4000000, 15000000, 400000, 'https://developers.openai.com/api/docs/pricing',
176
+ 'Batch long-context tier: >272K input tokens.'),
177
+ ('2026-10-29 00:00:00', 'openai/gpt-5.6-luna', '2026-09-02', 0, '', 'batch',
178
+ 100000, 600000, 10000, 'https://developers.openai.com/api/docs/pricing',
179
+ 'First batch observation (#716, 0122).'),
180
+ ('2026-10-29 00:00:00', 'openai/gpt-5.6-luna', '2026-09-02', 272001, '', 'batch',
181
+ 200000, 900000, 20000, 'https://developers.openai.com/api/docs/pricing',
182
+ 'Batch long-context tier: >272K input tokens.'),
183
+ ('2026-10-29 00:00:00', 'openai/gpt-5.6-terra', '2026-09-02', 0, '', 'batch',
184
+ 1000000, 6000000, 100000, 'https://developers.openai.com/api/docs/pricing',
185
+ 'First batch observation (#716, 0122).'),
186
+ ('2026-10-29 00:00:00', 'openai/gpt-5.6-terra', '2026-09-02', 272001, '', 'batch',
187
+ 2000000, 9000000, 200000, 'https://developers.openai.com/api/docs/pricing',
188
+ 'Batch long-context tier: >272K input tokens.');
@@ -0,0 +1,50 @@
1
+ -- Batch files, and what a batch job says about itself (#716, epic #712).
2
+ --
3
+ -- OpenAI's batch shape, which `/v1/files` and `/v1/batches` follow so that a
4
+ -- stock SDK works against Cruise unchanged: a project uploads a JSONL file,
5
+ -- then creates a batch naming it. The bytes live in R2 (`BATCH_FILES`); this
6
+ -- table is what Cruise knows about them — whose, how big, and when they go.
7
+ --
8
+ -- **A file belongs to a project**, through the one path to that fact every
9
+ -- other row uses (0004): a key's project, never a column that could disagree
10
+ -- with it. A key from another project asking for a file id is answered 404,
11
+ -- the same as an id that does not exist.
12
+ --
13
+ -- Deleted, not disabled: unlike a lane or a key, a file's bytes are the
14
+ -- payload Cruise promised not to keep (#712, 24h after its job is over), and
15
+ -- the ledger never references a file. `deleted_at` records that the bytes are
16
+ -- gone; the row stays, so a job's `input_file_id` still names something.
17
+ CREATE TABLE batch_files (
18
+ id TEXT PRIMARY KEY,
19
+ project_id TEXT NOT NULL REFERENCES projects(id),
20
+ purpose TEXT NOT NULL CHECK (purpose IN ('batch', 'batch_output')),
21
+ filename TEXT NOT NULL,
22
+ bytes INTEGER NOT NULL CHECK (bytes >= 0),
23
+ -- The R2 object. Its own column rather than derived from the id, so a
24
+ -- change to the key layout is a migration and not a silent miss.
25
+ object_key TEXT NOT NULL,
26
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
27
+ deleted_at TEXT
28
+ );
29
+
30
+ CREATE INDEX idx_batch_files_project ON batch_files (project_id, created_at);
31
+
32
+ -- ## What a batch job carries
33
+ --
34
+ -- 0121 made the job and its money; these are the fields the Batch object
35
+ -- reports back. `model` is the one model every line names — OpenAI requires a
36
+ -- batch's lines to share one, and admission checks it once rather than per
37
+ -- line. `line_count` is fixed at admission. `completed` and `failed` move as
38
+ -- results are settled (#716's Workflow), and `provider_batch_id` is the
39
+ -- provider's own id once the batch has been sent.
40
+ ALTER TABLE jobs ADD COLUMN endpoint TEXT;
41
+ ALTER TABLE jobs ADD COLUMN model TEXT;
42
+ ALTER TABLE jobs ADD COLUMN input_file_id TEXT;
43
+ ALTER TABLE jobs ADD COLUMN output_file_id TEXT;
44
+ ALTER TABLE jobs ADD COLUMN error_file_id TEXT;
45
+ ALTER TABLE jobs ADD COLUMN line_count INTEGER NOT NULL DEFAULT 0;
46
+ ALTER TABLE jobs ADD COLUMN completed_count INTEGER NOT NULL DEFAULT 0;
47
+ ALTER TABLE jobs ADD COLUMN failed_count INTEGER NOT NULL DEFAULT 0;
48
+ ALTER TABLE jobs ADD COLUMN provider_batch_id TEXT;
49
+ -- The caller's `metadata`, as JSON: up to 16 string pairs, as OpenAI caps it.
50
+ ALTER TABLE jobs ADD COLUMN metadata TEXT;
@@ -0,0 +1,21 @@
1
+ -- Running a batch (#716 part 2b, epic #712): what the Workflow needs to keep
2
+ -- on the job between submit and settle.
3
+ --
4
+ -- `markup_basis_points` — the markup the hold was taken at. A line settles at
5
+ -- its batch price plus this rate, never the rate in force when the provider
6
+ -- finishes up to a day later: the hold was sized at the submit-time rate, and
7
+ -- a markup raised overnight would settle a line above the bound that was
8
+ -- reserved for it. The same rule `accounting.ts` keeps for a request, which
9
+ -- carries the rate on its hold for the same reason (#99). 0 for a tenant that
10
+ -- is not billed, and for every job written before this column.
11
+ --
12
+ -- `provider_status` — OpenAI's own word for the batch (`validating`,
13
+ -- `in_progress`, `finalizing`, `completed`, `expired`, `cancelling`,
14
+ -- `cancelled`, `failed`), as last polled. `jobs.status` keeps Cruise's five;
15
+ -- this keeps the two distinctions they fold away and a caller still needs:
16
+ -- an `expired` batch is a `failed` job whose finished lines were billed and
17
+ -- returned, and its event is `batch.expired`, not `batch.failed`; and a
18
+ -- `running` job the caller asked to cancel reads `cancelling` until the
19
+ -- provider confirms it. Null until the batch has been sent.
20
+ ALTER TABLE jobs ADD COLUMN markup_basis_points INTEGER NOT NULL DEFAULT 0 CHECK (markup_basis_points >= 0);
21
+ ALTER TABLE jobs ADD COLUMN provider_status TEXT;
@@ -0,0 +1,22 @@
1
+ -- Anthropic's version segment moves from the call into its upstream row.
2
+ --
3
+ -- `prepareAnthropicStructuredAttempt` asked `providerPath` for `/v1/messages`.
4
+ -- Every other provider-path caller passes a path without a version
5
+ -- (`/embeddings`, `/responses`, `/images/generations`) and lets the upstream
6
+ -- row's `provider_path_prefix` (0063) supply what that provider needs — Google's
7
+ -- `/v1beta/openai`, xAI's `/v1`. Anthropic was the one caller carrying its own.
8
+ --
9
+ -- Through AI Gateway the two spellings are the same URL: the gateway forwards
10
+ -- whatever follows the provider segment, so `…/anthropic` + `/v1/messages` and
11
+ -- `…/anthropic` + prefix `/v1` + `/messages` both reach `api.anthropic.com/v1/messages`.
12
+ -- The difference is a direct upstream, which joins `base_url` and the path with
13
+ -- nothing between them (`providerPath`, `src/route-run.ts`). A self-hosted
14
+ -- deployment with no gateway points Anthropic at `https://api.anthropic.com/v1`,
15
+ -- because that is where its OpenAI-compatible `/chat/completions` lives — and
16
+ -- with the version inside the call, structured output went to `/v1/v1/messages`.
17
+ --
18
+ -- Written only where nothing is set, so an operator who has already chosen a
19
+ -- prefix for this row keeps it.
20
+
21
+ UPDATE upstreams SET provider_path_prefix = '/v1'
22
+ WHERE id = 'anthropic' AND provider_path_prefix IS NULL;
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "@bytesbrains/cruise-worker",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.12.0",
4
+ "description": "The Cruise Worker, built and ready to deploy: one OpenAI-compatible endpoint in front of every model provider, with budgets that stop.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./index.d.ts",
10
+ "default": "./index.mjs"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "index.mjs",
16
+ "index.d.ts",
17
+ "worker",
18
+ "migrations",
19
+ "wrangler.template.jsonc",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "engines": {
24
+ "node": ">=22"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://gitlab.com/bytesbrains-oss/cruise.git",
29
+ "directory": "packages/worker"
30
+ },
31
+ "homepage": "https://gitlab.com/bytesbrains-oss/cruise",
32
+ "keywords": [
33
+ "llm",
34
+ "openai",
35
+ "gateway",
36
+ "cloudflare-workers",
37
+ "budget"
38
+ ]
39
+ }
package/worker/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.12.0+c7b94bc