Skip to content

Commit 7129840

Browse files
Harden config provider routing
1 parent 299bc8c commit 7129840

6 files changed

Lines changed: 912 additions & 31 deletions

File tree

mythic_vibe_cli/ai/router.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ def from_dict(cls, payload: dict[str, Any]) -> "RoutingRule":
146146
return cls(
147147
role=str(payload.get("role", "*")),
148148
task_type=str(payload.get("task_type", "*")),
149-
min_ram_mb=int(payload.get("min_ram_mb", 0) or 0),
150-
min_logical_cpus=int(payload.get("min_logical_cpus", 0) or 0),
149+
min_ram_mb=_safe_int(payload.get("min_ram_mb", 0), 0),
150+
min_logical_cpus=_safe_int(payload.get("min_logical_cpus", 0), 0),
151151
prefer_local=bool(payload.get("prefer_local", False)),
152152
provider=str(payload.get("provider", "copy-paste")),
153153
model=str(payload.get("model", "")),
@@ -296,7 +296,10 @@ def load(cls, root: Path) -> "RoutingTable":
296296

297297
loaded = ConfigStore(root).load()
298298
for entry in loaded.config.ai_routing_rules:
299-
config_overrides.append(RoutingRule.from_dict(entry))
299+
try:
300+
config_overrides.append(RoutingRule.from_dict(entry))
301+
except (TypeError, ValueError):
302+
continue
300303
except Exception: # noqa: BLE001 - routing should degrade to defaults
301304
config_overrides = []
302305

@@ -312,7 +315,10 @@ def load(cls, root: Path) -> "RoutingTable":
312315
overrides: list[RoutingRule] = []
313316
for entry in payload:
314317
if isinstance(entry, dict):
315-
overrides.append(RoutingRule.from_dict(entry))
318+
try:
319+
overrides.append(RoutingRule.from_dict(entry))
320+
except (TypeError, ValueError):
321+
continue
316322
# Overrides come first so a user rule shadows a default with
317323
# the same predicates.
318324
return cls(rules=config_overrides + overrides + table.rules)
@@ -373,3 +379,10 @@ def route(
373379
"RoutingTable",
374380
"route",
375381
]
382+
383+
384+
def _safe_int(raw: object, fallback: int) -> int:
385+
try:
386+
return max(0, int(raw or fallback))
387+
except (TypeError, ValueError):
388+
return fallback

mythic_vibe_cli/commands.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,12 @@ def cmd_ai_run(args: argparse.Namespace) -> int:
12321232
projected = float(getattr(estimate, "cost_usd", 0.0) or 0.0)
12331233
except Exception: # noqa: BLE001 — estimator failure shouldn't block the call
12341234
projected = 0.0
1235-
budget = check_budget(root, projected)
1235+
cap_override = (
1236+
loaded_config.config.ai_daily_cost_cap_usd
1237+
if loaded_config.config.ai_daily_cost_cap_usd > 0.0
1238+
else None
1239+
)
1240+
budget = check_budget(root, projected, cap_usd_override=cap_override)
12361241
if not budget.allowed:
12371242
write_error(f"Daily AI cost cap blocked the call: {budget.reason}")
12381243
return OPERATIONAL_FAILURE
@@ -2721,6 +2726,8 @@ def cmd_config(args: argparse.Namespace) -> int:
27212726
"ai.context_window_tokens": loaded.config.ai_context_window_tokens,
27222727
"ai.max_output_tokens": loaded.config.ai_max_output_tokens,
27232728
"ai.request_timeout_seconds": loaded.config.ai_request_timeout_seconds,
2729+
"ai.retry_attempts": loaded.config.ai_retry_attempts,
2730+
"ai.daily_cost_cap_usd": loaded.config.ai_daily_cost_cap_usd,
27242731
"ai.temperature": loaded.config.ai_temperature,
27252732
"ai.top_p": loaded.config.ai_top_p,
27262733
"ai.router": loaded.config.ai_router,
@@ -2730,6 +2737,8 @@ def cmd_config(args: argparse.Namespace) -> int:
27302737
"prompts": loaded.config.ai_prompts,
27312738
"knowledge.sources": loaded.config.knowledge_sources,
27322739
},
2740+
"diagnostics": [item.to_dict() for item in loaded.diagnostics],
2741+
"ok": loaded.ok,
27332742
}
27342743
)
27352744
return SUCCESS
@@ -2753,13 +2762,22 @@ def cmd_config(args: argparse.Namespace) -> int:
27532762
write_key_value("ai.context_window_tokens", loaded.config.ai_context_window_tokens, indent=2)
27542763
write_key_value("ai.max_output_tokens", loaded.config.ai_max_output_tokens, indent=2)
27552764
write_key_value("ai.request_timeout_seconds", loaded.config.ai_request_timeout_seconds, indent=2)
2765+
write_key_value("ai.retry_attempts", loaded.config.ai_retry_attempts, indent=2)
2766+
write_key_value("ai.daily_cost_cap_usd", loaded.config.ai_daily_cost_cap_usd, indent=2)
27562767
write_key_value("ai.temperature", loaded.config.ai_temperature, indent=2)
27572768
write_key_value("ai.top_p", loaded.config.ai_top_p, indent=2)
27582769
write_key_value("ai.services", len(loaded.config.ai_services), indent=2)
27592770
write_key_value("ai.model_routes", len(loaded.config.ai_model_routes), indent=2)
27602771
write_key_value("ai.routing_rules", len(loaded.config.ai_routing_rules), indent=2)
27612772
write_key_value("prompts", len(loaded.config.ai_prompts), indent=2)
27622773
write_key_value("knowledge.sources", len(loaded.config.knowledge_sources), indent=2)
2774+
if loaded.diagnostics:
2775+
write_line("- Diagnostics:")
2776+
for item in loaded.diagnostics:
2777+
write_bullet(
2778+
f"{item.severity}: {item.code} at {item.path}: {item.message}",
2779+
indent=2,
2780+
)
27632781
return SUCCESS
27642782

27652783

0 commit comments

Comments
 (0)