Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
c8f1bb6
feat: add versioned session binding compatibility layer
Brisbanehuang Jul 20, 2026
0e90937
fix: observe late capability probe failures
Brisbanehuang Jul 20, 2026
fa8342c
fix: guard versioned session termination conflicts
Brisbanehuang Jul 20, 2026
a5aeccf
feat(proxy): add bounded streaming discovery
Brisbanehuang Jul 20, 2026
31c4324
fix(proxy): keep discovery disabled path compatible
Brisbanehuang Jul 20, 2026
c17a756
fix(proxy): keep discovery disabled path compatible
Brisbanehuang Jul 20, 2026
626f804
test(proxy): keep hedge lifecycle assertions current
Brisbanehuang Jul 20, 2026
856a44e
feat(settings): configure bounded streaming discovery
Brisbanehuang Jul 20, 2026
d4cba72
fix(proxy): close discovery coordinator lifecycle gaps
Brisbanehuang Jul 20, 2026
722900e
fix(proxy): close discovery coordinator lifecycle gaps
Brisbanehuang Jul 20, 2026
9c7793c
fix(proxy): guard optional discovery request message
Brisbanehuang Jul 20, 2026
3edd52b
fix(proxy): guard optional discovery request message
Brisbanehuang Jul 20, 2026
ee5557e
fix(proxy): handle replacement launch failures
Brisbanehuang Jul 20, 2026
fcaf248
fix(proxy): handle replacement launch failures
Brisbanehuang Jul 20, 2026
00de8ab
fix(discovery): close coordinator and configuration lifecycle gaps
Brisbanehuang Jul 20, 2026
420802a
docs(discovery): document Redis cluster capability fallback
Brisbanehuang Jul 20, 2026
96cbae6
fix(discovery): synchronize Sticky fallback promotion
Brisbanehuang Jul 20, 2026
e453b8e
fix(discovery): probe binding capability before eligibility
Brisbanehuang Jul 20, 2026
4c9690d
fix(discovery): reject stale attempt boundary events
Brisbanehuang Jul 20, 2026
43c6733
fix(proxy): preserve complete discovery candidates
Brisbanehuang Jul 20, 2026
8cbf3be
fix(discovery): honor configured sticky SLA and validation
Brisbanehuang Jul 20, 2026
488bc8c
fix: guard legacy binding fallback races
Brisbanehuang Jul 20, 2026
e95f0d0
test(discovery): cover complete ready stream
Brisbanehuang Jul 20, 2026
7e6dadf
fix(proxy): pause held discovery readers
Brisbanehuang Jul 20, 2026
309d6ca
test(settings): cover discovery window boundary
Brisbanehuang Jul 20, 2026
0de55d7
fix(discovery): recognize Anthropic tool deltas
Brisbanehuang Jul 20, 2026
8e0cedb
fix(proxy): close discovery attempt cleanup gaps
Brisbanehuang Jul 20, 2026
09fbb57
fix: preserve legacy mirror during binding races
Brisbanehuang Jul 20, 2026
45dac94
fix: validate canonical provider before mirror restore
Brisbanehuang Jul 20, 2026
0f7e170
fix(proxy): execute discovery normal winner actions
Brisbanehuang Jul 20, 2026
059de38
fix(discovery): preserve binding safety and tool prefixes
Brisbanehuang Jul 20, 2026
0690278
fix(proxy): keep discovery timers and candidates consistent
Brisbanehuang Jul 20, 2026
7d49cde
fix(discovery): clear binding against snapshot provider
Brisbanehuang Jul 20, 2026
9ac3d99
fix(binding): preserve imported legacy mirrors
Brisbanehuang Jul 20, 2026
f98a558
fix(discovery): preserve rectifier and sticky binding semantics
Brisbanehuang Jul 20, 2026
24a39cc
style(discovery): organize response handler imports
Brisbanehuang Jul 20, 2026
8adf020
fix(binding): guard legacy owner refresh races
Brisbanehuang Jul 20, 2026
a643ff8
fix(discovery): honor effective group priority
Brisbanehuang Jul 20, 2026
db5d68e
merge: synchronize versioned binding fixes
Brisbanehuang Jul 20, 2026
bb86b49
merge: synchronize discovery and binding fixes
Brisbanehuang Jul 20, 2026
21c1219
fix(binding): close legacy session races
Brisbanehuang Jul 20, 2026
e3cb570
fix(settings): tighten discovery configuration
Brisbanehuang Jul 20, 2026
2be4a4e
merge: synchronize session binding safety fixes
Brisbanehuang Jul 20, 2026
ca12624
fix(discovery): harden bounded streaming lifecycle
Brisbanehuang Jul 20, 2026
3e9fcd9
merge: synchronize discovery safety fixes
Brisbanehuang Jul 20, 2026
8ccaac3
fix(binding): preserve failover session metadata
Brisbanehuang Jul 20, 2026
0537a6e
fix(discovery): retain blocked fallback readiness
Brisbanehuang Jul 20, 2026
42ffcf4
merge: synchronize latest binding safety fixes
Brisbanehuang Jul 20, 2026
e852b6d
merge: synchronize latest discovery and binding fixes
Brisbanehuang Jul 20, 2026
c433d35
fix(binding): preserve scoped versioned session state
Brisbanehuang Jul 20, 2026
12887e4
fix(discovery): reserve sticky timeout wave
Brisbanehuang Jul 21, 2026
08337d8
fix(discovery): stop parsing after validity errors
Brisbanehuang Jul 21, 2026
95a94e8
feat(binding): add snapshot-safe TTL touch
Brisbanehuang Jul 21, 2026
41ecb53
merge: synchronize binding touch and scoped termination fixes
Brisbanehuang Jul 21, 2026
ddd156b
merge: synchronize final binding and discovery safety fixes
Brisbanehuang Jul 21, 2026
3d314e3
fix(discovery): preserve long-stream binding safety
Brisbanehuang Jul 21, 2026
825f4ff
fix(discovery): fail closed on binding conflicts
Brisbanehuang Jul 21, 2026
ef6b833
fix(discovery): close final lifecycle gaps
Brisbanehuang Jul 21, 2026
9391c30
fix(binding): close long-stream and terminate races
Brisbanehuang Jul 21, 2026
811192a
fix(discovery): reject failed response terminal markers
Brisbanehuang Jul 21, 2026
d915e08
fix(discovery): refill setup failures within current round
Brisbanehuang Jul 21, 2026
81d9b4f
fix(binding): fence failed hedge cleanup
Brisbanehuang Jul 21, 2026
7e24438
merge: synchronize final binding lifecycle fixes
Brisbanehuang Jul 21, 2026
754c1a9
merge: synchronize final discovery lifecycle fixes
Brisbanehuang Jul 21, 2026
74bb97e
fix(discovery): fence readiness and sticky cooldown
Brisbanehuang Jul 21, 2026
684e334
fix(session): scope content hash mapping by API key
Brisbanehuang Jul 21, 2026
3f9cca1
merge: synchronize tenant-scoped content hash fixes
Brisbanehuang Jul 21, 2026
3c1105b
merge: synchronize tenant isolation and discovery fences
Brisbanehuang Jul 21, 2026
0880619
fix(discovery): unwrap Gemini response candidates
Brisbanehuang Jul 21, 2026
8b1b678
fix(binding): avoid reusing canonical legacy mirrors
Brisbanehuang Jul 21, 2026
c1b291c
Merge branch 'codex/versioned-session-binding' into codex/discovery-pr2
Brisbanehuang Jul 21, 2026
1d23d6c
fix(discovery): release non-SSE winner resources
Brisbanehuang Jul 21, 2026
51cbc69
Merge branch 'codex/discovery-pr2' into codex/discovery-pr3
Brisbanehuang Jul 21, 2026
bde3d9f
fix(discovery): localize validation and completion checks
Brisbanehuang Jul 21, 2026
4c1f166
fix(discovery): refresh cleared binding authority
Brisbanehuang Jul 21, 2026
acda2e3
merge: synchronize discovery authority fixes
Brisbanehuang Jul 21, 2026
9643f10
fix(discovery): preserve peers after retry setup failure
Brisbanehuang Jul 21, 2026
1c82d52
fix(discovery): parse SSE events by complete data frames
Brisbanehuang Jul 21, 2026
43fa079
fix(discovery): parse SSE events by complete data frames
Brisbanehuang Jul 21, 2026
d98f641
fix(discovery): reserve slots during rectifier retry setup
Brisbanehuang Jul 21, 2026
3dd24e7
fix(discovery): validate fallback stream completion
Brisbanehuang Jul 21, 2026
75382b9
fix(discovery): close setup reservation races
Brisbanehuang Jul 21, 2026
70f3c44
fix(discovery): gate queued fallback promotion
Brisbanehuang Jul 21, 2026
1ae6bfb
fix(discovery): await queued wave handoff
Brisbanehuang Jul 21, 2026
e7b33e7
fix(discovery): refill failed fallback during setup
Brisbanehuang Jul 21, 2026
290e4c2
fix(config): stabilize Discovery window error code
Brisbanehuang Jul 21, 2026
8403236
feat(observability): add request-level Discovery routing trace
Brisbanehuang Jul 21, 2026
295b24f
test: complete live routing trace mocks
Brisbanehuang Jul 21, 2026
0819bfb
fix(discovery): close review gaps in trace and stream finalization
Brisbanehuang Jul 21, 2026
0e31397
fix(discovery): address follow-up review findings
Brisbanehuang Jul 21, 2026
0c6df07
fix(observability): durably persist final routing trace
Brisbanehuang Jul 21, 2026
01277fc
fix(observability): make final routing trace recoverable
Brisbanehuang Jul 21, 2026
5f14973
feat(ui): expose Discovery attempt details
Brisbanehuang Jul 21, 2026
c7a1dc3
fix(ui): polish Discovery trace review details
Brisbanehuang Jul 21, 2026
34f82f3
fix(ui): clarify Discovery fallback outcomes
Brisbanehuang Jul 21, 2026
31b8399
feat(ui): clarify Discovery routing summaries
Brisbanehuang Jul 22, 2026
f1ee7ee
feat(ui): color Discovery route badges
Brisbanehuang Jul 22, 2026
20fa640
fix(discovery): preserve final rescue and bill ready losers
Brisbanehuang Jul 22, 2026
de15e9c
fix(discovery): address review feedback
Brisbanehuang Jul 22, 2026
cd9a40d
Merge remote-tracking branch 'origin/integration/discovery-stack-2026…
ding113 Jul 22, 2026
25f0fa9
test(discovery): update versioned cleanup expectation
ding113 Jul 22, 2026
4aae243
Merge pull request #1347 from Brisbanehuang/codex/versioned-session-b…
ding113 Jul 22, 2026
a885e0c
Merge remote-tracking branch 'origin/integration/discovery-stack-2026…
ding113 Jul 22, 2026
46315cf
Merge pull request #1348 from Brisbanehuang/codex/discovery-pr2
ding113 Jul 23, 2026
06bbc1b
Merge remote-tracking branch 'origin/integration/discovery-stack-2026…
ding113 Jul 23, 2026
d585290
fix(discovery): align fallback error handling
ding113 Jul 23, 2026
113824c
fix(proxy): pass error to tryApplyReactiveRectifier in hedge catch path
github-actions[bot] Jul 23, 2026
28ee73f
Merge pull request #1349 from Brisbanehuang/codex/discovery-pr3
ding113 Jul 23, 2026
2c4a727
Merge remote-tracking branch 'origin/integration/discovery-stack-2026…
ding113 Jul 23, 2026
6c67804
test(discovery): preserve nested Responses failure classification
ding113 Jul 23, 2026
57bddf5
Merge remote-tracking branch 'origin/integration/discovery-stack-2026…
ding113 Jul 23, 2026
333ea75
Merge pull request #1351 from Brisbanehuang/codex/discovery-routing-t…
ding113 Jul 23, 2026
6a25fcc
Merge remote-tracking branch 'origin/integration/discovery-stack-2026…
ding113 Jul 23, 2026
671870b
Merge pull request #1353 from Brisbanehuang/codex/discovery-final-res…
ding113 Jul 23, 2026
104e42d
fix(discovery): address final integration review findings
ding113 Jul 23, 2026
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ ENABLE_API_KEY_REDIS_CACHE="true" # 是否启用 API Key Redis 缓存(
# 降低该值会按签发时间收紧已签发 ADMIN_TOKEN 签名 cookie 的剩余寿命,且不会延长其原始 exp。
AUTH_SESSION_TTL_SECONDS=604800 # Web UI 登录态过期时间(秒,默认 604800 = 7 天,范围 60-31536000)
SESSION_TTL=300 # 代理请求上下文缓存时间(秒,默认 300 = 5 分钟;不控制 Web UI 登录态)
DISCOVERY_ROLLOUT_PERCENT=100 # Discovery 运维灰度比例(0-100,按 API Key + Session 稳定分桶)
STORE_SESSION_MESSAGES=false # 会话消息存储模式(默认:false)
# - false:存储请求/响应体但对 message 内容脱敏 [REDACTED]
# - true:原样存储 message 内容(注意隐私和存储空间影响)
Expand Down
71 changes: 71 additions & 0 deletions docs/streaming-discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Bounded Streaming Discovery

