mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
Adds **Amazon Bedrock** as an opt-in built-in provider, over Bedrock's unified **Converse / ConverseStream** API. One codec serves every Converse-capable family — Claude, Amazon Nova, Meta Llama, Mistral, DeepSeek, Moonshot Kimi, Z.AI GLM, MiniMax, NVIDIA Nemotron, and OpenAI gpt-oss — because AWS translates the envelope to each model's native dialect server-side. Auth is either **AWS SigV4** (the default credential chain — env / profile / IMDS / IRSA / SSO, resolved per request so sessions refresh) or a **Bedrock API key** (`AWS_BEARER_TOKEN_BEDROCK`, bearer). Disabled by default (the Ollama / OpenRouter opt-in pattern). This is the redo of #459's original Claude-only `InvokeModel` adapter, rebuilt on the gateway-refactor seams (#481–#497). @depopry's SigV4 signer, AWS event-stream frame decoder, `BedrockAuth`, the `aws_sigv4` credential grammar, `AdapterKind::Bedrock`, region-from-base_url, and the lean-deps decision are preserved and authored by him on the first two commits; the per-family `BedrockCodec` trait he wrote turned out to be the crate-wide `Codec` seam in miniature, so the refactor promoted exactly that shape. The original Claude-only description is preserved in a comment below. ## What's here - **`AdapterKind::Bedrock` × `CodecKind::BedrockConverse`** on the route, plus the `aws_sigv4` credential source (no static secret — the adapter signs at request time; `fabro-auth` stays AWS-free). *(@depopry)* - **SigV4 signer + AWS event-stream `FrameDecoder`** on the lean AWS stack (no `aws-sdk-bedrockruntime`; transport stays on `fabro-http`). Re-targeted at Converse's direct-JSON stream frames; the signer resolves credentials per request. *(@depopry)* - **`bedrock_converse` codec** — Converse envelope (`system[]`, typed content blocks, `inferenceConfig`, `toolConfig`), prompt caching via `cachePoint`, thinking-signature round-trip through `reasoningContent`, usage mapped onto the disjoint `TokenCounts` buckets, `provider_options.bedrock` passthrough. Plus the adapter shell and an event-stream byte loop beside the transport's shared SSE loop. - **Catalog**: `bedrock.toml` (Claude incl. Fable 5, Nova 2, Llama 4, Mistral, DeepSeek, Kimi, GLM, MiniMax, Nemotron, gpt-oss — cross-region inference-profile ids, per-model `billing_policy` so Claude bills Anthropic-style) and a companion **`bedrock-openai`** provider for GPT-5.5/5.4 over the `bedrock-mantle` Responses endpoint (pure config over the existing `openai_responses` codec, zero new code). - Secrets registry (`AWS_BEARER_TOKEN_BEDROCK`), gitleaks rules for both Bedrock key formats, the `docs/integrations/bedrock` guide, and live e2e tests. ## Live verification (confirmed end-to-end against a real AWS account) Verified on a real Bedrock account (us-east-2, SigV4 + bearer): - **SigV4 + Converse** — multiple families (Claude, Nova, DeepSeek, …) via the full settings → catalog → route → adapter → codec path. - **ConverseStream** — streaming deltas through the workflow engine. - **Multi-turn tool use** — agent loop with tool calls round-tripping (no-arg tools included). - **Multi-model routing** — Claude + DeepSeek pinned in one run through the single Converse codec. - **mantle Responses** — `openai.gpt-5.5` answered via the `bedrock-openai` provider (bearer auth). The exercise caught and fixed several issues that unit tests (static creds, mocked transports) could not — see the follow-up commits below. ## Follow-up fixes from live testing (commits on top of the foundation) 1. **Worker AWS env** — the workflow worker scrubs its env to an allowlist, so SigV4 (which re-resolves from the ambient chain per request) couldn't work through `fabro run`. The AWS credential-chain inputs now cross into the worker. 2. **Vault bearer key** — Bedrock was the only key-based provider missing a `vault:` credential ref, so `fabro secret set AWS_BEARER_TOKEN_BEDROCK` silently didn't feed it. Now resolves env → vault → SigV4. 3. **Converse tool-encoding hardening** — a no-arg tool call's `toolUse.input` is now a `{}` object (Bedrock rejects null), and every tool `inputSchema` gets a top-level `type: "object"` (strict families like DeepSeek reject a typeless schema Claude tolerates). 4. **Nova output cap** — `amazon.nova-2-lite` max_output 65536 → 65535 (Bedrock's per-request limit). Earlier fixes already folded into the foundation commits: the `aws-config` sleep-impl (default chain panicked) and AWS error-body decoding (top-level `message`/`Message`/`__type` → proper messages instead of "Unknown error"). ## Manual testing & setup See `docs/integrations/bedrock` — now documents the non-obvious account setup that live testing surfaced: the per-Region Anthropic use-case approval, `aws-marketplace:Subscribe` for third-party models, the Fable 5 / Mythos-class data-sharing opt-in, and the bearer-vs-SigV4 precedence override for running Converse + mantle side by side. ## Open decision / discussion - **Model-id naming** — Bedrock rows use dotted ids mirroring Bedrock's native inference-profile ids (`us.anthropic.claude-sonnet-4-6`, `openai.gpt-5.5`), which also makes them the wire `api_id`. Third scheme alongside bare ids and OpenRouter's `vendor/model` slashes. No collision risk (enforced at catalog build). Open to a uniform scheme if preferred. - **`BEDROCK_API_KEY` alias** — see the comment thread; the AWS console hands some users `export BEDROCK_API_KEY=` while the SDK-standard var is `AWS_BEARER_TOKEN_BEDROCK`. Question of whether to accept both. ## Deferred (named follow-ups) - **`qwen.qwen3-coder-next`** — omitted pending a verified Bedrock model/inference-profile id (its fabro id isn't a valid Bedrock identifier; needs an explicit `api_id`). Re-add once confirmed via `aws bedrock list-inference-profiles`. - **Claude Mythos 5** — Anthropic-Messages-only on `bedrock-mantle` (limited preview). - **Converse structured output** (`response_format` rejected with a clear error). - **`reasoning_effort` on Converse rows** via `additionalModelRequestFields` (the `bedrock-openai` GPT rows already accept effort levels). - **CountTokens** route (`count_input_tokens` returns `None`). ## Verification `cargo nextest run --workspace`: green except the pre-existing environment-dependent fabro-workflow failures (identical on main). clippy `-D warnings` + pinned-nightly fmt clean. Codec unit tests + adapter httpmock tests + frame-decoder/signer locks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Scott Werner <scott@sublayer.com> Co-authored-by: Scott Werner <stwerner@vt.edu>
458 lines
13 KiB
JSON
458 lines
13 KiB
JSON
{
|
|
"$schema": "https://mintlify.com/docs.json",
|
|
"theme": "almond",
|
|
"name": "Fabro",
|
|
"colors": {
|
|
"primary": "#357F9E",
|
|
"light": "#67B2D7",
|
|
"dark": "#357F9E"
|
|
},
|
|
"favicon": "/favicon.svg",
|
|
"navigation": {
|
|
"tabs": [
|
|
{
|
|
"tab": "Documentation",
|
|
"icon": "book",
|
|
"groups": [
|
|
{
|
|
"group": "Getting Started",
|
|
"icon": "rocket",
|
|
"pages": [
|
|
"getting-started/introduction",
|
|
"getting-started/why-fabro",
|
|
"getting-started/quick-start"
|
|
]
|
|
},
|
|
{
|
|
"group": "Core Concepts",
|
|
"icon": "lightbulb",
|
|
"pages": [
|
|
"core-concepts/how-fabro-works",
|
|
"getting-started/dark-factory",
|
|
"core-concepts/workflows",
|
|
"core-concepts/agents",
|
|
"core-concepts/models"
|
|
]
|
|
},
|
|
{
|
|
"group": "Defining Workflows",
|
|
"icon": "diagram-project",
|
|
"pages": [
|
|
"workflows/stages-and-nodes",
|
|
"workflows/transitions",
|
|
"workflows/variables",
|
|
"workflows/imports",
|
|
"workflows/human-in-the-loop",
|
|
"workflows/stylesheets"
|
|
]
|
|
},
|
|
{
|
|
"group": "Executing Workflows",
|
|
"icon": "play",
|
|
"pages": [
|
|
"execution/run-configuration",
|
|
"execution/child-runs",
|
|
"execution/automations",
|
|
"execution/environments",
|
|
"execution/context",
|
|
"execution/checkpoints",
|
|
"execution/outcomes",
|
|
"execution/failures",
|
|
"execution/observability"
|
|
]
|
|
},
|
|
{
|
|
"group": "Human Tools",
|
|
"icon": "hand",
|
|
"pages": [
|
|
"human-tools/interviews",
|
|
"human-tools/steering",
|
|
"human-tools/ssh-access",
|
|
"human-tools/vs-code",
|
|
"human-tools/preview",
|
|
"human-tools/vnc-access",
|
|
"human-tools/vpn-connections"
|
|
]
|
|
},
|
|
{
|
|
"group": "Agents",
|
|
"icon": "robot",
|
|
"pages": [
|
|
"agents/prompts",
|
|
"agents/skills",
|
|
"agents/tools",
|
|
"agents/mcp",
|
|
"agents/hooks",
|
|
"agents/subagents",
|
|
"agents/permissions",
|
|
"agents/outputs"
|
|
]
|
|
},
|
|
{
|
|
"group": "Integrations",
|
|
"icon": "puzzle-piece",
|
|
"pages": [
|
|
"integrations/github",
|
|
"integrations/daytona",
|
|
"integrations/litellm",
|
|
"integrations/bedrock",
|
|
"integrations/openrouter",
|
|
"integrations/slack",
|
|
"integrations/brave-search"
|
|
]
|
|
},
|
|
{
|
|
"group": "Reference",
|
|
"icon": "book",
|
|
"pages": [
|
|
"reference/dot-language",
|
|
"reference/cli",
|
|
"reference/shell-completions",
|
|
"reference/user-configuration",
|
|
"reference/run-directory",
|
|
"reference/sdk",
|
|
"reference/architecture",
|
|
"reference/server-operations",
|
|
"reference/verifying-releases",
|
|
"administration/server-configuration",
|
|
"administration/troubleshooting",
|
|
"getting-started/comparison",
|
|
"administration/security",
|
|
"reference/acknowledgements"
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"tab": "Guides",
|
|
"icon": "graduation-cap",
|
|
"groups": [
|
|
{
|
|
"group": "Overview",
|
|
"icon": "compass",
|
|
"pages": []
|
|
},
|
|
{
|
|
"group": "Workflow Tutorials",
|
|
"icon": "graduation-cap",
|
|
"pages": [
|
|
"tutorials/hello-world",
|
|
"tutorials/plan-implement",
|
|
"tutorials/branch-loop",
|
|
"tutorials/parallel-review",
|
|
"tutorials/multi-model",
|
|
"tutorials/ensemble",
|
|
"tutorials/sub-workflow"
|
|
]
|
|
},
|
|
{
|
|
"group": "Example Workflows",
|
|
"icon": "flask",
|
|
"pages": [
|
|
"examples/repl-handoff",
|
|
"examples/solitaire",
|
|
"examples/semantic-port",
|
|
"examples/definition-of-done",
|
|
"examples/nlspec-conformance",
|
|
"examples/clone-substack"
|
|
]
|
|
},
|
|
{
|
|
"group": "Deployment",
|
|
"icon": "server",
|
|
"pages": [
|
|
"administration/deployment",
|
|
"administration/self-host-docker",
|
|
"administration/deploy-railway"
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"tab": "API Reference",
|
|
"icon": "code",
|
|
"openapi": "api-reference/fabro-api.yaml",
|
|
"groups": [
|
|
{
|
|
"group": "Using the API",
|
|
"icon": "code",
|
|
"pages": [
|
|
"api-reference/overview",
|
|
"api-reference/client-sdks"
|
|
]
|
|
},
|
|
{
|
|
"group": "Runs",
|
|
"icon": "play",
|
|
"pages": [
|
|
"GET /api/v1/runs",
|
|
"POST /api/v1/runs",
|
|
"GET /api/v1/runs/{id}",
|
|
"PUT /api/v1/runs/{id}/parent",
|
|
"DELETE /api/v1/runs/{id}/parent",
|
|
"POST /api/v1/runs/{id}/start",
|
|
"POST /api/v1/runs/{id}/approve",
|
|
"POST /api/v1/runs/{id}/deny",
|
|
"POST /api/v1/runs/{id}/cancel",
|
|
"POST /api/v1/runs/{id}/pause",
|
|
"POST /api/v1/runs/{id}/unpause",
|
|
"GET /api/v1/runs/{id}/graph",
|
|
"GET /api/v1/runs/{id}/events"
|
|
]
|
|
},
|
|
{
|
|
"group": "Human-in-the-Loop",
|
|
"icon": "hand",
|
|
"pages": [
|
|
"GET /api/v1/runs/{id}/questions",
|
|
"POST /api/v1/runs/{id}/questions/{qid}/answer",
|
|
"POST /api/v1/runs/{id}/preview"
|
|
]
|
|
},
|
|
{
|
|
"group": "Run Outputs",
|
|
"icon": "file-export",
|
|
"pages": [
|
|
"GET /api/v1/runs/{id}/artifacts",
|
|
"GET /api/v1/runs/{id}/billing",
|
|
{
|
|
"group": "Run Internals",
|
|
"icon": "microchip",
|
|
"pages": [
|
|
"GET /api/v1/runs/{id}/checkpoint",
|
|
"GET /api/v1/runs/{id}/stages",
|
|
"GET /api/v1/runs/{id}/stages/{stageId}/events",
|
|
"GET /api/v1/runs/{id}/settings"
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"group": "Automations",
|
|
"icon": "robot",
|
|
"pages": [
|
|
"GET /api/v1/automations",
|
|
"POST /api/v1/automations",
|
|
"GET /api/v1/automations/{id}",
|
|
"PUT /api/v1/automations/{id}",
|
|
"DELETE /api/v1/automations/{id}",
|
|
"GET /api/v1/automations/{id}/runs",
|
|
"POST /api/v1/automations/{id}/runs"
|
|
]
|
|
},
|
|
{
|
|
"group": "Environments",
|
|
"icon": "box",
|
|
"pages": [
|
|
"GET /api/v1/environments",
|
|
"POST /api/v1/environments",
|
|
"GET /api/v1/environments/{id}",
|
|
"PUT /api/v1/environments/{id}",
|
|
"DELETE /api/v1/environments/{id}"
|
|
]
|
|
},
|
|
{
|
|
"group": "Variables",
|
|
"icon": "brackets-curly",
|
|
"pages": [
|
|
"GET /api/v1/variables",
|
|
"POST /api/v1/variables",
|
|
"GET /api/v1/variables/{name}",
|
|
"PUT /api/v1/variables/{name}",
|
|
"DELETE /api/v1/variables/{name}"
|
|
]
|
|
},
|
|
{
|
|
"group": "More",
|
|
"icon": "ellipsis",
|
|
"pages": [
|
|
{
|
|
"group": "Administration",
|
|
"icon": "gear",
|
|
"pages": [
|
|
"GET /api/v1/settings"
|
|
]
|
|
},
|
|
{
|
|
"group": "Models",
|
|
"icon": "microchip",
|
|
"pages": [
|
|
"GET /api/v1/models",
|
|
"POST /api/v1/models/{id}/test"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"tab": "Changelog",
|
|
"icon": "clock-rotate-left",
|
|
"groups": [
|
|
{
|
|
"group": "June 2026",
|
|
"icon": "clock-rotate-left",
|
|
"pages": [
|
|
"changelog/2026-06-10",
|
|
"changelog/2026-06-09",
|
|
"changelog/2026-06-05",
|
|
"changelog/2026-06-03",
|
|
"changelog/2026-06-02"
|
|
]
|
|
},
|
|
{
|
|
"group": "May 2026",
|
|
"icon": "clock-rotate-left",
|
|
"pages": [
|
|
"changelog/2026-05-30",
|
|
"changelog/2026-05-29",
|
|
"changelog/2026-05-28",
|
|
"changelog/2026-05-27",
|
|
"changelog/2026-05-26",
|
|
"changelog/2026-05-25",
|
|
"changelog/2026-05-24",
|
|
"changelog/2026-05-23",
|
|
"changelog/2026-05-22",
|
|
"changelog/2026-05-21",
|
|
"changelog/2026-05-20",
|
|
"changelog/2026-05-18",
|
|
"changelog/2026-05-17",
|
|
"changelog/2026-05-16",
|
|
"changelog/2026-05-14",
|
|
"changelog/2026-05-13",
|
|
"changelog/2026-05-12",
|
|
"changelog/2026-05-11",
|
|
"changelog/2026-05-10",
|
|
"changelog/2026-05-09",
|
|
"changelog/2026-05-08",
|
|
"changelog/2026-05-07",
|
|
"changelog/2026-05-06",
|
|
"changelog/2026-05-05",
|
|
"changelog/2026-05-04",
|
|
"changelog/2026-05-03",
|
|
"changelog/2026-05-02",
|
|
"changelog/2026-05-01"
|
|
]
|
|
},
|
|
{
|
|
"group": "April 2026",
|
|
"icon": "clock-rotate-left",
|
|
"pages": [
|
|
"changelog/2026-04-30",
|
|
"changelog/2026-04-29",
|
|
"changelog/2026-04-28",
|
|
"changelog/2026-04-27",
|
|
"changelog/2026-04-26",
|
|
"changelog/2026-04-25",
|
|
"changelog/2026-04-24",
|
|
"changelog/2026-04-23",
|
|
"changelog/2026-04-22",
|
|
"changelog/2026-04-21",
|
|
"changelog/2026-04-20",
|
|
"changelog/2026-04-19",
|
|
"changelog/2026-04-18",
|
|
"changelog/2026-04-17",
|
|
"changelog/2026-04-16",
|
|
"changelog/2026-04-15",
|
|
"changelog/2026-04-14",
|
|
"changelog/2026-04-13",
|
|
"changelog/2026-04-12",
|
|
"changelog/2026-04-11",
|
|
"changelog/2026-04-10",
|
|
"changelog/2026-04-09",
|
|
"changelog/2026-04-08",
|
|
"changelog/2026-04-07",
|
|
"changelog/2026-04-06",
|
|
"changelog/2026-04-04",
|
|
"changelog/2026-04-02",
|
|
"changelog/2026-04-01"
|
|
]
|
|
},
|
|
{
|
|
"group": "March 2026",
|
|
"icon": "clock-rotate-left",
|
|
"pages": [
|
|
"changelog/2026-03-31",
|
|
"changelog/2026-03-30",
|
|
"changelog/2026-03-29",
|
|
"changelog/2026-03-28",
|
|
"changelog/2026-03-27",
|
|
"changelog/2026-03-26",
|
|
"changelog/2026-03-23",
|
|
"changelog/2026-03-22",
|
|
"changelog/2026-03-21",
|
|
"changelog/2026-03-20",
|
|
"changelog/2026-03-19",
|
|
"changelog/2026-03-18",
|
|
"changelog/2026-03-17",
|
|
"changelog/2026-03-16",
|
|
"changelog/2026-03-15",
|
|
"changelog/2026-03-14",
|
|
"changelog/2026-03-13",
|
|
"changelog/2026-03-12",
|
|
"changelog/2026-03-11",
|
|
"changelog/2026-03-10",
|
|
"changelog/2026-03-09",
|
|
"changelog/2026-03-08",
|
|
"changelog/2026-03-07",
|
|
"changelog/2026-03-06",
|
|
"changelog/2026-03-05",
|
|
"changelog/2026-03-04",
|
|
"changelog/2026-03-03",
|
|
"changelog/2026-03-02",
|
|
"changelog/2026-03-01"
|
|
]
|
|
},
|
|
{
|
|
"group": "February 2026",
|
|
"icon": "clock-rotate-left",
|
|
"pages": [
|
|
"changelog/2026-02-28",
|
|
"changelog/2026-02-27",
|
|
"changelog/2026-02-26",
|
|
"changelog/2026-02-25",
|
|
"changelog/2026-02-24",
|
|
"changelog/2026-02-23"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"logo": {
|
|
"light": "/logo/light.svg",
|
|
"dark": "/logo/dark.svg"
|
|
},
|
|
"navbar": {
|
|
"primary": {
|
|
"type": "button",
|
|
"label": "Get Started",
|
|
"href": "/getting-started/introduction"
|
|
}
|
|
},
|
|
"contextual": {
|
|
"options": [
|
|
"copy",
|
|
"view",
|
|
"chatgpt",
|
|
"claude",
|
|
"perplexity",
|
|
"mcp",
|
|
"cursor",
|
|
"vscode"
|
|
]
|
|
},
|
|
"styling": {
|
|
"codeblocks": {
|
|
"languages": {
|
|
"custom": ["/languages/dot.json", "/languages/fabro.json"]
|
|
}
|
|
}
|
|
},
|
|
"footer": {
|
|
"socials": {
|
|
"x": "https://x.com/fabro_sh",
|
|
"github": "https://github.com/fabro-sh"
|
|
}
|
|
}
|
|
}
|