feat(usage): mark attempt list as best-effort observations

Decide attempt facts are best-effort (adr-002), not authoritative history:
- Usage detail response exposes attemptsComplete: false
- frontend labels the attempt chain as best-effort
- architecture.md documents the semantics

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
zyycn
2026-08-02 00:31:39 +08:00
co-authored by Claude Fable 5
parent 3c4ac99ae4
commit 7ed2a6668a
4 changed files with 19 additions and 5 deletions
@@ -718,6 +718,8 @@ pub struct UsageRecordDetailView {
#[serde(flatten)]
pub request: UsageRecordView,
pub attempts: Vec<UsageAttemptView>,
/// 尝试列表是否完整;best-effort 下恒为 false。
pub attempts_complete: bool,
}
/// Dashboard 趋势数据。
@@ -1842,6 +1844,7 @@ fn usage_detail_view(detail: domain::UsageDetail) -> UsageRecordDetailView {
.into_iter()
.map(usage_attempt_view)
.collect(),
attempts_complete: false,
}
}
+3 -2
View File
@@ -66,8 +66,7 @@ API decode/auth
1. 每个客户端请求只有一条 `model_requests`;attempt 明细保存在其 JSON 事实中,不另建 attempt 表。
2. 任何可能到达上游的调用都必须先按序提交 request/attempt 观测到有界队列;PostgreSQL 投影是可恢复
观测,不是数据面发送的同步前置条件。
3. `not_sent`、`sent`、`ambiguous` 是不同的上游发送边界;发送状态是请求级单调水位(`sent` > `ambiguous` > `not_sent`),跨 attempt 只升不降,终态写回不得低于水位;`ambiguous` 不自动重放。
观测,不是数据面发送的同步前置条件。3. `not_sent`、`sent`、`ambiguous` 是不同的上游发送边界;发送状态是请求级单调水位(`sent` > `ambiguous` > `not_sent`),跨 attempt 只升不降,终态写回不得低于水位;`ambiguous` 不自动重放。
4. 内存中的 downstream commit 表示网关已作出不可撤回的交付承诺,从该时刻禁止 retry/fallback;
它在实际首字节写出前越过并按序入队。`downstream_committed_at` 是该事实的 PostgreSQL 观测投影,
不宣称字节已经到达客户端,也不反向决定协议交付。
@@ -75,6 +74,8 @@ API decode/auth
6. 下游 commit 前,Core 可按冻结策略处理同一 Provider 内的账号 fallback;禁止跨 Provider fallback。
7. 本架构不通过隐式连接复用承载业务身份。HTTP client 可安全复用 transport 连接,但账号、credential revision、cookie/session binding 必须显式绑定到本次调用。
管理端 Usage 详情中的 attempt 列表是 best-effort 观测(见 adr-002):中间失败来自 `ops_events`,最终尝试由 `model_requests` 合成,二者都不承诺完整(队列丢弃、写入失败、非失败观测不落库会造成缺口)。API 通过 `attemptsComplete: false` 显式标注这一语义,不再声称「全部尝试」。
## 4. 路由、fallback 与错误处理
RuntimeSnapshot 冻结 Provider 集合、模型能力、运行时策略和全局 `config_revision`。一次请求始终使用同一快照,不在执行中拼接新旧配置。
+2
View File
@@ -175,6 +175,8 @@ export interface UsageAttempt {
export type UsageRecordDetail = UsageRecord & {
attempts: UsageAttempt[]
/** 尝试列表是否完整;best-effort 下恒为 false。 */
attemptsComplete: boolean
}
export interface UsageRecordsResponse {
@@ -402,9 +402,17 @@ const tokenDonutOption = computed<EChartsOption>(() => {
</section>
<section v-if="attemptRows.length" :class="panelClass">
<h3 :class="panelTitleClass">
尝试链路
</h3>
<div class="flex items-center gap-2">
<h3 :class="panelTitleClass">
尝试链路
</h3>
<span
class="rounded-full bg-(--cp-bg-muted) px-2 py-0.5 text-[11px] leading-none font-emphasis text-(--cp-text-secondary)"
title="中间尝试可能有缺口,仅展示可观测到的记录"
>
尽力观测
</span>
</div>
<BaseTable
class="mt-3 min-w-0"
:columns="attemptColumns"