Commit graph

2165 commits

Author SHA1 Message Date
Bryan Helmkamp
e5f0290229
fix(llm): send cache_control breakpoints for Claude via OpenRouter
Anthropic prompt caching is opt-in per request: without explicit
ephemeral cache_control breakpoints in the body, no cache writes or
reads ever happen. The OpenAI-compatible codec never emitted them, so
every run on openrouter Claude models billed the full conversation at
the uncached input rate on every turn (0 cache tokens on the billing
page, confirmed by OpenRouter's activity portal).

- Add a `cache_control_breakpoints` model feature declaring that a
  route only caches when the request marks the cacheable prefix; set it
  on the builtin OpenRouter Claude rows. Catalog build rejects the flag
  without `prompt_cache`.
- Teach the Chat Completions wire shape a parts-form content variant so
  a message can carry the annotation; unmarked messages keep the
  plain-string form for compatibility with strict servers.
- Mark the last system message (covers tools + system upstream) and the
  second-to-last user turn, counting tool results as user turns —
  mirroring the anthropic codec's placement so agent loops get
  incremental cache hits.
- Extract the shared placement/opt-out policy into codec::cache and
  refactor the anthropic codec onto it; anthropic wire snapshots are
  unchanged.
- Honor `provider_options.<name>.auto_cache = false` as an opt-out and
  consume the control key instead of merging it into the body.
- Mirror the new feature through settings (fabro-config), the OpenAPI
  schema, and the generated TypeScript client.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:55:41 -04:00
Bryan Helmkamp
b558af070f
Merge pull request #601 from fabro-sh/fabro/run/01KY7Y01REECZ24XXTMBZ3PPV9
Provider-scoped model catalog: `(provider, model slug)` as stable ident…
2026-07-23 17:14:51 -04:00
Bryan Helmkamp
b33a38e459
Merge pull request #600 from fabro-sh/fix/last-file-routing-fallback
fix(workflow): constrain last-file routing fallback
2026-07-23 17:13:50 -04:00
Fabro
000085cb52 Merge remote-tracking branch 'origin/main' into fabro/run/01KY7Y01REECZ24XXTMBZ3PPV9 2026-07-23 20:51:45 +00:00
Bryan Helmkamp
e736786bf2
fix(test): eliminate install and session-root races 2026-07-23 16:46:40 -04:00
Fabro
b3e085dc45 Merge remote-tracking branch 'origin/main' into fabro/run/01KY7Y01REECZ24XXTMBZ3PPV9 2026-07-23 20:29:21 +00:00
Fabro
9b85653a2d fabro(01KY7Y01REECZ24XXTMBZ3PPV9): simplify_sol (succeeded)
Fabro-Run: 01KY7Y01REECZ24XXTMBZ3PPV9
Fabro-Completed: 7
Fabro-Checkpoint: 334aea8fdc

⚒️ Generated with [Fabro](https://fabro.sh)
2026-07-23 20:29:19 +00:00
Bryan Helmkamp
c9b5303128
fix(workflow): flush agent events before stage completion 2026-07-23 15:52:25 -04:00
Bryan Helmkamp
886371e474
refactor(workflow): clean up last-file routing fallback review nits
Document the contract of read_last_file_routing_json (terminal JSON
extraction only; routing validation happens downstream), extract a
shared sandbox_with_file test helper, and drop the misleading
"standalone" wording from the fallback docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:47:17 -04:00
Bryan Helmkamp
02437fb18a
fix(workflow): constrain last-file routing fallback 2026-07-23 15:28:19 -04:00
Fabro
56119990be fabro(01KY7Y01REECZ24XXTMBZ3PPV9): simplify_fable (succeeded)
Fabro-Run: 01KY7Y01REECZ24XXTMBZ3PPV9
Fabro-Completed: 6
Fabro-Checkpoint: 497f92f4d9

⚒️ Generated with [Fabro](https://fabro.sh)
2026-07-23 19:10:39 +00:00
Bryan Helmkamp
e75c3dcd37
fix(test): replay SSE events before asserting stage types 2026-07-23 14:58:01 -04:00
Bryan Helmkamp
187e10879a
fix(server): make generated title updates atomic 2026-07-23 14:18:04 -04:00
Bryan Helmkamp
21e84484d2
Merge pull request #598 from fabro-sh/run-session-trace-header
feat(llm): send x-session-id trace header with the run ID
2026-07-23 13:54:26 -04:00
Fabro
1a2bd7966d fabro(01KY7Y01REECZ24XXTMBZ3PPV9): implement (succeeded)
Fabro-Run: 01KY7Y01REECZ24XXTMBZ3PPV9
Fabro-Completed: 5
Fabro-Checkpoint: 1f2ff54692

⚒️ Generated with [Fabro](https://fabro.sh)
2026-07-23 17:41:29 +00:00
Bryan Helmkamp
0432c019ee
chore: address review feedback on error mapping and test-support gating
- Return 400 (not 500) for WorkflowError::ModelReference from run
  creation, matching ModelSelection: an ambiguous model/provider token
  is user input, not a server fault.
- Gate fabro-workflow's test_support module behind
  cfg(any(test, feature = "test-support")) so the feature actually
  controls exposure, per the repo's test-support boundary guidance.
  Add the self dev-dependency so tests/it keeps compiling, and gate
  the pipeline helpers that only test_support consumed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:38:55 -04:00
Release Repro
e8db4e1a9b
refactor(llm): simplify session trace header plumbing 2026-07-23 13:38:18 -04:00
Release Repro
1448d996e2
feat(llm): send x-session-id trace header with the run ID
Tag every LLM request in a run with an x-session-id header carrying the
run ID, so gateways that understand session tracing (e.g. OpenRouter
broadcast) can group a run's requests into one session.

Adds ExtraHeadersCredentialSource to fabro-auth: a CredentialSource
decorator that appends fixed headers to every resolved credential,
leaving operator-configured extra_headers untouched. The run pipeline
wraps its vault/env source with it, so agent stages, prompt stages,
hooks, and PR-content generation all pick up the header through the
existing extra_headers plumbing with no fabro-llm changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:26:20 -04:00
Bryan Helmkamp
712b7702a2
fix: simplify provider-pin routing after concurrent fixes merged
Both this branch and the remote qa branch fixed the same provider-pin
regression; the merge stacked the two implementations. Keep the remote's
semantics: pin the run's provider whenever it offers the model, otherwise
fall back to priority selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:16:09 -04:00
Bryan Helmkamp
05acb11ee6
Merge remote-tracking branch 'origin/provider-aware-model-aliases' into provider-aware-model-aliases 2026-07-23 13:14:22 -04:00
Bryan Helmkamp
96102854d8
chore: address review feedback
- rename resolve_route catalog-instance test to describe its actual
  id-based resolution assertion
- use EnvVars::OPENAI_API_KEY instead of a raw string in the automation
  scheduler test fixture

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:13:45 -04:00
Bryan Helmkamp
831b750664
fix: honor the run's provider pin during request-time routing
Merging main brought in billing tests that construct ModelRef with String
model ids and an integration test that pins an OpenRouter run via the
backend's provider id. The ModelRef sites now use ModelId conversions.

The integration test also exposed a real regression: resolve_provider_context
ignored the persisted run provider whenever the model selector resolved
globally, re-routing pinned OpenRouter runs to a higher-priority provider for
nodes without explicit model/provider attrs. Request-time routing now treats
the run's selected provider as a pin with custom-model passthrough, matching
transform-time selection semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:13:45 -04:00
Bryan Helmkamp
4f697c527c
fix: preserve provider pins during model routing 2026-07-23 13:11:49 -04:00
Bryan Helmkamp
32d1b1b339
Merge remote-tracking branch 'origin/main' into provider-aware-model-aliases 2026-07-23 12:50:22 -04:00
Bryan Helmkamp
770d393a0c
Merge remote-tracking branch 'origin/main' into qa/pr-597 2026-07-23 12:43:06 -04:00
Bryan Helmkamp
1874497056
Merge pull request #596 from fabro-sh/test-strip-force-color
fix(test): strip inherited color-forcing env vars in brew upgrade tests
2026-07-23 12:16:01 -04:00
Bryan Helmkamp
d213e23dc6
Merge pull request #595 from fabro-sh/validate-inert-attributes
feat(validate): warn on inert node and edge attributes
2026-07-23 12:12:26 -04:00
Bryan Helmkamp
14317ccaf7
fix: preserve legacy model selector compatibility 2026-07-23 12:12:09 -04:00
Bryan Helmkamp
f02ee6aaed
fix(validate): handle custom types and inherited threads 2026-07-23 11:19:44 -04:00
Bryan Helmkamp
edbd25f67c
fix(test): strip inherited color-forcing env vars in brew upgrade tests
The brew_command helper inherits the developer's environment (unlike
context.command(), which env_clears), so an exported FORCE_COLOR or
CLICOLOR_FORCE overrides the NO_COLOR=1 the harness sets and the CLI
renders ANSI codes into snapshot output, failing
upgrade_brew_install_refuses_and_prints_brew_command and
upgrade_brew_install_rejects_version_flag on any machine with
FORCE_COLOR exported.

Remove FORCE_COLOR, CLICOLOR_FORCE, and CLICOLOR from the spawned
command's env, and add the FORCE_COLOR constant to EnvVars.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 11:19:01 -04:00
Bryan Helmkamp
05e6507095
refactor: unify model selection and legacy-model normalization
Consolidate duplicated resolution logic introduced by the provider-aware
model alias work:

- Add Catalog::resolve_selection (with SelectedModel and ready_provider)
  as the single home for the select -> passthrough -> default-fallback
  policy, replacing six hand-rolled copies across fabro-server,
  fabro-workflow, and fabro-llm.
- Move legacy [models] row resolution into a shared LegacyModelIndex and
  LegacyModelError in fabro-model; delete fabro-config's parallel
  implementation and its LlmNormalizationError enum, plus the now-unused
  builtin_* catalog helpers.
- Drop redundant client.resolve_request calls (and their full-request
  clones) from the completions and playground handlers.
- Remove the redundant resolve_provider_context round-trip in
  resolve_start_llm and make resolve_run_model return a ProviderId
  instead of a never-None Option.
- Replace the "<default model>" sentinel selector with a dedicated
  ModelSelectionError::NoDefaultModel variant.
- Add a CatalogRoute trait so provider adapters call
  self.api_model_id(...) instead of threading catalog/provider args.
- Delete the unused FromStr impl for ModelId; dedupe the CLI's
  id-or-alias predicate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 11:07:47 -04:00
Bryan Helmkamp
3af4335098
fix(validate): name every parallel parent in branch diagnostics
A branch node reachable from multiple parallel nodes previously had its
warning and fix hint name an arbitrary first parent. Collect all unique
parallel parents (sorted) and render the full list in both.

Addresses review feedback on #595.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 11:04:14 -04:00
Bryan Helmkamp
0748d0c6b8
feat(validate): warn on inert node and edge attributes
Add two lint rules so the graph format stops silently accepting
attributes that nothing reads:

- inert_attribute: handler-specific attributes (script, language,
  duration, join_policy, max_parallel, output_schema, prompt) placed on
  node types that never read them. Attributes read by several handlers
  (timeout), resolved for every node (fidelity, retry_policy), or
  injectable via model stylesheets (model, reasoning_effort, ...) are
  deliberately excluded.
- parallel_branch_inert_attribute: fidelity/thread_id on parallel
  branch nodes and fork->branch edges. Branch dispatch bypasses the
  fidelity lifecycle, so these are dead letters today; the warning
  points at the parallel node, where fidelity does take effect.

Also reconcile the loop_restart docs with actual executor behavior:
taking a loop_restart edge restarts from the target with a fresh empty
context (visit counts preserved), on success as well as failure; the
transient_infra guard applies only to failure crossings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:48:18 -04:00
Bryan Helmkamp
65cdf52061
feat: make model aliases provider-aware 2026-07-23 10:12:25 -04:00
Bryan Helmkamp
7b52b3b6ba
Merge pull request #584 from andremw/fix/parallel-branch-stage-stuck-running
Finalize parallel branch stages instead of leaving them Running
2026-07-23 09:45:49 -04:00
Bryan Helmkamp
580ee156b2
Merge pull request #594 from fabro-sh/preserve-provider-costs
Preserve provider-reported workflow costs
2026-07-23 08:10:18 -04:00
Bryan Helmkamp
08cef66ec8
Saturate billing cost accumulation 2026-07-23 07:53:01 -04:00
Bryan Helmkamp
5b0cfd4050
Tighten parallel branch projection tests
Assert ParallelBranchStarted seeds started_at (the live-timer half of
the fix) and cover the failed-status fold to a Failed terminal state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 07:37:34 -04:00
Bryan Helmkamp
4d2712b8db
Consolidate provider-reported cost handling into fabro-model
Replace the billed_model_usage_from_llm_with_cost wrapper with a
with_reported_cost method on BilledModelUsage and BilledTokenCounts, and
centralize the optional-cost fold as UsdMicros::accumulate so fabro-agent
and fabro-workflow share one implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 07:21:17 -04:00
Bryan Helmkamp
7f25689fb6
Preserve provider-reported workflow costs 2026-07-23 07:08:04 -04:00
Bryan Helmkamp
f8ed856959
Add regression test for authoritative provider billing 2026-07-23 06:59:48 -04:00
Bryan Helmkamp
e6c92aeb27
Add current GPT and Claude models to OpenRouter catalog 2026-07-23 06:50:26 -04:00
Bryan Helmkamp
b39e3767a3
Fix legacy token billing projection 2026-07-22 21:37:13 -04:00
Bryan Helmkamp
d629816c3b
Merge pull request #589 from fabro-sh/codex/glm-5-2-support
Add GLM 5.2 direct and OpenRouter support
2026-07-22 19:47:21 -04:00
Bryan Helmkamp
3fd4b44d6e
Rename reasoning stream snapshot 2026-07-22 19:29:16 -04:00
Bryan Helmkamp
911f26f8e5
Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Bryan Helmkamp <19+brynary@users.noreply.github.com>
2026-07-22 19:14:54 -04:00
Bryan Helmkamp
8f47275d84
feat(llm): add Poolside Laguna models
Add Poolside as a built-in OpenAI-compatible provider and expose Laguna S 2.1 and XS 2.1 both directly and through OpenRouter. Include vault/env credential registration, secret redaction, live coverage, catalog tests, and user documentation.
2026-07-22 18:49:16 -04:00
Bryan Helmkamp
8a243085d0
Merge remote-tracking branch 'origin/main' into codex/glm-5-2-support
# Conflicts:
#	lib/crates/fabro-llm/tests/integration.rs
#	lib/crates/fabro-model/src/catalog.rs
2026-07-22 18:37:15 -04:00
Bryan Helmkamp
04f55666f9
Merge pull request #588 from fabro-sh/codex/openrouter-kimi-k3
Add Kimi K3 via OpenRouter
2026-07-22 18:29:47 -04:00
Bryan Helmkamp
1511c84f48
Test GLM 5.2 through Z.AI 2026-07-22 17:26:37 -04:00