Skip to content

Commit 18eeb06

Browse files
committed
Merge upstream main to resolve conflicts
Made-with: Cursor
2 parents 49cbede + 5c2cdc4 commit 18eeb06

49 files changed

Lines changed: 5856 additions & 156 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/catalog-check.yml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,32 @@ jobs:
2222
go-version-file: go.mod
2323
cache-dependency-path: go.sum
2424

25+
- name: Restore catalog check history
26+
uses: actions/cache/restore@v4
27+
with:
28+
path: .catalog-check-history.json
29+
key: catalog-check-history-${{ github.ref_name }}-${{ github.run_id }}
30+
restore-keys: |
31+
catalog-check-history-${{ github.ref_name }}-
32+
2533
- name: Run catalog source URL check
2634
id: check
27-
run: go run ./scripts/catalog-check
35+
run: >-
36+
go run ./scripts/catalog-check
37+
-mode strict
38+
-fail-after 3
39+
-hard-status 404,410
40+
-allow-status 403,429
41+
-history .catalog-check-history.json
2842
continue-on-error: true
2943

44+
- name: Save catalog check history
45+
if: always()
46+
uses: actions/cache/save@v4
47+
with:
48+
path: .catalog-check-history.json
49+
key: catalog-check-history-${{ github.ref_name }}-${{ github.run_id }}
50+
3051
- name: Open issue on failure
3152
if: steps.check.outcome == 'failure'
3253
uses: actions/github-script@v7

CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,49 @@ All notable changes to Ferro Labs AI Gateway will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
## [0.6.0] — 2026-03-06
11+
12+
### Added
13+
14+
- **`pii-redact` guardrail plugin** (`internal/plugins/pii/`): detects PII entities (email, phone, credit card with Luhn validation, SSN, IP, AWS access key, IBAN, passport) with configurable actions (`block|redact|warn|log`), per-entity overrides, input/output scope, and redaction modes (`mask`, `replace_type`, `hash`, `synthetic`)
15+
- **`secret-scan` guardrail plugin** (`internal/plugins/secretscan/`): detects hardcoded credentials and DSNs across cloud/API/source-control/payment patterns with optional Shannon entropy filtering for generic high-entropy tokens
16+
- **`prompt-shield` guardrail plugin** (`internal/plugins/promptshield/`): scores prompt-injection signals using max-confidence matching with threshold-based enforcement and optional custom tenant signals
17+
- **`schema-guard` guardrail plugin** (`internal/plugins/schemaguard/`): validates JSON content against tenant-provided JSON Schema (draft-07 via `github.com/santhosh-tekuri/jsonschema/v5`), including optional markdown code-fence JSON extraction
18+
- **`regex-guard` guardrail plugin** (`internal/plugins/regexguard/`): ordered, per-rule regex guardrail supporting stage targeting (`input|output|both`) and per-rule actions (`block|warn|log`)
19+
- **Shared guardrail helpers** (`internal/plugins/guardrailutil/`): common parsing and message extraction helpers used by guardrail plugins to keep config handling consistent
20+
21+
### Changed
22+
23+
- **Plugin rejection handling** (`plugin/errors.go`, `plugin/manager.go`): introduced typed `RejectionError` for intentional guardrail rejections and extended after-request lifecycle to propagate rejections (needed for output guardrails such as `schema-guard`)
24+
- **Gateway request mutation propagation** (`gateway.go`): `Route()` now applies before-request plugin request mutations before strategy execution (enables in-place redaction plugins)
25+
- **HTTP error mapping for guardrail rejections** (`cmd/ferrogw/main.go`): chat-completions routes now return `400 invalid_request_error` for plugin rejection errors instead of generic `500 routing_error`
26+
- **Plugin registration** (`cmd/ferrogw/main.go`, `cmd/ferrogw-cli/main.go`): wired all new guardrail plugins into server and CLI plugin listing
27+
- **Config examples** (`config.example.yaml`, `config.example.json`): added sample blocks for all new guardrail plugins
28+
29+
## [0.5.0] — 2026-03-03
30+
31+
### Added
32+
33+
- **Streaming cost tracking** (`internal/streamwrap/wrap.go`): `Meter()` wraps any `<-chan StreamChunk` in a transparent goroutine that accumulates token usage from the final chunk and emits `gateway_requests_total`, `gateway_request_duration_seconds`, `gateway_tokens_input_total`, `gateway_tokens_output_total`, and `gateway_request_cost_usd_total` Prometheus metrics plus `request.completed` event hooks on stream close; `RouteStream()` in `gateway.go` now fully mirrors `Route()` metrics coverage
34+
- **OpenAI streaming usage** (`providers/openai.go`): `CompleteStream()` now sets `stream_options.include_usage: true` so the final SSE chunk carries token counts; `StreamChunk` gained a `Usage` field populated from the final chunk's usage data (including `reasoning_tokens` and `cached_tokens`)
35+
- **`providers.ParseStatusCode(err)`** (`providers/provider.go`): regex-based helper extracting the HTTP status code from provider error messages formatted as `"... (NNN): ..."` — used by retry and fallback logic across all 15 providers without requiring per-provider changes
36+
- **Per-target retry status-code filtering** (`internal/strategies/fallback.go`): `Fallback.WithTargetRetry()` now accepts an `onStatusCodes []int` slice; if non-empty, retries are only attempted when the error's status code is in the list — e.g. retry on 429/503 but fail-fast on 400/401; `shouldRetry()` helper extracts codes via `ParseStatusCode`
37+
- **`RetryConfig` extensions** (`config.go`): new `on_status_codes` (array of ints) and `initial_backoff_ms` (int, default 100) fields on per-target retry config
38+
- **Least-latency routing strategy** (`internal/strategies/leastlatency.go`, `internal/latency/tracker.go`): `LeastLatency` strategy selects the compatible provider with the lowest P50 latency from a thread-safe in-process sliding window (default 100 samples per provider); falls back to random selection when a provider has no recorded samples; `Route()` records every successful call's latency into a shared `*latency.Tracker` on the `Gateway` struct
39+
- **Cost-optimized routing strategy** (`internal/strategies/costoptimized.go`): `CostOptimized` strategy estimates prompt token count (~4 chars/token heuristic on request messages), calls `models.Calculate()` for each compatible provider, and routes to the cheapest option; falls back to the first compatible provider when no catalog pricing is available
40+
- **`ModeLatency` / `ModeCostOptimized` strategy modes** (`config.go`): two new `StrategyMode` constants (`"least-latency"`, `"cost-optimized"`) wired into `gateway.go`'s `getStrategy()` switch
41+
- **CLI UX overhaul** (`cmd/ferrogw-cli/`): replaced hand-rolled `switch os.Args[1]` with [Cobra](https://github.com/spf13/cobra); added persistent `--gateway-url`, `--api-key`, and `--format table|json|yaml` flags; ported `validate`, `plugins`, `version` commands to `cobra.RunE`; added full `admin` command group (`admin keys list/get/create/delete/rotate`, `admin config get/history/update/rollback`, `admin logs list/stats`, `admin providers list/health`) in `admin.go`; thin admin HTTP client in `client.go`; table/JSON/YAML output formatter in `output.go`
42+
43+
### Changed
44+
45+
- **`gateway.go` `RouteStream()`**: emits error metrics on provider failure (previously silent); wraps the raw provider channel with `streamwrap.Meter()` for full metrics/event parity with `Route()`
46+
- **`gateway.go` `getStrategy()`**: `ModeFallback` now wires per-target `RetryConfig` (including `OnStatusCodes` and `InitialBackoffMs`) via `fb.WithTargetRetry()`; added `ModeLatency` and `ModeCostOptimized` cases
47+
- **`gateway.go` `Route()`**: records per-provider response latency into `g.latencyTracker` on every successful call
48+
49+
---
50+
851
## [0.4.5] — 2026-02-28
952

1053
### Added

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,8 @@ Ferro Gateway is actively developed to support an end-to-end AI operating enviro
477477
* [x] **v0.2.0** — Observability & Resilience: Structured JSON logging with trace IDs, Prometheus metrics, per-provider circuit breakers, token-bucket rate limiting, deep health checks, and consistent error schema.
478478
* [x] **v0.3.0** — Modality Expansions: Embeddings, Image generation mapping, Cost tracking via pricing tables, and Model aliasing.
479479
* [x] **v0.4.0** — Persistent State: Dedicated Admin API, SQLite/PostgreSQL persistence, persistent request logs, dashboard, and runtime config CRUD.
480-
* [ ] **v0.5.0** — Advanced Intelligence: Least-latency and Cost-optimized algorithmic routing, A/B Testing modules, and Semantic Caching.
480+
* [x] **v0.5.0** — Advanced Intelligence: Least-latency and Cost-optimized algorithmic routing, A/B Testing modules, and Semantic Caching.
481+
* [x] **v0.6.0** — Developer Experience: Server-side prompt templates and expanded guardrails.
481482
* [ ] **v1.0.0** — Production Ready: Helm charts, open-telemetry export, edge caching, and official SDK embeddings.
482483

483484
*Review our detailed [ROADMAP.md](ROADMAP.md) for deeper implementation plans.*

ROADMAP.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,40 @@
128128

129129
## v0.5.0 — Advanced Routing & Intelligence
130130

131-
**Status**: 📋 Planned
131+
**Status**: ✅ Released
132132
**Theme**: Smart routing based on cost, latency, and content.
133133

134+
| Feature | Description | Status |
135+
|---|---|---|
136+
| **CLI UX overhaul** | `ferrogw-cli` migrated to Cobra: richer admin command groups (`admin keys`, `admin config`, `admin logs`, `admin providers`), `--format table/json/yaml` output flag, shell completions via `ferrogw-cli completion` | ✅ Done |
137+
| **Streaming cost tracking** | `RouteStream()` now wraps the SSE channel in a metering goroutine; emits Prometheus metrics (duration, tokens, cost) and event hooks on stream close, matching `Route()` behaviour | ✅ Done |
138+
| **Retry policies** | `RetryConfig` extended with `on_status_codes` (only retry listed HTTP status codes) and `initial_backoff_ms` (configurable exponential backoff base); applied per-target in the fallback strategy | ✅ Done |
139+
| **Least-latency routing** | New `least-latency` strategy mode; in-process rolling-window p50 tracker (`internal/latency`) records observed latency per provider; routes to fastest compatible provider, falls back to random when no samples exist | ✅ Done |
140+
| **Cost-optimized routing** | New `cost-optimized` strategy mode; estimates prompt cost via the model catalog for each compatible provider and routes to the cheapest; falls back to first compatible provider when pricing is unavailable | ✅ Done |
141+
142+
---
143+
144+
## v0.5.5 — Intelligent Request Handling
145+
146+
**Status**: 📋 Planned
147+
**Theme**: Route based on what the request says, not just what model it targets.
148+
149+
| Feature | Description |
150+
|---|---|
151+
| **Content-based routing** | Extend conditional strategy with `prompt_contains` and `prompt_regex` match keys; `X-Route-Tag` header overrides all rules via `header_routing` config map |
152+
| **A/B testing** | New `ab-test` strategy mode; traffic split by percentage across named variants; `variant` label on all Prometheus metrics; `GET /admin/experiments` endpoint for live stats |
153+
154+
---
155+
156+
## v0.6.0 — Developer Experience
157+
158+
**Status**: ✅ Completed
159+
**Release Date**: 2026-03-06
160+
**Theme**: Server-side prompt management to eliminate client-side template sprawl.
161+
134162
| Feature | Description |
135163
|---|---|
136-
| **CLI UX overhaul** | Improve `ferrogw-cli` with richer admin command groups, clearer help output, structured output modes (`table/json/yaml`), and shell completions |
137-
| **Streaming cost tracking** | Consume final `usage` chunk from SSE stream in `RouteStream()` and emit cost metrics/events on stream close, matching `Route()` behavior |
138-
| **Least-latency routing** | Route to the provider with lowest p50 latency |
139-
| **Cost-optimized routing** | Route to cheapest provider that meets quality threshold |
140-
| **Content-based routing** | Route based on prompt content (code → Codex, chat → GPT) |
141-
| **A/B testing** | Split traffic between models for comparison |
142-
| **Prompt templates** | Server-side prompt template management and versioning |
143-
| **Retry policies** | Configurable retry with status code filtering per provider |
164+
| **Prompt templates** | First-class `PromptTemplate` entity with CRUD admin API (`/admin/templates`); `template_id` + `variables` fields in request body; Go `text/template` rendering injected into `messages` before routing; memory / SQLite / PostgreSQL backends |
144165

145166
---
146167

0 commit comments

Comments
 (0)