Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# fusionAIze Gate Changelog

## v1.13.0 - Unreleased

### Added

- Expanded the provider source catalog scope beyond `blackbox`, `kilo`, and `openai` so Gate can also track mirrored official source data for `anthropic`, `deepseek`, and `google`
- Added local models-endpoint overlays per configured route, which lets Gate compare what a specific key can really see against the mirrored global provider catalog

### Changed

- Provider source alerts now distinguish more clearly between global catalog drift and key-specific route/model visibility drift
- Catalog summaries now include local route counts, local visible model counts, and route-vs-catalog mismatch hints instead of only source freshness and change counts

## v1.12.0 - 2026-03-29

### Added
Expand Down
4 changes: 4 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,10 @@ provider_source_refresh:
timeout_seconds: 10.0
interval_seconds: 21600
providers:
- anthropic
- blackbox
- deepseek
- google
- kilo
- openai
```
Expand All @@ -393,4 +396,5 @@ Notes:
- startup refresh is best-effort and should not block the service if docs are unavailable
- `interval_seconds` controls the conservative background refresh loop after startup
- source snapshots live in the same local DB as metrics
- for providers with a usable local `models` endpoint, Gate also mirrors key-specific model visibility per configured route and compares that against the global source snapshot
- local billing overlays such as subscription or quota windows belong in the local account profile layer, not in the global provider snapshot
26 changes: 14 additions & 12 deletions docs/FAIGATE-ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,31 @@ The detailed design lives in [Adaptive model orchestration](./ADAPTIVE-ORCHESTRA

The next block should stay disciplined: build on the workstation baseline, keep packaging practical, and avoid turning fusionAIze Gate into a sprawling platform.

## Current release target: `v1.12.0`
## Current release target: `v1.13.0`

The next release should land as a clean operational release, not as another loose pile of runtime slices.
`v1.12.0` closed the first operator-facing catalog and release-hardening loop. The
next release should make that catalog meaningfully more alive instead of merely
more visible.

`v1.12.0` should close around three themes that now fit together:
`v1.13.0` should close around three tightly related themes:

- provider source cataloging and alerting as a first-class operator surface
- clearer aggregator behavior for Kilo and BLACKBOX, especially where "free", "budget", "wallet", and explicit paid lanes are easy to conflate
- hardened release automation after the `v1.11.x` release failures
- provider source catalog moves from mirrored docs pages to a more living operator dataset
- local key and route visibility are overlaid against global provider source snapshots
- provider drift gets classified more clearly as global docs drift, key-specific access drift, or route-level mismatch

The release should feel coherent from an operator point of view:

- Quick Setup, Doctor, Provider Probe, Dashboard, and route preview all explain drift or route pressure using the same language
- Kilo explicit Sonnet/Opus lanes are visible as deliberate routing choices instead of hidden aggregator magic
- release prep, tag validation, and publish dry-runs are boring and repeatable again
- Doctor, Provider Probe, Dashboard, Quick Setup, and `/api/provider-catalog` tell the same story about what changed globally and what is only true for this key or route
- free-tier, paid-tier, wallet, and BYOK assumptions are treated as per-key operational facts instead of being inferred blindly from public pricing tables
- the provider source catalog becomes a trustworthy early-warning surface before route selection starts leaning on outdated assumptions

What is intentionally not in scope for `v1.12.0`:
What is intentionally not in scope for `v1.13.0`:

- the virtual key layer
- gateway-level response caching
- fully automated external provider-source crawling on a long-running schedule
- a large new bridge or client-surface expansion

Those stay as follow-on tracks once the operator surfaces, release path, and aggregator semantics are stable enough to trust.
Those stay as follow-on tracks once the provider catalog and route-availability overlay are stable enough to trust under real operator workflows.

## Shipped: `v1.8.0` – `v1.9.1`

Expand Down
14 changes: 11 additions & 3 deletions faigate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1704,9 +1704,10 @@ def _normalize_provider_source_refresh(data: dict[str, Any]) -> dict[str, Any]:
if interval_seconds <= 0:
raise ConfigError("'provider_source_refresh.interval_seconds' must be positive")

providers = raw.get("providers", ["blackbox", "kilo", "openai"])
default_providers = ["anthropic", "blackbox", "deepseek", "google", "kilo", "openai"]
providers = raw.get("providers", default_providers)
if providers in (None, ""):
providers = ["blackbox", "kilo", "openai"]
providers = default_providers
if not isinstance(providers, list) or any(
not isinstance(item, str) or not item.strip() for item in providers
):
Expand Down Expand Up @@ -1881,7 +1882,14 @@ def provider_source_refresh(self) -> dict:
"on_startup": True,
"timeout_seconds": 10.0,
"interval_seconds": 21600,
"providers": ["blackbox", "kilo", "openai"],
"providers": [
"anthropic",
"blackbox",
"deepseek",
"google",
"kilo",
"openai",
],
},
)

Expand Down
31 changes: 31 additions & 0 deletions faigate/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
)
from .lane_registry import get_provider_lane_binding, get_route_add_recommendations
from .metrics import MetricsStore, calc_cost
from .provider_availability import (
record_availability_from_config,
refresh_local_model_availability,
)
from .provider_catalog import (
build_provider_catalog_report,
build_provider_discovery_view,
Expand Down Expand Up @@ -74,6 +78,10 @@
_provider_catalog_refresh_task: asyncio.Task[None] | None = None


def _provider_catalog_config_path() -> str:
return str(os.environ.get("FAIGATE_CONFIG_FILE") or "config.yaml")


class PayloadTooLargeError(ValueError):
"""Raised when one request or upload exceeds configured size limits."""

Expand Down Expand Up @@ -204,6 +212,21 @@ async def _refresh_provider_source_catalog(*, force: bool = False) -> list[dict[
provider_ids=target_ids,
timeout_seconds=float(source_refresh_cfg.get("timeout_seconds") or 10.0),
)
await asyncio.to_thread(
record_availability_from_config,
_provider_catalog_store,
config_path=_provider_catalog_config_path(),
health_payload={
"providers": {item["name"]: item for item in _build_provider_inventory()}
},
)
await asyncio.to_thread(
refresh_local_model_availability,
_provider_catalog_store,
config_path=_provider_catalog_config_path(),
provider_ids=target_ids,
timeout_seconds=float(source_refresh_cfg.get("timeout_seconds") or 10.0),
)
ok_count = sum(1 for item in refresh_results if item.ok)
logger.info(
"Provider source refresh completed: %s/%s source endpoints succeeded (%s)",
Expand Down Expand Up @@ -1908,6 +1931,14 @@ async def provider_catalog():
"priority_next": {},
}
if _provider_catalog_store is not None:
await asyncio.to_thread(
record_availability_from_config,
_provider_catalog_store,
config_path=_provider_catalog_config_path(),
health_payload={
"providers": {item["name"]: item for item in _build_provider_inventory()}
},
)
source_catalog = build_catalog_summary(
_provider_catalog_store,
provider_ids=list(_config.provider_source_refresh.get("providers") or []),
Expand Down
Loading
Loading