mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
80 lines
No EOL
16 KiB
JSON
80 lines
No EOL
16 KiB
JSON
{
|
|
"timestamp": "2026-03-20T00:47:31.624031Z",
|
|
"current_node": "preflight_lint",
|
|
"completed_nodes": [
|
|
"start",
|
|
"toolchain",
|
|
"preflight_compile",
|
|
"preflight_lint"
|
|
],
|
|
"node_retries": {
|
|
"start": 1,
|
|
"toolchain": 1,
|
|
"preflight_compile": 1,
|
|
"preflight_lint": 1
|
|
},
|
|
"context_values": {
|
|
"outcome": "success",
|
|
"thread.preflight_compile.current_node": "preflight_lint",
|
|
"command.output": "",
|
|
"internal.run_id": "01KM4B94S7CD09R6CZSH9KWW5H",
|
|
"graph.goal": "# Extract `fabro-model` crate from `fabro-llm`\n\n## Context\n\nThe model catalog (provider identity, model metadata, alias resolution, fallback chains) is currently embedded inside `fabro-llm`, a heavyweight crate that pulls in tokio, reqwest, and many async runtime dependencies. Seven crates depend on `fabro-llm`, but several only need catalog lookups — not the LLM client. Extracting a focused `fabro-model` crate gives a clean dependency boundary: crates that only need \"what models exist?\" no longer pull in the entire LLM runtime.\n\n## Public API of `fabro-model`\n\nAll items re-exported at the crate root for flat access (`fabro_model::get_model_info()`):\n\n```rust\n// Types\npub use types::{ModelInfo, ModelLimits, ModelFeatures, ModelCosts};\n\n// Provider identity\npub use provider::{Provider, ModelId};\n\n// Catalog lookups\npub use catalog::{\n get_model_info, list_models, default_model, default_model_for_provider,\n default_model_from_env, probe_model_for_provider, closest_model,\n build_fallback_chain, FallbackTarget,\n};\n```\n\nNo `Catalog` struct — the catalog is static embedded data with no configuration or lifecycle. Free functions are the right abstraction. The crate name itself is the namespace.\n\n## Key design decisions\n\n1. **No re-export shim in `fabro-llm`** — update all consumers directly. Exception: `fabro-llm` re-exports `Provider` and `ModelId` so `fabro_llm::Provider` stays valid (it's a type alias, not a shim module).\n2. **Provider moves entirely** — `Provider` enum, `ModelId`, and all `Provider` methods (ALL, as_str, from_str, api_key_env_vars, has_api_key, default_from_env). Only `ProviderAdapter` trait, `validate_tool_choice()`, and `StreamEventStream` stay in `fabro-llm::provider`.\n3. **`fabro-validate` drops `fabro-llm`** — it only uses catalog + Provider, so it can depend solely on `fabro-model`.\n\n## Steps\n\n### 1. Create `lib/crates/fabro-model/` crate\n\n**`Cargo.toml`**:\n```toml\n[package]\nname = \"fabro-model\"\nedition.workspace = true\nversion.workspace = true\nlicense.workspace = true\ndescription = \"LLM model catalog: provider identity, model metadata, and resolution\"\n\n[lib]\ndoctest = false\n\n[dependencies]\nserde.workspace = true\nserde_json.workspace = true\n\n[dev-dependencies]\ninsta.workspace = true\n```\n\n### 2. Move model types → `fabro-model/src/types.rs`\n\nExtract from `fabro-llm/src/types.rs` (lines 639-675):\n- `ModelInfo`, `ModelLimits`, `ModelFeatures`, `ModelCosts`\n\nRemove these 4 structs from `fabro-llm/src/types.rs`.\n\n### 3. Move Provider + ModelId → `fabro-model/src/provider.rs`\n\nExtract from `fabro-llm/src/provider.rs`:\n- `Provider` enum + all impl blocks (lines 14-95)\n- `Display`, `FromStr` impls (lines 97-118)\n- `ModelId` struct + impls (lines 126-146)\n- All tests for these items (lines 208-350)\n\nWhat stays in `fabro-llm/src/provider.rs`:\n- `ProviderAdapter` trait (lines 157-181)\n- `StreamEventStream` type alias (line 153)\n- `validate_tool_choice()` (lines 192-206)\n- Tests for ProviderAdapter/validate_tool_choice (lines 351-418)\n- Add `use fabro_model::Provider;` import at top\n\n### 4. Move catalog → `fabro-model/src/catalog.rs` + `catalog.json`\n\nMove both files verbatim. Internal `crate::` paths remain valid since Provider and ModelInfo are in the same crate now.\n\n### 5. Write `fabro-model/src/lib.rs`\n\n```rust\npub mod catalog;\npub mod provider;\npub mod types;\n\npub use catalog::{\n build_fallback_chain, closest_model, default_model, default_model_for_provider,\n default_model_from_env, get_model_info, list_models, probe_model_for_provider,\n FallbackTarget,\n};\npub use provider::{ModelId, Provider};\npub use types::{ModelCosts, ModelFeatures, ModelInfo, ModelLimits};\n```\n\n### 6. Update `fabro-llm`\n\n- Add `fabro-model = { path = \"../fabro-model\" }` to `Cargo.toml`\n- Remove `pub mod catalog;` from `lib.rs`\n- Change `pub use provider::{ModelId, Provider};` → `pub use fabro_model::{ModelId, Provider};`\n- `cli.rs`: change `use crate::catalog` → `use fabro_model as catalog`, split `use crate::types::{Message, ModelInfo}` so `ModelInfo` comes from `fabro_model`\n- `client.rs`: change `crate::catalog::get_model_info` → `fabro_model::get_model_info`\n- `providers/anthropic.rs`: change `crate::catalog::get_model_info` → `fabro_model::get_model_info`\n- Any other internal `crate::catalog` or `crate::types::ModelInfo` references\n\n### 7. Update consumer crates\n\n| Crate | Add dep | Import changes | Drop `fabro-llm`? |\n|---|---|---|---|\n| **fabro-validate** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*`, `fabro_llm::Provider` → `fabro_model::Provider` | **Yes** |\n| **fabro-cli** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*` | No |\n| **fabro-api** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*` | No |\n| **fabro-workflows** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*`, `FallbackTarget` | No |\n| **fabro-agent** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*` | No |\n| **fabro-hooks** | `fabro-model` | `fabro_llm::catalog::get_model_info` → `fabro_model::get_model_info` | No |\n\n## Files to modify\n\n- **Create**: `lib/crates/fabro-model/Cargo.toml`, `src/lib.rs`, `src/types.rs`, `src/provider.rs`\n- **Move**: `lib/crates/fabro-llm/src/catalog.rs` → `lib/crates/fabro-model/src/catalog.rs`\n- **Move**: `lib/crates/fabro-llm/src/catalog.json` → `lib/crates/fabro-model/src/catalog.json`\n- **Edit**: `lib/crates/fabro-llm/src/lib.rs`, `types.rs`, `provider.rs`, `cli.rs`, `client.rs`, `providers/anthropic.rs`, `Cargo.toml`\n- **Edit**: `lib/crates/fabro-validate/Cargo.toml`, `src/rules.rs`\n- **Edit**: `lib/crates/fabro-cli/Cargo.toml` + source files with `fabro_llm::catalog` imports\n- **Edit**: `lib/crates/fabro-api/Cargo.toml` + source files\n- **Edit**: `lib/crates/fabro-workflows/Cargo.toml` + source files\n- **Edit**: `lib/crates/fabro-agent/Cargo.toml` + source files\n- **Edit**: `lib/crates/fabro-hooks/Cargo.toml` + source files\n\n## Verification\n\n1. `cargo build --workspace` — compiles cleanly\n2. `cargo test -p fabro-model` — all catalog tests pass (snapshot tests included)\n3. `cargo test --workspace` — no regressions\n4. `cargo clippy --workspace -- -D warnings` — no lint warnings\n5. `cargo fmt --check --all` — formatted\n6. Verify `fabro-validate` no longer depends on `fabro-llm`: `cargo tree -p fabro-validate | grep fabro-llm` should return nothing\n",
|
|
"thread.start.current_node": "toolchain",
|
|
"internal.retry_count.toolchain": 1,
|
|
"internal.retry_count.preflight_compile": 1,
|
|
"graph.model_stylesheet": "\n * { backend: api; model: claude-opus-4-6;}\n ",
|
|
"current.preamble": "Goal: # Extract `fabro-model` crate from `fabro-llm`\n\n## Context\n\nThe model catalog (provider identity, model metadata, alias resolution, fallback chains) is currently embedded inside `fabro-llm`, a heavyweight crate that pulls in tokio, reqwest, and many async runtime dependencies. Seven crates depend on `fabro-llm`, but several only need catalog lookups — not the LLM client. Extracting a focused `fabro-model` crate gives a clean dependency boundary: crates that only need \"what models exist?\" no longer pull in the entire LLM runtime.\n\n## Public API of `fabro-model`\n\nAll items re-exported at the crate root for flat access (`fabro_model::get_model_info()`):\n\n```rust\n// Types\npub use types::{ModelInfo, ModelLimits, ModelFeatures, ModelCosts};\n\n// Provider identity\npub use provider::{Provider, ModelId};\n\n// Catalog lookups\npub use catalog::{\n get_model_info, list_models, default_model, default_model_for_provider,\n default_model_from_env, probe_model_for_provider, closest_model,\n build_fallback_chain, FallbackTarget,\n};\n```\n\nNo `Catalog` struct — the catalog is static embedded data with no configuration or lifecycle. Free functions are the right abstraction. The crate name itself is the namespace.\n\n## Key design decisions\n\n1. **No re-export shim in `fabro-llm`** — update all consumers directly. Exception: `fabro-llm` re-exports `Provider` and `ModelId` so `fabro_llm::Provider` stays valid (it's a type alias, not a shim module).\n2. **Provider moves entirely** — `Provider` enum, `ModelId`, and all `Provider` methods (ALL, as_str, from_str, api_key_env_vars, has_api_key, default_from_env). Only `ProviderAdapter` trait, `validate_tool_choice()`, and `StreamEventStream` stay in `fabro-llm::provider`.\n3. **`fabro-validate` drops `fabro-llm`** — it only uses catalog + Provider, so it can depend solely on `fabro-model`.\n\n## Steps\n\n### 1. Create `lib/crates/fabro-model/` crate\n\n**`Cargo.toml`**:\n```toml\n[package]\nname = \"fabro-model\"\nedition.workspace = true\nversion.workspace = true\nlicense.workspace = true\ndescription = \"LLM model catalog: provider identity, model metadata, and resolution\"\n\n[lib]\ndoctest = false\n\n[dependencies]\nserde.workspace = true\nserde_json.workspace = true\n\n[dev-dependencies]\ninsta.workspace = true\n```\n\n### 2. Move model types → `fabro-model/src/types.rs`\n\nExtract from `fabro-llm/src/types.rs` (lines 639-675):\n- `ModelInfo`, `ModelLimits`, `ModelFeatures`, `ModelCosts`\n\nRemove these 4 structs from `fabro-llm/src/types.rs`.\n\n### 3. Move Provider + ModelId → `fabro-model/src/provider.rs`\n\nExtract from `fabro-llm/src/provider.rs`:\n- `Provider` enum + all impl blocks (lines 14-95)\n- `Display`, `FromStr` impls (lines 97-118)\n- `ModelId` struct + impls (lines 126-146)\n- All tests for these items (lines 208-350)\n\nWhat stays in `fabro-llm/src/provider.rs`:\n- `ProviderAdapter` trait (lines 157-181)\n- `StreamEventStream` type alias (line 153)\n- `validate_tool_choice()` (lines 192-206)\n- Tests for ProviderAdapter/validate_tool_choice (lines 351-418)\n- Add `use fabro_model::Provider;` import at top\n\n### 4. Move catalog → `fabro-model/src/catalog.rs` + `catalog.json`\n\nMove both files verbatim. Internal `crate::` paths remain valid since Provider and ModelInfo are in the same crate now.\n\n### 5. Write `fabro-model/src/lib.rs`\n\n```rust\npub mod catalog;\npub mod provider;\npub mod types;\n\npub use catalog::{\n build_fallback_chain, closest_model, default_model, default_model_for_provider,\n default_model_from_env, get_model_info, list_models, probe_model_for_provider,\n FallbackTarget,\n};\npub use provider::{ModelId, Provider};\npub use types::{ModelCosts, ModelFeatures, ModelInfo, ModelLimits};\n```\n\n### 6. Update `fabro-llm`\n\n- Add `fabro-model = { path = \"../fabro-model\" }` to `Cargo.toml`\n- Remove `pub mod catalog;` from `lib.rs`\n- Change `pub use provider::{ModelId, Provider};` → `pub use fabro_model::{ModelId, Provider};`\n- `cli.rs`: change `use crate::catalog` → `use fabro_model as catalog`, split `use crate::types::{Message, ModelInfo}` so `ModelInfo` comes from `fabro_model`\n- `client.rs`: change `crate::catalog::get_model_info` → `fabro_model::get_model_info`\n- `providers/anthropic.rs`: change `crate::catalog::get_model_info` → `fabro_model::get_model_info`\n- Any other internal `crate::catalog` or `crate::types::ModelInfo` references\n\n### 7. Update consumer crates\n\n| Crate | Add dep | Import changes | Drop `fabro-llm`? |\n|---|---|---|---|\n| **fabro-validate** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*`, `fabro_llm::Provider` → `fabro_model::Provider` | **Yes** |\n| **fabro-cli** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*` | No |\n| **fabro-api** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*` | No |\n| **fabro-workflows** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*`, `FallbackTarget` | No |\n| **fabro-agent** | `fabro-model` | `fabro_llm::catalog::*` → `fabro_model::*` | No |\n| **fabro-hooks** | `fabro-model` | `fabro_llm::catalog::get_model_info` → `fabro_model::get_model_info` | No |\n\n## Files to modify\n\n- **Create**: `lib/crates/fabro-model/Cargo.toml`, `src/lib.rs`, `src/types.rs`, `src/provider.rs`\n- **Move**: `lib/crates/fabro-llm/src/catalog.rs` → `lib/crates/fabro-model/src/catalog.rs`\n- **Move**: `lib/crates/fabro-llm/src/catalog.json` → `lib/crates/fabro-model/src/catalog.json`\n- **Edit**: `lib/crates/fabro-llm/src/lib.rs`, `types.rs`, `provider.rs`, `cli.rs`, `client.rs`, `providers/anthropic.rs`, `Cargo.toml`\n- **Edit**: `lib/crates/fabro-validate/Cargo.toml`, `src/rules.rs`\n- **Edit**: `lib/crates/fabro-cli/Cargo.toml` + source files with `fabro_llm::catalog` imports\n- **Edit**: `lib/crates/fabro-api/Cargo.toml` + source files\n- **Edit**: `lib/crates/fabro-workflows/Cargo.toml` + source files\n- **Edit**: `lib/crates/fabro-agent/Cargo.toml` + source files\n- **Edit**: `lib/crates/fabro-hooks/Cargo.toml` + source files\n\n## Verification\n\n1. `cargo build --workspace` — compiles cleanly\n2. `cargo test -p fabro-model` — all catalog tests pass (snapshot tests included)\n3. `cargo test --workspace` — no regressions\n4. `cargo clippy --workspace -- -D warnings` — no lint warnings\n5. `cargo fmt --check --all` — formatted\n6. Verify `fabro-validate` no longer depends on `fabro-llm`: `cargo tree -p fabro-validate | grep fabro-llm` should return nothing\n\n\n## Completed stages\n- **toolchain**: success\n - Script: `command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1`\n - Stdout:\n ```\n cargo 1.94.0 (85eff7c80 2026-01-15)\n ```\n - Stderr: (empty)\n- **preflight_compile**: success\n - Script: `cargo check -q --workspace 2>&1`\n - Stdout: (empty)\n - Stderr: (empty)\n",
|
|
"failure_class": "",
|
|
"failure_signature": "",
|
|
"internal.node_visit_count": 1,
|
|
"current_node": "preflight_lint",
|
|
"graph.rankdir": "LR",
|
|
"internal.fidelity": "compact",
|
|
"internal.thread_id": "preflight_compile",
|
|
"internal.retry_count.start": 1,
|
|
"command.stderr": "",
|
|
"thread.toolchain.current_node": "preflight_compile",
|
|
"internal.retry_count.preflight_lint": 1
|
|
},
|
|
"logs": [],
|
|
"node_outcomes": {
|
|
"preflight_lint": {
|
|
"status": "success",
|
|
"context_updates": {
|
|
"command.output": "",
|
|
"command.stderr": ""
|
|
},
|
|
"notes": "Script completed: cargo clippy -q --workspace -- -D warnings 2>&1",
|
|
"duration_ms": 13320
|
|
},
|
|
"toolchain": {
|
|
"status": "success",
|
|
"context_updates": {
|
|
"command.stderr": "",
|
|
"command.output": "cargo 1.94.0 (85eff7c80 2026-01-15)\n"
|
|
},
|
|
"notes": "Script completed: command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1",
|
|
"duration_ms": 354
|
|
},
|
|
"preflight_compile": {
|
|
"status": "success",
|
|
"context_updates": {
|
|
"command.output": "",
|
|
"command.stderr": ""
|
|
},
|
|
"notes": "Script completed: cargo check -q --workspace 2>&1",
|
|
"duration_ms": 76890
|
|
},
|
|
"start": {
|
|
"status": "success",
|
|
"duration_ms": 0
|
|
}
|
|
},
|
|
"next_node_id": "implement",
|
|
"node_visits": {
|
|
"preflight_lint": 1,
|
|
"preflight_compile": 1,
|
|
"toolchain": 1,
|
|
"start": 1
|
|
}
|
|
} |