Bounded Discovery is an optional cold-start routing mode for streaming
requests. It exists to reduce duplicate upstream spend while preserving a
working request when the first provider is slow.

## Defaults

| Setting | Default | Meaning |
| --- | ---: | --- |
| `discoveryEnabled` | `false` | Keep the existing Hedge path until explicitly enabled. |
| `discoveryConcurrency` | `2` | Number of normal providers in the first batch. |
| `maxDiscoveryRounds` | `2` | Maximum Discovery rounds. |
| `discoverySlaMs` | `10000` | First-byte budget for a Discovery round. |
| `stickySlaMs` | `20000` | First-byte budget for an existing Sticky provider. |
| `racingTotalTimeoutMs` | `60000` | Total pre-winner deadline; it is cleared after a winner is committed. |
| `stickyTimeoutCooldownMs` | `300000` | Session/provider cooldown after a Sticky timeout. |

The total deadline must be at least `stickySlaMs + maxDiscoveryRounds *
discoverySlaMs`. The UI and API reject configurations that do not satisfy
this relationship.

## Request lifecycle

- A healthy Sticky provider is probed alone. If it times out, it becomes the
single fallback for this request and receives a cooldown; a later request
may select it again after the cooldown.
- A cold start launches the configured initial normal candidates. The highest
priority ready candidate wins; a lower-priority candidate remains held while
a higher-priority candidate is still inside its SLA window.
- At a round boundary, at most one pending normal attempt is promoted to the
fallback. The next round uses the remaining slots for new normal candidates,
so `discoveryConcurrency=2` means `one fallback + one new candidate`.
- A fallback that has produced a valid prefix is held until the current normal
window closes, all normal candidates fail, or no candidates remain. A normal
winner always has precedence during the window.
- When `bill_hedge_losers` is enabled, a Discovery loser that already produced
a protocol-valid prefix and reached ready state may reuse the legacy
background drain and billing path. It is billed only after natural stream
completion with a completion marker and explicit usage. All other losers are
cancelled and their readers/agents/provider-session references are released.
- Sticky binding is written only after a natural, successful stream completion
with the protocol completion marker and a generation-aware CAS. Fake-200,
incomplete, and client-aborted streams do not create or renew Sticky.

