Skip to content

Add Modal as an LLM provider - #673

Merged
brynary merged 2 commits into
mainfrom
feat/modal-llm-provider
Jul 28, 2026
Merged

Add Modal as an LLM provider#673
brynary merged 2 commits into
mainfrom
feat/modal-llm-provider

Conversation

@brynary

@brynary brynary commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

  • add an opt-in Modal provider for Kimi K3 using the existing OpenAI-compatible adapter and Kimi agent profile
  • authenticate with the Modal-Key and Modal-Secret proxy-token headers while keeping the endpoint base URL in user settings
  • add catalog, credential-resolution, request-routing, and optional live coverage
  • document server vault setup, direct SDK environment setup, pricing, troubleshooting, and the provider icon

Validation

  • cargo nextest run -p fabro-model -p fabro-auth -p fabro-llm (888 passed, 24 skipped)
  • cargo build --workspace
  • cargo +nightly-2026-04-14 fmt --check --all
  • cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings
  • cd apps/fabro-web && bun run typecheck
  • cd apps/fabro-web && bun run build
  • cargo nextest run -p fabro-llm --profile e2e --run-ignored only modal_kimi_k3_deep_tool_round_trip against the live Modal endpoint (1 passed)

Notes

fabro provider login --provider modal remains out of scope because the current login flow accepts one credential value. Modal setup uses two fabro secret set commands instead.

Copilot AI review requested due to automatic review settings July 28, 2026 20:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in Modal LLM provider to Fabro’s model catalog, routing Kimi K3 via the existing OpenAI-compatible adapter while authenticating with Modal proxy-token headers. This fits into the broader provider/catalog + credential-resolution architecture by extending the built-in catalog, auth resolution, test coverage, and end-user documentation.

Changes:

  • Introduces a disabled-by-default modal provider with Kimi K3 metadata and Modal-Key / Modal-Secret header wiring.
  • Extends catalog + auth resolution test coverage to ensure Modal is opt-in and that both proxy-token values are required/resolved correctly.
  • Adds live and hermetic request-routing coverage plus end-user docs (server vault setup, SDK env overrides, troubleshooting) and the provider icon.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated no comments.

Show a summary per file
File Description
lib/foundation/fabro-model/src/catalog/providers/modal.toml Adds the built-in (disabled) Modal provider definition, headers, and Kimi K3 model metadata/costing.
lib/foundation/fabro-model/src/catalog.rs Adds catalog tests validating Modal is opt-in and that Kimi K3 is exposed when enabled.
lib/foundation/fabro-auth/src/resolve.rs Adds vault credential-resolution tests ensuring both proxy-token secrets are required and resolved into headers (no Authorization).
lib/foundation/fabro-auth/src/env_source.rs Adds env-source tests showing Modal can be resolved via env only when extra headers are explicitly configured to env.*.
lib/components/fabro-llm/tests/integration.rs Adds a live Modal deep tool round-trip E2E test gated on Modal env vars.
lib/components/fabro-llm/src/client.rs Adds a hermetic routing test verifying proxy headers are forwarded and Authorization is omitted.
docs/public/reference/sdk.mdx Documents Modal env variables and how to override header sources for direct EnvCredentialSource usage.
docs/public/integrations/modal.mdx Adds a dedicated Modal integration guide (endpoint setup, vault secrets, costs, troubleshooting).
docs/public/docs.json Adds the Modal integration page to docs navigation.
docs/public/core-concepts/models.mdx Adds Modal to the provider enablement documentation and vault secret requirements.
docs/public/administration/server-configuration.mdx Documents Modal vault variables and header resolution behavior.
apps/fabro-web/public/images/providers/modal.svg Adds the Modal provider icon asset.
.env.example Adds env var placeholders used by the Modal live integration test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Reduce duplication and over-specification introduced with the Modal
provider, without changing shipped behavior.

- Extract enabled_provider_catalog and assert_deep_tool_round_trip in
  the fabro-llm integration tests. The Poolside, Fireworks, OpenRouter,
  and Modal deep round trips were four near-identical copies.
- Add ApiCredential::with_extra_headers for providers that authenticate
  with request headers instead of an API key.
- Replace the unreachable require_env guards in the Modal e2e test with
  the std::env::var form used by every sibling test, and register
  MODAL_TOKEN_ID and MODAL_TOKEN_SECRET in EnvVars.
- Collapse modal_requires_both_vault_proxy_tokens to a single case. The
  loop rebuilt the whole built-in catalog per iteration.
- Drop tautological and over-specified assertions: the api_key_url doc
  URL, the forced default/probe lookups on a single-model provider, and
  the get_on_provider loop that could not fail.
- Inline the single-use modal_env_catalog fixture and note why it
  overrides the shipped secrets templates.
- Sort the MODAL_* keys in .env.example, and record in modal.toml why
  api_id keeps the Hugging Face capitalization.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 22:06
@brynary

brynary commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Ran a simplification pass over the branch (reuse, quality, and efficiency review). Pushed 8e4251b — tests and fixtures only, no change to shipped behavior. Net −65 lines.

Removed duplication

  • The Poolside, Fireworks, OpenRouter, and Modal deep round trips were four near-identical ~30-line bodies. Extracted enabled_provider_catalog and assert_deep_tool_round_trip; each test is now ~6 lines.
  • Added ApiCredential::with_extra_headers for providers that authenticate with request headers instead of an API key. Replaces two 7-field struct literals that set two fields.

Fixed dead and duplicated test code

  • The three require_env guards in the Modal e2e test were unreachable — #[e2e_test(live(...))] already emits a guard per declared var before the body runs. Switched to the std::env::var(...).expect(...) form used by every sibling, and registered MODAL_TOKEN_ID / MODAL_TOKEN_SECRET in EnvVars instead of raw strings.
  • modal_requires_both_vault_proxy_tokens rebuilt the full built-in catalog on each of its two iterations. Collapsed to a single case; the symmetric half duplicated missing_secret_header_fails_without_echoing_value.
  • Dropped assertions that cannot fail or pin nothing: the exact api_key_url doc URL, default_for_provider/probe_for_provider on a provider with one model marked default = true, and the get_on_provider loop that re-asserted the key it looked up by. Renamed that test to builtin_kimi_k3_selection_prefers_direct_kimi_then_modal_over_openrouter — the two select assertions are what it actually proves.
  • Inlined the single-use modal_env_catalog fixture and noted why it overrides the shipped {{ secrets.* }} templates.

Small things

  • Sorted the MODAL_* keys in .env.example (they were inserted before MINIMAX_API_KEY).
  • Recorded in modal.toml why api_id keeps the Hugging Face capitalization while OpenRouter uses a lowercase slug, and pointed the setup comment at ~/.fabro/settings.toml to match openrouter.toml.

Verified: cargo nextest run --workspace (7468 passed), nightly clippy -D warnings across the workspace, and cargo fmt --check.

Left alone — flagging for your call, not defects I want to fix silently:

  1. Cost data. Review flagged that per-token costs describe the Shared API while a dedicated Auto Endpoint bills by GPU-second, and suggested billing_policy = "none". The docs page already draws exactly this distinction, so this reads as deliberate. Leaving it.
  2. enabled = true with no base_url. Modal is the only openai_compatible provider shipping without a base URL, so enabling it alone builds a catalog where Kimi K3 is listed and selectable but Client::from_credentials fails later in adapter_registry.rs. Failing at catalog build with the settings key named would be a better error, but that is a behavior change beyond a cleanup.
  3. estimated_output_tps = 460. Deployment-specific, and Modal would be the only Kimi K3 row in fabro model list showing a speed — the Kimi and OpenRouter rows omit it, so Modal will look categorically faster rather than merely measured. Worth a source comment or dropping.
  4. ApiCredential derives Debug and extra_headers prints raw. ApiKeyHeader hand-writes a redacting Debug, so the API-key channel is protected and the header channel is not. Pre-existing (Portkey uses extra headers too), but Modal is the first shipped provider whose entire credential lives only in extra_headers.
  5. fabro provider login --provider modal builds an ApiKeyStrategy with an empty env-var list and stores a key nothing reads, since Modal has no auth block. The docs note the command is unsupported; the CLI does not.

@brynary
brynary marked this pull request as ready for review July 28, 2026 22:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

lib/foundation/fabro-static/src/env_vars.rs:60

  • MODAL_TOKEN_ID / MODAL_TOKEN_SECRET were added to EnvVars, but they are not registered as optional vault secrets in fabro-static/src/secret_registry.rs (OPTIONAL_VAULT_SECRETS). That means the server.env→vault migration (2026052501_optional_server_env_secrets_to_vault.rs) will never migrate these secrets if an operator has configured them in server.env, and the registry helpers will classify them as unknown.
    pub const MODAL_TOKEN_ID: &'static str = "MODAL_TOKEN_ID";
    pub const MODAL_TOKEN_SECRET: &'static str = "MODAL_TOKEN_SECRET";

@brynary
brynary merged commit b611d94 into main Jul 28, 2026
18 checks passed
@brynary
brynary deleted the feat/modal-llm-provider branch July 28, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants