diff --git a/scripts/shots/core/en/overview.json b/scripts/shots/core/en/overview.json index 9111af73..158330cf 100644 --- a/scripts/shots/core/en/overview.json +++ b/scripts/shots/core/en/overview.json @@ -10,15 +10,15 @@ }, { "id": "titling", - "mode": "passthrough" + "mode": "forward" }, { "id": "topic_detect", - "mode": "passthrough" + "mode": "forward" }, { "id": "suggestion", - "mode": "passthrough" + "mode": "forward" } ], "clients": [ diff --git a/scripts/shots/core/en/status.json b/scripts/shots/core/en/status.json index cba85b6e..1c1abf89 100644 --- a/scripts/shots/core/en/status.json +++ b/scripts/shots/core/en/status.json @@ -1,5 +1,5 @@ { - "api_version": 35, + "api_version": 36, "clients": 4, "config_path": "", "gateway_addr": null, @@ -14,5 +14,5 @@ "reachable": [] }, "uptime_secs": 0, - "version": "0.59.0" + "version": "0.60.0" } diff --git a/scripts/shots/core/zh/overview.json b/scripts/shots/core/zh/overview.json index 2df9aa12..8665b2eb 100644 --- a/scripts/shots/core/zh/overview.json +++ b/scripts/shots/core/zh/overview.json @@ -10,15 +10,15 @@ }, { "id": "titling", - "mode": "passthrough" + "mode": "forward" }, { "id": "topic_detect", - "mode": "passthrough" + "mode": "forward" }, { "id": "suggestion", - "mode": "passthrough" + "mode": "forward" } ], "clients": [ diff --git a/scripts/shots/core/zh/status.json b/scripts/shots/core/zh/status.json index cba85b6e..1c1abf89 100644 --- a/scripts/shots/core/zh/status.json +++ b/scripts/shots/core/zh/status.json @@ -1,5 +1,5 @@ { - "api_version": 35, + "api_version": 36, "clients": 4, "config_path": "", "gateway_addr": null, @@ -14,5 +14,5 @@ "reachable": [] }, "uptime_secs": 0, - "version": "0.59.0" + "version": "0.60.0" } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c61d36c8..b17bbf8c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -82,7 +82,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "wl-clipboard-rs", "x11rb", ] @@ -974,7 +974,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.3", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1175,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2651,7 +2651,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3591,7 +3591,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3647,7 +3647,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4733,7 +4733,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5279,8 +5279,8 @@ dependencies = [ [[package]] name = "tw-api" -version = "0.59.0" -source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.59.0#ef10a8b0bbc1325edf43553a87169029672808a4" +version = "0.60.0" +source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.60.0#291ef531b121117872815cd6db731fdd04ac336a" dependencies = [ "serde", "serde_json", @@ -5291,8 +5291,8 @@ dependencies = [ [[package]] name = "tw-dialect" -version = "0.59.0" -source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.59.0#ef10a8b0bbc1325edf43553a87169029672808a4" +version = "0.60.0" +source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.60.0#291ef531b121117872815cd6db731fdd04ac336a" dependencies = [ "serde", "serde_json", @@ -5300,8 +5300,8 @@ dependencies = [ [[package]] name = "tw-guard" -version = "0.59.0" -source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.59.0#ef10a8b0bbc1325edf43553a87169029672808a4" +version = "0.60.0" +source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.60.0#291ef531b121117872815cd6db731fdd04ac336a" dependencies = [ "base64 0.22.1", "bytes", @@ -5316,8 +5316,8 @@ dependencies = [ [[package]] name = "tw-link" -version = "0.59.0" -source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.59.0#ef10a8b0bbc1325edf43553a87169029672808a4" +version = "0.60.0" +source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.60.0#291ef531b121117872815cd6db731fdd04ac336a" dependencies = [ "serde", "serde_json", @@ -5343,8 +5343,8 @@ dependencies = [ [[package]] name = "tw-types" -version = "0.59.0" -source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.59.0#ef10a8b0bbc1325edf43553a87169029672808a4" +version = "0.60.0" +source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.60.0#291ef531b121117872815cd6db731fdd04ac336a" dependencies = [ "serde", "ts-rs", @@ -5352,8 +5352,8 @@ dependencies = [ [[package]] name = "tw-watch" -version = "0.59.0" -source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.59.0#ef10a8b0bbc1325edf43553a87169029672808a4" +version = "0.60.0" +source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.60.0#291ef531b121117872815cd6db731fdd04ac336a" dependencies = [ "notify", "thiserror 2.0.21", @@ -5362,8 +5362,8 @@ dependencies = [ [[package]] name = "tw-yaml" -version = "0.59.0" -source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.59.0#ef10a8b0bbc1325edf43553a87169029672808a4" +version = "0.60.0" +source = "git+https://github.com/ThinkWatchProject/ThinkWatch-Core.git?tag=v0.60.0#291ef531b121117872815cd6db731fdd04ac336a" dependencies = [ "saphyr-parser", "thiserror 2.0.21", @@ -5860,7 +5860,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 0d40570f..4ff7f9aa 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,12 +26,12 @@ license = "MIT" # twcore 二进制必须和这里编译进去的协议镜像来自同一个 core 版本 —— 打包脚本正是 # 从 tw-api 锁到的 tag 去取二进制的(见 scripts/fetch-core.sh)。 [workspace.dependencies] -tw-api = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.59.0" } -tw-types = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.59.0" } -tw-yaml = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.59.0" } -tw-guard = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.59.0" } -tw-watch = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.59.0" } -tw-link = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.59.0" } +tw-api = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.60.0" } +tw-types = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.60.0" } +tw-yaml = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.60.0" } +tw-guard = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.60.0" } +tw-watch = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.60.0" } +tw-link = { git = "https://github.com/ThinkWatchProject/ThinkWatch-Core.git", tag = "v0.60.0" } [lib] name = "thinkwatch_lite_lib" diff --git a/src-tauri/src/notices/fixtures/snapshot.json b/src-tauri/src/notices/fixtures/snapshot.json index 5063e4ed..1b5e5d94 100644 --- a/src-tauri/src/notices/fixtures/snapshot.json +++ b/src-tauri/src/notices/fixtures/snapshot.json @@ -148,15 +148,15 @@ }, { "id": "titling", - "mode": "passthrough" + "mode": "forward" }, { "id": "topic_detect", - "mode": "passthrough" + "mode": "forward" }, { "id": "suggestion", - "mode": "passthrough" + "mode": "forward" } ], "retention": { diff --git a/src/generated/tw-api.ts b/src/generated/tw-api.ts index df95d279..7b268c3e 100644 --- a/src/generated/tw-api.ts +++ b/src/generated/tw-api.ts @@ -1,6 +1,6 @@ // Generated by tw-api (`tw_api::ts::export_all`). Do not edit by hand. -export const CONTROL_API_VERSION = 35; +export const CONTROL_API_VERSION = 36; /** * 一个账号上游登的是哪个账号。 @@ -672,7 +672,7 @@ export type Dialect = "anthropic" | "openai-chat" | "openai-responses" | "gemini /** * 一次试算的结论。 */ -export type DryRunOutcome = "route" | "deny" | "no_match" | "unavailable" | "intercepted" | "passthrough"; +export type DryRunOutcome = "route" | "deny" | "no_match" | "unavailable" | "intercepted"; /** * 「如果现在来这样一个请求,会走到哪儿」。 @@ -719,11 +719,11 @@ route: string, strategy?: GroupKind | null, /** * `route` | `deny` | `no_match` | `unavailable`(选中的上游都服务不了, - * 见 `skipped`)| `intercepted` | `passthrough` + * 见 `skipped`)| `intercepted` * - * **后两个说的是这个请求压根没到规则那一层。**客户端自己发的辅助 - * 请求先过 `client_probes`:本地应答的一个字节都不出本机,原样放行的 - * 直接转发 —— 两种情况下 `trace` 都是空的,因为确实一条规则都没求值。 + * **`intercepted` 说的是这个请求压根没到规则那一层。**客户端自己发的辅助 + * 请求先过 `client_probes`:本地应答的一个字节都不出本机,`trace` 是空的, + * 因为确实一条规则都没求值。转发的那几类照常走规则,和普通请求一样。 */ outcome: DryRunOutcome, /** @@ -2501,14 +2501,13 @@ export type ProbeClass = "health_check" | "warmup" | "titling" | "topic_detect" /** * 一类辅助请求怎么处理。 */ -export type ProbeMode = "intercept" | "route" | "passthrough"; +export type ProbeMode = "intercept" | "forward"; /** * 一类客户端辅助请求的处置。 * - * **这一段以前在界面上完全不存在,而它的缺席是连锁的**:路由条件 - * `when.intent` 只有在对应那一类被配成 `route` 时才可能命中,所以 - * 界面上那些写了 `intent` 的规则永远不会生效,而用户无从知道为什么。 + * 路由条件 `when.intent` 只对 `forward` 的类别命中 —— `intercept` 的一个字节都不出 + * 本机,到不了规则那一层。 */ export type ProbeView = { /** @@ -2516,7 +2515,7 @@ export type ProbeView = { */ id: ProbeClass, /** - * `intercept` / `route` / `passthrough` + * `intercept` / `forward` */ mode: ProbeMode, }; @@ -3165,14 +3164,7 @@ export type RouteSave = { route: RouteInput, base_version?: string | null, * 原来用它、不在列表里的改用默认路由,和路由本身在同一个版本里写入。 * 不给就不动密钥的选择 */ -keys?: Array | null, -/** - * 和路由一起写入:把这几类客户端辅助请求设为「交给路由」。 - * - * **规则里的辅助请求条件只对交给路由的类别生效** —— 其余类别的请求在 - * 进路由之前就被本地应答或原样放行,不带类别标记,那个条件永远不满足 - */ -route_probes?: Array, }; +keys?: Array | null, }; /** * 一段时间里各条路由走了多少请求、各条规则命中了多少,以及这些数从哪一刻起是全的 diff --git a/src/i18n/core.zh.json b/src/i18n/core.zh.json index fc8d322e..1020d265 100644 --- a/src/i18n/core.zh.json +++ b/src/i18n/core.zh.json @@ -526,7 +526,6 @@ "control.sheet_in_use": "价目表「{sheet}」仍被上游{upstreams!names}使用,请先解除关联再删除。", "control.default_route_cannot_delete": "路由「{route}」是默认路由,不能删除。请先将其他路由设为默认路由。", "control.reassign_to_deleted_route": "网关密钥不能改用正在删除的路由「{route}」。", - "control.unknown_probe_class": "没有「{class}」这一类辅助请求。", "control.builtin_group_fixed": "每个上游的内置策略组随上游列表自动生成,不能编辑或删除。", "control.route.duplicate_rule": "路由中有两条名为「{rule}」的规则。", "control.rule.condition_no_value": "规则「{rule}」:条件 {field} 没有值。", diff --git a/src/routing/DryRunDialog.i18n.ts b/src/routing/DryRunDialog.i18n.ts index 385fd9a4..ac4bf1b6 100644 --- a/src/routing/DryRunDialog.i18n.ts +++ b/src/routing/DryRunDialog.i18n.ts @@ -35,12 +35,10 @@ export const dryRunText = messages( denied: "拒绝", unavailable: "选中的上游均无法服务此请求,请求将返回错误", noMatch: "无规则命中,请求将返回错误", - // 这两类请求在规则之前就有了去向 —— 说清它去了哪儿,以及怎么让它走规则 + // 本地应答的请求在规则之前就有了去向 —— 说清它去了哪儿,以及怎么让它走规则 intercepted: "本地应答", interceptedWhat: "网关直接应答这一类请求,不发送到上游,也不产生费用。路由规则不参与。", - passedThrough: "原样放行", - passedThroughWhat: "作为普通请求转发,不经过路由规则。", - howToRoute: "要让它按规则走,在「辅助请求」中将这一类改为「交给路由」。", + howToRoute: "要让它按规则走,在「辅助请求」中将这一类改为「转发」。", decides: "命中 · 决定去向", applies: "命中 · 应用改写参数或安全要求", decidedBy: (n: number) => `命中 · 去向已由第 ${n} 条决定`, @@ -86,9 +84,7 @@ export const dryRunText = messages( intercepted: "Answered locally", interceptedWhat: "The gateway answers this kind directly. Nothing is sent upstream and no cost is incurred. The routing rules take no part.", - passedThrough: "Passed through", - passedThroughWhat: "Forwarded as an ordinary request, without going through the routing rules.", - howToRoute: "To have the rules decide, set this kind to “Use routing” under Auxiliary.", + howToRoute: "To have the rules decide, set this kind to “Forward” under Auxiliary.", decides: "Matched · decides the destination", applies: "Matched · applies parameter rewrites or security requirements", decidedBy: (n: number) => `Matched · destination already decided by rule ${n}`, diff --git a/src/routing/DryRunDialog.tsx b/src/routing/DryRunDialog.tsx index ad5d569d..6fb9beb7 100644 --- a/src/routing/DryRunDialog.tsx +++ b/src/routing/DryRunDialog.tsx @@ -338,7 +338,6 @@ function verdictTone(r: DryRunResult): StatusTone { case "route": return "ok"; case "intercepted": - case "passthrough": return "idle"; default: return "error"; @@ -368,7 +367,7 @@ function Result({ * **那时下面每一格都不适用** —— 路由、候选、改写说的都是规则走完之后 * 的事,而一条规则都没走。留着它们只会让人以为它真的去了那儿。 */ - const short = r.outcome === "intercepted" || r.outcome === "passthrough"; + const short = r.outcome === "intercepted"; const target = r.outcome === "route" ? (r.via_group ?? r.candidates[0] ?? null) : null; // 这一趟经过的路:密钥 → 路由 → 规则 → 去向。和路由图同一套标志 @@ -427,7 +426,7 @@ function Result({ {r.outcome === "deny" && r.reason &&

{t.reason(r.reason)}

} {short && (
- {r.outcome === "intercepted" ? t.interceptedWhat : t.passedThroughWhat} + {t.interceptedWhat} {t.howToRoute}
)} @@ -577,8 +576,6 @@ function headline(r: DryRunResult): string { return m.unavailable; case "intercepted": return m.intercepted; - case "passthrough": - return m.passedThrough; default: return m.noMatch; } diff --git a/src/routing/ProbesTab.i18n.ts b/src/routing/ProbesTab.i18n.ts index 709470e8..5b748465 100644 --- a/src/routing/ProbesTab.i18n.ts +++ b/src/routing/ProbesTab.i18n.ts @@ -6,23 +6,18 @@ export const probesTabText = messages( "客户端自动发起的辅助请求,不由用户操作触发,同样产生费用。这一层先过,剩下的才轮到规则。", intercept: "本地应答", interceptWhat: "由网关直接应答,不发送到上游,不产生费用。", - passthrough: "原样放行", - passthroughWhat: "作为普通请求转发,按上游计费方式产生费用。", - routed: "交给路由", - routedWhat: "按「路由」标签里的规则转发,按转发到的上游产生费用,可分流至费用更低的上游。", - ruleHint: "规则里的「辅助请求」条件只对设为「交给路由」的类别成立。", + forward: "转发", + forwardWhat: "和普通请求一样按路由规则转发,按转发到的上游产生费用。", + ruleHint: "规则可用「辅助请求」条件匹配设为转发的类别,例如分流至费用更低的上游。", }, { intro: "Auxiliary requests that clients send on their own, without any user action. They incur costs as well, and they pass through here before any rule is evaluated.", intercept: "Answer locally", interceptWhat: "The gateway answers directly. Nothing is sent upstream and no cost is incurred.", - passthrough: "Pass through", - passthroughWhat: "Forwarded as an ordinary request, with costs according to the upstream's billing.", - routed: "Use routing", - routedWhat: - "Forwarded by the rules under the Routes tab, with costs according to the upstream it goes to; the rules can send it to a lower-cost upstream.", + forward: "Forward", + forwardWhat: "Forwarded by the routing rules like any other request, with costs according to the upstream it goes to.", ruleHint: - "The “Auxiliary request” condition in a rule only holds for the kinds set to “Use routing”.", + "Rules can match the kinds set to “Forward” with the “Auxiliary request” condition, for example to send them to a lower-cost upstream.", }, ); diff --git a/src/routing/ProbesTab.tsx b/src/routing/ProbesTab.tsx index d086156e..388f9908 100644 --- a/src/routing/ProbesTab.tsx +++ b/src/routing/ProbesTab.tsx @@ -10,11 +10,11 @@ import type { Overview, ProbeMode } from "@/types"; import { probesTabText } from "./ProbesTab.i18n"; /** - * 客户端自己发的辅助请求,以及每一类怎么处理。 + * 客户端自己发的辅助请求,以及每一类怎么处理:本地应答,或者转发。 * - * **它属于路由,不属于网关。**三档里有一档就叫「交给路由」,而规则的 - * `intent` 条件只对那一档成立 —— 写规则的地方和决定它能不能命中的地方 - * 隔着一个导航项时,规则会静悄悄地永远不命中。 + * **它属于路由,不属于网关。**转发的那几类带着类别走规则,规则的 `intent` + * 条件只对它们成立 —— 写规则的地方和决定它能不能命中的地方隔着一个导航项 + * 时,规则会静悄悄地永远不命中。 * * 换档是一次配置写入:**先按新档画**,写入期间那一类旁边转圈、分段控件失效; * 失败时拨回原档并报错。成功不弹提示 —— 分段控件已经停在新档上了。 @@ -40,8 +40,7 @@ export function ProbesTab({ ov }: { ov: Overview }) { const modes: { id: ProbeMode; label: string; what: string }[] = [ { id: "intercept", label: t.intercept, what: t.interceptWhat }, - { id: "passthrough", label: t.passthrough, what: t.passthroughWhat }, - { id: "route", label: t.routed, what: t.routedWhat }, + { id: "forward", label: t.forward, what: t.forwardWhat }, ]; async function set(id: string, mode: ProbeMode) { diff --git a/src/routing/RouteDialog.tsx b/src/routing/RouteDialog.tsx index a620fd7a..f025febc 100644 --- a/src/routing/RouteDialog.tsx +++ b/src/routing/RouteDialog.tsx @@ -121,13 +121,6 @@ export function RouteDialog({ const [rules, setRules] = useState(() => source ? source.rules.map(draftFromView) : [{ ...blankRule(ALL_UPSTREAMS), name: t.catchAllName }], ); - /** - * 每条规则保存时要一并「交给路由」的辅助请求类别,按规则的 `key` 记。**保存那条规则时 - * 整个换掉**:重开之后取消了勾选、去掉了那个条件,都要算数;删掉的规则不算。以前这里 - * 只加不减,取消勾选之后保存路由,那几类照样被改成交给路由 —— 连通性检查、预热从 - * 网关本地应答变成发给付费的上游 - */ - const [probes, setProbes] = useState>(() => new Map()); /** * 保存时带的版本号:**草稿起步的那一版**,不是保存那一刻的。 * @@ -177,7 +170,6 @@ export function RouteDialog({ base_version: base, // 默认路由的使用者是「没指定路由的密钥」,这里不改 keys: isDefault ? undefined : keys, - route_probes: [...new Set(rules.flatMap((r) => probes.get(r.key) ?? []))], }; if (mode.kind === "edit") await api.updateRoute(mode.name, save); else await api.createRoute(save); @@ -202,9 +194,6 @@ export function RouteDialog({ onSelect: () => { const c = { ...copyDraft(rules[i]!), name: uniqueName(rt.copyName(rules[i]!.name), rules) }; setRules((r) => [...r.slice(0, i + 1), c, ...r.slice(i + 1)]); - // 复制出来的那条带着同样的条件,也带着「交给路由」的那个选择 - const chosen = probes.get(rules[i]!.key); - if (chosen) setProbes((m) => new Map(m).set(c.key, chosen)); }, }, { kind: "sep" }, @@ -460,13 +449,12 @@ export function RouteDialog({ onChanged(); }} onClose={() => setEditing(null)} - onSave={(d, routeProbes) => { + onSave={(d) => { setRules((r) => editing.index == null ? [...r.slice(0, editing.at), d, ...r.slice(editing.at)] : r.map((x, j) => (j === editing.index ? d : x)), ); - setProbes((m) => new Map(m).set(d.key, routeProbes)); setEditing(null); }} /> diff --git a/src/routing/RuleDialog.i18n.ts b/src/routing/RuleDialog.i18n.ts index 5b0b77c7..1f844ec5 100644 --- a/src/routing/RuleDialog.i18n.ts +++ b/src/routing/RuleDialog.i18n.ts @@ -8,8 +8,8 @@ export const ruleDialogText = messages( namePlaceholder: "例如 长上下文", conditions: "条件", allOf: "同时满足以下条件", - unrouted: (probes: string[]) => `${probes.join("、")}当前未设为交给路由,此条件不会满足。`, - routeProbes: "保存时将这些类别改为交给路由", + intercepted: (probes: string[]) => `${probes.join("、")}当前由网关本地应答,不会进入路由,此条件对其不成立。`, + toForward: "可在「辅助请求」中改为转发。", onMatch: "命中后", forward: "转发", forwardTo: "转发至", @@ -49,9 +49,9 @@ export const ruleDialogText = messages( namePlaceholder: "e.g. Long context", conditions: "Conditions", allOf: "All of the following must be met", - unrouted: (probes: string[]) => - `${andList(probes)} ${probes.length === 1 ? "is" : "are"} not set to be routed, so this condition is never met.`, - routeProbes: "Set these categories to be routed on save", + intercepted: (probes: string[]) => + `${andList(probes)} ${probes.length === 1 ? "is" : "are"} answered locally by the gateway and never ${probes.length === 1 ? "reaches" : "reach"} routing, so this condition never holds for ${probes.length === 1 ? "it" : "them"}.`, + toForward: "This can be changed to “Forward” under Auxiliary.", onMatch: "On match", forward: "Forward", forwardTo: "Forward to", diff --git a/src/routing/RuleDialog.tsx b/src/routing/RuleDialog.tsx index a66d9715..d6c37c6f 100644 --- a/src/routing/RuleDialog.tsx +++ b/src/routing/RuleDialog.tsx @@ -2,7 +2,6 @@ import { useMemo, useState, type ReactNode } from "react"; import { ChevronRightIcon, PlusIcon } from "lucide-react"; import { Banner } from "@/ui/banner"; import { Button } from "@/ui/button"; -import { Checkbox } from "@/ui/checkbox"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/ui/collapsible"; import { Dialog, @@ -19,7 +18,6 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/ui/dropdown-menu"; -import { Field, FieldLabel } from "@/ui/field"; import { Input } from "@/ui/input"; import { NativeSelect, NativeSelectOptGroup, NativeSelectOption } from "@/ui/native-select"; import { cn } from "@/lib/utils"; @@ -36,7 +34,6 @@ import { TargetIcon } from "./parts"; import { COND_FIELDS, DIALECTS, - PROBE_IDS, addOnsText, blankCondition, compareOps, @@ -88,8 +85,7 @@ export function RuleDialog({ /** 在这里新建了策略组:外面要重读概览,路由对话框接着用写完的版本 `version` */ onChanged: (version: string) => void; onClose: () => void; - /** `routeProbes`:保存路由时一并设为「交给路由」的辅助请求类别 */ - onSave: (rule: RuleDraft, routeProbes: string[]) => void; + onSave: (rule: RuleDraft) => void; }) { const t = useText(ruleDialogText); const rt = useText(routingText); @@ -99,21 +95,22 @@ export function RuleDialog({ const [rewriteOpen, setRewriteOpen] = useState( () => initial.model !== "" || initial.maxTokens !== "" || initial.thinking !== "keep", ); - const [routeProbes, setRouteProbes] = useState(null); const [newGroup, setNewGroup] = useState(false); const modelIds = useMemo(() => models.map((m) => m.id), [models]); const phaseTwo = isPhaseTwo(d); const problem = ruleProblem(d, takenNames); - // 辅助请求条件里还没交给路由的类别:不交给路由,这个条件永远不满足 + /** + * 辅助请求条件里点名的、设为本地应答的类别:它们到不了路由,这个条件对它们永远不成立。 + * 选了「任一辅助请求」就不提 —— 那是在说「转发的那几类里随便哪一类」,本地应答的连通性 + * 检查、预热本来就不在里面 + */ const intent = d.conditions.find((c) => c.field === "intent"); const anyProbe = intent?.values.includes("assistant_internal") ?? false; - const unrouted = (anyProbe ? PROBE_IDS : (intent?.values ?? [])).filter( - (id) => ov.client_probes.find((p) => p.id === id)?.mode !== "route", - ); - // 「任一辅助请求」时不默认勾选:连通性检查和预热原本由网关本地应答,改为交给路由会产生费用 - const routing = routeProbes ?? !anyProbe; + const intercepted = anyProbe + ? [] + : (intent?.values ?? []).filter((id) => ov.client_probes.find((p) => p.id === id)?.mode === "intercept"); const rewriteSummary = addOnsText(d); @@ -160,17 +157,13 @@ export function RuleDialog({ onRemove={() => set({ conditions: d.conditions.filter((_, j) => j !== i) })} /> ))} - 0} title={t.unrouted(unrouted.map(probeLabel))}> - - setRouteProbes(v === true)} - /> - - {t.routeProbes} - - + 0} + title={t.intercepted(intercepted.map(probeLabel))} + > + {t.toForward} c.field)} @@ -291,7 +284,7 @@ export function RuleDialog({ -