Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5faf086
Ratify the AI provider contract and its plan
softmarshmallow Oct 13, 2026
8242da7
Split the adapter contract out of the providers spec
softmarshmallow Oct 13, 2026
dfef285
Let the engine check each request against its route and capability
softmarshmallow Oct 13, 2026
ef791ad
Fill each route's adapter identity, and let a route extend another
softmarshmallow Oct 14, 2026
e0cdfaf
Keep the provider-contract work local until the owner asks for the PR
softmarshmallow Oct 14, 2026
ecc99ab
Let a route without a known price give a per-call cap
softmarshmallow Oct 14, 2026
a907e49
Pass parameters FX does not expose through provider_options
softmarshmallow Oct 14, 2026
00e9ef8
Run any model on GG, OpenRouter and fal from a route alone
softmarshmallow Oct 14, 2026
53c326c
Check the built-in routes against Grida Models, and send transparency…
softmarshmallow Oct 14, 2026
f66881f
Let a project declare the providers it uses
softmarshmallow Oct 14, 2026
89bf05d
Serve a declared OpenAI-compatible provider's chat routes
softmarshmallow Oct 14, 2026
3745ebd
Let a project write its own provider adapter in Python or TypeScript
softmarshmallow Oct 15, 2026
bd83b19
Document community providers end to end, and mark AR-20 current
softmarshmallow Oct 15, 2026
12a526f
Bump the version to 0.7.0
softmarshmallow Oct 15, 2026
11922fd
Link the provider contract's follow-up issues from its plan
softmarshmallow Oct 15, 2026
410aa43
Pin Grida Models v1.20261014.1
softmarshmallow Oct 15, 2026
d4c0296
Wrap make_models.py's Gemini Omni card line for ruff
softmarshmallow Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ jobs:
- uses: astral-sh/setup-uv@v10.2.0
- run: uv sync --locked --project python
- run: uv run --locked --project python python tools/check_spec.py
- run: uv run --locked --project python python tools/make_models.py --check