Discovery is eligible only for supported streaming protocol families and when
the versioned Redis binding capability is available. If Redis capability is
unknown/unavailable, the existing provider selection and Hedge behavior remain
active.

The versioned binding scripts require the canonical binding, legacy provider,
legacy owner, lease, and cooldown keys to be evaluated atomically. On Redis
Cluster layouts where those keys do not share a slot and Redis returns
`CROSSSLOT` (or when Lua capability probing fails), the capability state is
`unavailable`; the service records that state and uses the tenant-checked
legacy wrapper. Discovery stays disabled until a later connection-lifecycle
probe succeeds.

## Rollout

1. Apply the system-settings migration.
2. Confirm the Redis versioned-binding capability probe is `available`.
3. Leave `discoveryEnabled=false` while validating the existing Hedge and
versioned binding checks.
4. Enable Discovery for a controlled group, observe provider-chain outcomes,
first-token latency, fallback promotions, cancellations, CAS conflicts, and
final 503s.
5. Disable the setting to return immediately to the legacy Hedge path.

This feature does not change the final client failure contract: an exhausted
request continues to return the existing `503` mapping.
7 changes: 7 additions & 0 deletions drizzle/0110_daffy_rawhide_kid.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE "system_settings" ADD COLUMN "discovery_enabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "system_settings" ADD COLUMN "discovery_concurrency" integer DEFAULT 2 NOT NULL;--> statement-breakpoint
ALTER TABLE "system_settings" ADD COLUMN "max_discovery_rounds" integer DEFAULT 2 NOT NULL;--> statement-breakpoint
ALTER TABLE "system_settings" ADD COLUMN "discovery_sla_ms" integer DEFAULT 10000 NOT NULL;--> statement-breakpoint
ALTER TABLE "system_settings" ADD COLUMN "sticky_sla_ms" integer DEFAULT 20000 NOT NULL;--> statement-breakpoint
ALTER TABLE "system_settings" ADD COLUMN "racing_total_timeout_ms" integer DEFAULT 60000 NOT NULL;--> statement-breakpoint
ALTER TABLE "system_settings" ADD COLUMN "sticky_timeout_cooldown_ms" integer DEFAULT 300000 NOT NULL;
41 changes: 41 additions & 0 deletions drizzle/0111_happy_mauler.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
ALTER TABLE "message_request" ADD COLUMN "routing_trace" jsonb;

-- Routing trace finalization is observability-only. Restrict the ledger trigger
-- to columns that can actually change its projection so trace-only patches do
-- not rewrite accounting rows.
DROP TRIGGER IF EXISTS trg_upsert_usage_ledger ON message_request;

CREATE TRIGGER trg_upsert_usage_ledger
AFTER INSERT OR UPDATE OF
blocked_by,
status_code,
error_message,
provider_chain,
actual_response_model,
endpoint,
provider_id,
user_id,
"key",
model,
original_model,
api_type,
session_id,
cost_usd,
cost_multiplier,
group_cost_multiplier,
input_tokens,
output_tokens,
cache_creation_input_tokens,
cache_read_input_tokens,
cache_creation_5m_input_tokens,
cache_creation_1h_input_tokens,
cache_ttl_applied,
context_1m_applied,
swap_cache_ttl_applied,
duration_ms,
ttfb_ms,
client_ip,
created_at
ON message_request
FOR EACH ROW
EXECUTE FUNCTION fn_upsert_usage_ledger();
Loading
Loading