# Every conformance case, through the command line of a fresh grida-fx, with the repository's
# Python SDK and its built @grida/fx hosting the node bodies. --strict: a case without reviewed
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Guardrails for working in this repository. [README.md](README.md) says what FX i
| `fixtures/viewer/` | Canonical provider-free development harness for viewer topology, media, cache, takes, failures and recorded in-progress states; generated data stays outside the source fixture. |
| `fixtures/host/grida/` | A stand-in for the npm `grida` command reduced to its `fx` verb; `tools/check_host.py` installs it beside packed `@grida/fx` and runs conformance through `grida fx`. |
| `fixtures/control/` | Provider-free checkpoint and delayed-cleanup harness for cancellation acceptance, waiting, verified cleanup and resume. Copy it into a fresh project for each test. |
| `tools/` | What runs outside the engine: the spec gate, the independent digest checker, packaging, `build_engine.py` (the engine into a checkout's SDK) and `check_examples.py`. |
| `tools/` | What runs outside the engine: the spec gate, the independent digest checker, packaging, `build_engine.py` (the engine into a checkout's SDK), `check_examples.py` and `make_models.py` (the pinned Grida Models facts the built-in routes are checked against). |

## Rules

Expand Down Expand Up @@ -74,6 +74,7 @@ cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test --w
(cd python && uv run ruff check --config pyproject.toml . ../tools ../conformance ../examples ../fixtures && uv run ruff format --check --config pyproject.toml . ../tools ../conformance/run.py ../examples ../fixtures && uv run pytest)
(cd js/fx && bun --no-env-file run typecheck && bun --no-env-file test)
uv run --project python python tools/check_spec.py
uv run --project python python tools/make_models.py --check
uv run --project python python conformance/run.py --command target/debug/grida-fx --js-sdk js/fx
python3 tools/build_engine.py && uv run --project python python tools/check_examples.py
uv run --project python python tools/check_llms.py
Expand Down
23 changes: 16 additions & 7 deletions AGENT_READINESS.md

Large diffs are not rendered by default.

24 changes: 18 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ plans, records and exports are byte-identical under each (the invoker, `GRIDA_FX
unsupported platform `grida fx` explains this and exits 1; the rest of `grida` is unaffected.
- **Your own engine.** `GRIDA_FX_BIN` points every launcher and both SDKs at a `grida-fx` binary,
such as a source build or one built for another platform.
- **Python node bodies.** Python nodes, builders and the local built-ins (`image.resize`,
`files.copy`, `package`, …) need the Python package even when FX runs through npm, and in a
TypeScript project too; `GRIDA_FX_PYTHON` selects the interpreter.
- **TypeScript node bodies.** TypeScript (and JavaScript) nodes, builders and stand-in files run
in `@grida/fx`'s node host, `<node> <@grida/fx>/dist/host-entry.js`, on Node.js 24 or later
(type stripping and `module.registerHooks`; Bun and Deno cannot host). `<node>` is
- **Python node bodies.** Python nodes, builders, adapters and the local built-ins
(`image.resize`, `files.copy`, `package`, …) need the Python package even when FX runs through
npm, and in a TypeScript project too; `GRIDA_FX_PYTHON` selects the interpreter.
- **TypeScript node bodies.** TypeScript (and JavaScript) nodes, builders, stand-in files and
adapters run in `@grida/fx`'s node host, `<node> <@grida/fx>/dist/host-entry.js`, on Node.js
24 or later (type stripping and `module.registerHooks`; Bun and Deno cannot host). `<node>` is
`GRIDA_FX_NODE`, else `GRIDA_FX_SDK_NODE`, else `node` on `PATH`. `dist/host-entry.js` comes
from the `@grida/fx` the workflow's project resolves (`node_modules/@grida/fx`, in the project
or a folder above it), else from the one the planning project resolves, else it is
Expand Down Expand Up @@ -147,6 +147,7 @@ bun --no-env-file run build:viewer

# Contracts and runnable examples
uv run --project python python tools/check_spec.py
uv run --project python python tools/make_models.py --check
cargo build --locked -p grida-fx
uv run --project python python conformance/run.py --command target/debug/grida-fx --js-sdk js/fx
uv run --project python python tools/check_examples.py
Expand All @@ -160,6 +161,17 @@ helper defaults to the release profile. Use the Python and JavaScript checks in
AGENTS.md when changing either SDK. Changes to public schemas, identities, or
protocol behavior update their specification and conformance evidence together.

The built-in route table is checked against a pinned release of
[Grida Models](https://github.com/gridaco/models) (spec/providers.md §3), whose
projection is `crates/grida-fx-providers/routes/models.json`. A new built-in route
gets its card in `CARDS` of `tools/make_models.py` and a new projection: run
`uv run --project python python tools/make_models.py --release <tag>` with the
network, where `<tag>` is the tool's `RELEASE`, the pin. To move the pin, change
`RELEASE` and the release spec/providers.md §3 names (`--check` holds them
together), run the same command with the new tag, and fix what `cargo test -p
grida-fx-providers --test models` then finds, in the routes or in its exceptions
table.

## Viewer development

`web/viewer` builds the client embedded in the engine. Shared browser contracts
Expand Down
13 changes: 7 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "3"

[workspace.package]
version = "0.6.1"
version = "0.7.0"
edition = "2024"
rust-version = "1.89"
repository = "https://github.com/gridaco/fx"
Expand Down Expand Up @@ -35,7 +35,7 @@ chrono = { version = "0.4", default-features = false, features = ["clock", "std"
base64 = "0.22"
getrandom = "0.3"
unicode-general-category = "1.1"
# rustls trusting the bundled roots and the platform's certificate store (spec/providers.md §2).
# rustls trusting the bundled roots and the platform's certificate store (spec/adapters.md §2).
reqwest = { version = "0.12", default-features = false, features = [
"rustls-tls-webpki-roots",
"rustls-tls-native-roots",
Expand Down
10 changes: 5 additions & 5 deletions ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ Known problems that are not fixed yet. Each says what was seen, what is known, a

**Status:** open. A design gap, not a bug.

An answer holds only its files and its `data`: "no request id, no usage, no provider URL" ([providers.md](spec/providers.md) §4.4, and the §11 change notes). That rule is right for the answer, since a call record is replayed to node bodies and must not depend on which provider made it. But nothing else keeps the provider's usage either: not the call record, and not the run's events. FX reads the cost it needs and drops the rest.
An answer holds only its files and its `data`: "no request id, no usage, no provider URL" ([adapters.md](spec/adapters.md) §3.4). That rule is right for the answer, since a call record is replayed to node bodies and must not depend on which provider made it. But nothing else keeps the provider's usage either: not the call record, and not the run's events. FX reads the cost it needs and drops the rest.

So a bill cannot be explained after the fact:

- OpenRouter's `usage` splits a bill into image-output tokens, reasoning tokens, prompt tokens, and the upstream provider's own charge. FX keeps only the total.
- OpenAI reports token usage and no cost, so FX books an OpenAI image at its whole hold and keeps none of the tokens. Nothing then shows whether the hold covered the call.
- No request or generation id is kept, so a call cannot be looked up at the provider afterwards.

The per-size image prices of [providers.md](spec/providers.md) §10 could only be measured from an earlier engine's records, which kept the usage block. Issue 2 is open because FX's own run kept nothing.
The per-size image prices of [providers.md](spec/providers.md) §3 could only be measured from an earlier engine's records, which kept the usage block. Issue 2 is open because FX's own run kept nothing.

**Proposal.** Keep answers as they are. Record what the provider reported on the run's `call` event: the token counts and the cost breakdown, as allowlisted numbers, and the provider's request id when it is a safe id ([providers.md](spec/providers.md) §8). None of it is a secret, none of it enters a call key, and a replay would carry no usage, since nothing was billed. Spec: [protocol.md](spec/protocol.md) (the `call` event), [providers.md](spec/providers.md) §4.4 and each provider's section of §9, and the events schema.
**Proposal.** Keep answers as they are. Record what the provider reported on the run's `call` event: the token counts and the cost breakdown, as allowlisted numbers, and the provider's request id when it is a safe id ([adapters.md](spec/adapters.md) §7). None of it is a secret, none of it enters a call key, and a replay would carry no usage, since nothing was billed. Spec: [protocol.md](spec/protocol.md) (the `call` event), [adapters.md](spec/adapters.md) §3.4 and each adapter's reference ([providers.md](spec/providers.md) §2), and the events schema.

## 2. OpenRouter billed twice the image output for one call

Expand All @@ -39,7 +39,7 @@ The per-size image prices of [providers.md](spec/providers.md) §10 could only b

**Open candidates:**

1. **`moderation: low`.** FX always passes it to OpenAI through `provider.options` ([providers.md](spec/providers.md) §9.2). The earlier calls that billed the single count did not send it. It is the only request difference found, and no mechanism is known for it to double the output.
1. **`moderation: low`.** FX always passes it to OpenAI through `provider.options` ([OpenRouter's adapter reference](crates/grida-fx-providers/src/openrouter/README.md)). The earlier calls that billed the single count did not send it. It is the only request difference found, and no mechanism is known for it to double the output.
2. **An unannounced change since late September**, at OpenRouter (counting the output twice) or at OpenAI (the model drawing twice as much at `max`).

**Seen since.** On 2026-10-12, a 1024x1024 `max` generate (background `opaque`) on
Expand All @@ -54,4 +54,4 @@ does not send `moderation: low`; it does not say which upstream GG used.
- **Two 1024x1024 `max` calls on OpenRouter**, one with `moderation: low` and one without (about $0.85), separate candidate 1 from candidate 2.
- **Once issue 1 is fixed**, any new call shows its breakdown in the run.

**What depends on it.** FX's OpenRouter image highs cover twice the output as a precaution. They are only holds, since OpenRouter reports its cost. If OpenAI itself now bills twice, the OpenAI and fal highs under-count by up to one output: a 1024x1024 generate would cost about $0.42 and be booked at $0.29 ([providers.md](spec/providers.md) §10, "An open risk").
**What depends on it.** FX's OpenRouter image highs cover twice the output as a precaution. They are only holds, since OpenRouter reports its cost. If OpenAI itself now bills twice, the OpenAI and fal highs under-count by up to one output: a 1024x1024 generate would cost about $0.42 and be booked at $0.29 ([providers.md](spec/providers.md) §3, "An open risk").
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ A workflow engine for images, audio, video, 3D and the code around them.

A build system for generated assets.

- **Priced before it spends.** The whole run is priced up front, and a live run never crosses
its ceiling.
- **Priced before it spends.** The whole run is priced up front, and a live run reserves each call
against its ceiling before making it.
- **Never pays twice.** Results and paid calls are cached by content, and an interrupted run picks
up where it stopped.
- **Judged, not just generated.** Steps can judge each other's output, draw again when rejected,
Expand Down Expand Up @@ -42,7 +42,10 @@ step, and mix them freely:
| **Provider keys** | `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, `FAL_KEY`, `TRIPO_API_KEY`, `ELEVENLABS_API_KEY` | each provider's models, on its own account |
| **Grida Gateway** | `GG_API_KEY` | text, images, video and 3D with one key and one bill |

[AI providers](docs/guide/13-providers.md) says what each one serves and where to get each key.
[AI providers](docs/guide/13-providers.md) says what each one serves and where to get each key. A
model FX does not list runs from a route of your own; a provider FX does not ship, from a
declaration in `fx.yaml` and, unless it speaks OpenAI's chat API, an adapter of your own in Python
or TypeScript ([Community providers](docs/guide/15-community-providers.md)).

## Plan. Run. Inspect.

Expand Down
15 changes: 8 additions & 7 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,9 @@ implementation; installation alone does not authorize invocation.
## Codex-backed image generation for local workflows

**Status: in review, 2026-10-12** ([gridaco/fx#46](https://github.com/gridaco/fx/pull/46)), as
the route `imagegen@codex` of `image.generate`
([spec/providers.md §9.6](spec/providers.md#96-codex)). Users signed into Codex with ChatGPT pick
it in `fx.yaml` and run with no provider key. `codex` bills the user's plan, not dollars: a live run whose
the route `imagegen@codex` of `image.generate` ([the Codex adapter's
reference](crates/grida-fx-providers/src/codex/README.md)). Users signed into Codex with ChatGPT
pick it in `fx.yaml` and run with no provider key. `codex` bills the user's plan, not dollars: a live run whose
calls are all on it needs no `--max-usd`, runs under a ceiling of $0 and refuses any other
provider. Codex is a *runtime* the engine owns: one `codex app-server` per invocation, started
on the first Codex call, shared by the rest (a thread each) and shut down with the run; its
Expand Down Expand Up @@ -431,7 +431,7 @@ Follow-ups:

**Grok Build: in review, 2026-10-13** ([gridaco/fx#72](https://github.com/gridaco/fx/issues/72)),
as the route `imagine@grok` of `image.generate` and `image.edit`
([spec/providers.md §9.8](spec/providers.md#98-grok-build)): Grok Build's own ACP server
([the Grok Build adapter's reference](crates/grida-fx-providers/src/grok/README.md)): Grok Build's own ACP server
(`grok agent --no-leader stdio`) through FX's ACP client ([`acp`](crates/grida-fx-providers/src/acp/mod.rs)),
a session per call whose agent offers only Grok Build's two image tools, billed to the user's
subscription (`Billing::Plan`). Verified with fake Grok Build programs in tests and real runs on
Expand All @@ -442,7 +442,8 @@ Grok Build 1.0.52. Follow-ups:
client is provider-neutral. Still per runtime: the JSON-RPC connection (Codex's app-server
client and [`acp`](crates/grida-fx-providers/src/acp/mod.rs) differ only in routing keys and
request answers), the start-once runtime state, the version check (gridaco/fx#73 for Codex),
and spec/providers.md §9.6 and §9.8, which restate the same rules; share them spec first.
and the Codex and Grok Build adapters' references, which restate the same rules; share them spec
first.
- **An aspect ratio.** Grok Build's tools take one (`1:1`, `16:9`, `9:16`, …); FX leaves it unset
until `image.generate` has a shape setting (gridaco/fx#57).
- **Retrying a failed image tool.** A passing network error between Grok Build and xAI fails the
Expand All @@ -461,8 +462,8 @@ gridaco/fx#72.

## Grida Gateway follow-ups

**Status: in review, 2026-10-12.** Grida Gateway (`@gg`, [spec/providers.md
§9.7](spec/providers.md#97-grida-gateway)) serves text, agent turns, images, video, 3D models and
**Status: in review, 2026-10-12.** Grida Gateway (`@gg`, [its adapter
reference](crates/grida-fx-providers/src/gg/README.md)) serves text, agent turns, images, video, 3D models and
rigs with one key, `GG_API_KEY`, and is what the docs recommend first. FX books a GG call at
GG's settled charge, read from the response or its billing readback; one that has not settled
within a short wait is booked at its whole hold.
Expand Down
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading