Skip to content

Commit 70745d8

Browse files
committed
feat: unify model readiness, pin language catalog, and improve postprocess fallback
- Introduce IModelReadinessService / ModelNotReadyException for centralized model availability checks - Integrate readiness preflight into TranslationPipeline before translation and post-processing - Add fixed ILanguageCatalog to decouple UI language selection from model installation state - Graceful session-scoped fallback when post-processing model is missing (no repeated error noise) - Filter Active Model dropdown to show only installed translation models - Remove FastText from wizard required downloads - Unify QwenModelHost model path resolution via IModelManager - Full i18n for all new UI strings (en-US, zh-CN) - Sync delta specs to main specs and archive both completed changes Made-with: Cursor
1 parent d5443a5 commit 70745d8

64 files changed

Lines changed: 2129 additions & 466 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-03-09
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## Context
2+
3+
当前桌面端语言选择存在两类耦合:
4+
5+
1. **数据源耦合**`Settings``Overlay` 语言列表来自引擎声明(`ITranslationEngine.SupportedLanguages`),向导又维护独立列表,导致来源不一致。
6+
2. **时序耦合**`OverlayViewModel` 在翻译前主动调用 `SupportsLanguagePair` 做预判,用户在未尝试翻译时就被阻断,且与“按需下载模型”的产品路径冲突。
7+
8+
本次变更目标是把“语言选择”与“模型支持/安装状态”解耦:先允许选择与尝试翻译,失败时再通过错误路径引导用户下载模型。
9+
10+
## Goals / Non-Goals
11+
12+
**Goals:**
13+
- 建立固定语言目录,作为设置页、浮窗、向导的统一选择来源。
14+
- 移除翻译前主动支持性拦截(`SupportsLanguagePair` 预检)。
15+
- 保持现有设置结构、模型下载入口与翻译引擎接口兼容。
16+
- 保证 UI 行为一致:各入口可选语言与顺序一致。
17+
18+
**Non-Goals:**
19+
- 不修改 Marian/Qwen 模型注册规则与下载协议。
20+
- 不新增自动下载策略(仍由用户自行下载所需模型)。
21+
- 不重构 `ITranslationEngine` 接口或核心推理实现。
22+
23+
## Decisions
24+
25+
### D1: 引入固定语言目录服务(Desktop 层)
26+
27+
**Decision**: 在 `LiveLingo.Desktop` 引入统一语言目录(例如 `ILanguageCatalog` + `LanguageCatalog`),提供稳定 `IReadOnlyList<LanguageInfo>`
28+
29+
**Rationale**:
30+
- 语言选择属于 UI/产品策略,而非推理引擎能力声明。
31+
- 统一目录可消除向导/设置/浮窗三处重复定义与漂移。
32+
33+
**Alternatives**:
34+
- 继续复用 `ITranslationEngine.SupportedLanguages`:会继续把 UI 选择与当前引擎实现耦合,不满足“固定语言”目标。
35+
- 在每个 ViewModel 内各自维护常量列表:会扩大重复和不一致风险。
36+
37+
### D2: 语言选择不再按模型支持性动态收缩
38+
39+
**Decision**: 语言下拉仅由固定目录决定,不按 `SupportsLanguagePair`、已安装模型、已缓存 session 进行禁用或过滤。
40+
41+
**Rationale**:
42+
- 符合“先选语言、后决策下载模型”的用户路径。
43+
- UI 反馈更稳定,不因运行时环境变化导致选项抖动。
44+
45+
### D3: Overlay 去除翻译前主动支持性预检
46+
47+
**Decision**: 移除 `OverlayViewModel``SupportsLanguagePair` 早期返回逻辑,直接执行 pipeline。
48+
49+
**Rationale**:
50+
- 避免过早阻断,确保错误由翻译流程统一产生。
51+
- 失败时可复用现有错误处理(模型缺失/不支持)并引导下载。
52+
53+
**Alternatives**:
54+
- 保留预检并仅改文案:仍然是“前置阻断”,与目标冲突。
55+
56+
### D4: DI 注册策略
57+
58+
**Decision**: 在应用启动 DI 中注册固定语言目录服务为 `Singleton`,并注入到 `SetupWizardViewModel` / `SettingsViewModel` / `OverlayViewModel`
59+
60+
**Rationale**:
61+
- 目录数据只读且全局共享,单例成本最低。
62+
- 与现有 MVVM + DI 结构一致。
63+
64+
Dependency graph (logical):
65+
- `SettingsViewModel` -> `ILanguageCatalog`
66+
- `OverlayViewModel` -> `ILanguageCatalog`
67+
- `SetupWizardViewModel` -> `ILanguageCatalog`
68+
- `ITranslationEngine` 保持独立,仅用于翻译执行
69+
70+
## Risks / Trade-offs
71+
72+
- **[Risk] 用户更容易选择到当前未安装模型对应语对** -> **Mitigation**: 保持明确错误提示与模型下载入口,文案强调“可在模型页下载所需模型”。
73+
- **[Risk] 固定目录与未来引擎真实能力偏差** -> **Mitigation**: 固定目录由产品策略维护;引擎失败路径保持权威并可观测。
74+
- **[Trade-off] 去掉预检后失败在后置阶段出现** -> **Mitigation**: 统一错误文案,减少“为何不能选”的困惑。
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Why
2+
3+
当前语言选择入口依赖引擎声明或即时支持性判断,用户会在设置/浮窗/向导中看到被动收缩的可选项,且在未下载模型时被过早阻断。需要统一为固定语言目录,先允许用户完成选择与翻译尝试,再由用户自行决定下载对应模型。
4+
5+
## What Changes
6+
7+
- 将所有语言选择 UI(设置页、浮窗、向导)统一切换到固定语言目录,不再从引擎动态读取并收缩可选语言。
8+
- 移除翻译前的主动 `SupportsLanguagePair` 阻断校验,改为直接走翻译流程;若模型缺失/不支持,由翻译结果错误反馈引导用户到模型管理下载。
9+
- 统一错误提示语义:不再“提前判不支持”,而是“翻译失败时给出可操作提示(下载模型)”。
10+
- 保持现有配置结构与模型管理入口不变,避免破坏现有用户设置与流程。
11+
12+
## Capabilities
13+
14+
### New Capabilities
15+
16+
- `fixed-language-catalog`: 定义并复用固定语言目录作为 UI 语言选择唯一来源,与模型安装状态解耦。
17+
18+
### Modified Capabilities
19+
20+
- `language-dropdown-ui`: 由“来源于 `ITranslationEngine.SupportedLanguages`”调整为“来源于固定语言目录”;不再因当前模型支持性动态过滤。
21+
22+
## Impact
23+
24+
- Affected code:
25+
- `src/LiveLingo.Desktop/ViewModels/SettingsViewModel.cs`
26+
- `src/LiveLingo.Desktop/ViewModels/OverlayViewModel.cs`
27+
- `src/LiveLingo.Desktop/ViewModels/SetupWizardViewModel.cs`
28+
- `src/LiveLingo.Desktop/Views/*.axaml`
29+
- Behavior impact:
30+
- 用户可在所有入口始终看到一致语言选项;
31+
- 不支持语对不再被预检拦截,失败时由错误反馈与模型下载入口承接。
32+
- Risks:
33+
- 失败路径触发频率上升(可接受);需确保错误提示可理解且可操作。
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Desktop fixed language catalog
4+
5+
Desktop layer SHALL define a centralized fixed language catalog used by UI selection flows. The catalog SHALL expose at least 10 language entries: `zh`, `en`, `ja`, `ko`, `fr`, `de`, `es`, `ru`, `ar`, `pt`, each with stable `LanguageInfo(Code, DisplayName)` values.
6+
7+
```csharp
8+
public interface ILanguageCatalog
9+
{
10+
IReadOnlyList<LanguageInfo> All { get; }
11+
}
12+
```
13+
14+
#### Scenario: Catalog returns stable language set
15+
- **WHEN** `ILanguageCatalog.All` is read
16+
- **THEN** it SHALL return the fixed language list in deterministic order with no runtime filtering by model state
17+
18+
### Requirement: UI language selectors source from catalog
19+
20+
`SetupWizardViewModel`, `SettingsViewModel`, and `OverlayViewModel` SHALL source selectable translation languages from `ILanguageCatalog.All` instead of engine-declared capabilities.
21+
22+
#### Scenario: Same options across wizard, settings, and overlay
23+
- **WHEN** user opens setup wizard, settings translation tab, and overlay picker
24+
- **THEN** all three selectors SHALL present the same language set from the fixed catalog
25+
26+
### Requirement: Language selection decouples from model support checks
27+
28+
Language selection SHALL remain available regardless of model installation/support status. The system MUST NOT disable or hide language options based on `SupportsLanguagePair` or installed model list.
29+
30+
#### Scenario: Uninstalled pair still selectable
31+
- **WHEN** user selects a language pair whose model is not installed
32+
- **THEN** selection SHALL be accepted and persisted, and subsequent translation failure SHALL be handled by normal error flow
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: Settings source language uses ComboBox
4+
5+
Settings 页面的 DefaultSourceLanguage 输入控件 SHALL 使用 `ComboBox`,数据源来自固定语言目录(`ILanguageCatalog.All`),而非 `ITranslationEngine.SupportedLanguages`
6+
7+
#### Scenario: Source language shows dropdown with fixed languages
8+
- **WHEN** user opens Settings window
9+
- **THEN** source语言下拉 SHALL 显示固定目录中的所有语言
10+
11+
#### Scenario: Source language selection persists
12+
- **WHEN** user selects "中文" from source language dropdown and saves
13+
- **THEN** `UserSettings.Translation.DefaultSourceLanguage` SHALL be set to "zh"
14+
15+
### Requirement: Settings target language uses ComboBox
16+
17+
Settings 页面的 DefaultTargetLanguage 输入控件 SHALL 使用 `ComboBox`,数据源同样来自固定语言目录(`ILanguageCatalog.All`)。
18+
19+
#### Scenario: Target language shows dropdown with fixed languages
20+
- **WHEN** user opens Settings window
21+
- **THEN** target语言下拉 SHALL 显示固定目录中的所有语言
22+
23+
#### Scenario: Target language selection persists
24+
- **WHEN** user selects "English" from target language dropdown and saves
25+
- **THEN** `UserSettings.Translation.DefaultTargetLanguage` SHALL be set to "en"
26+
27+
### Requirement: SettingsViewModel exposes language list from engine
28+
29+
`SettingsViewModel` SHALL expose `AvailableLanguages` property of type `IReadOnlyList<LanguageInfo>` sourced from固定语言目录(`ILanguageCatalog.All`),不再依赖 `ITranslationEngine.SupportedLanguages` 作为 UI 选择来源。
30+
31+
#### Scenario: ViewModel provides fixed language list for binding
32+
- **WHEN** `SettingsViewModel` is constructed
33+
- **THEN** `AvailableLanguages` SHALL be non-empty and match the fixed language catalog
34+
35+
### Requirement: ComboBox displays language name and stores code
36+
37+
`ComboBox` SHALL display `LanguageInfo.DisplayName` to the user and bind selected value to the language code string.
38+
39+
#### Scenario: User sees friendly names but code is stored
40+
- **WHEN** dropdown shows "日本語"
41+
- **THEN** selecting it SHALL set the backing property to "ja"
42+
43+
### Requirement: Overlay available languages from engine
44+
45+
`OverlayViewModel` SHALL source its available languages from固定语言目录(`ILanguageCatalog.All`),而非硬编码静态列表或引擎支持列表;同时,目标语言切换后 MUST 直接触发翻译流程,且 MUST NOT 在翻译前通过 `SupportsLanguagePair` 主动阻断。
46+
47+
#### Scenario: Overlay language cycling uses fixed catalog
48+
- **WHEN** user clicks target language in overlay to cycle
49+
- **THEN** it SHALL cycle through languages returned by the fixed language catalog
50+
51+
#### Scenario: Unsupported pair is not pre-blocked
52+
- **WHEN** user selects a currently不支持/未安装模型的语对并输入文本
53+
- **THEN** overlay SHALL still invoke pipeline translation and handle failure via translation error path
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## 1. 固定语言目录基础设施
2+
3+
- [x] 1.1 新增 `ILanguageCatalog`/`LanguageCatalog`,定义固定语言清单与稳定顺序(zh/en/ja/ko/fr/de/es/ru/ar/pt);AC: 能在单测中断言数量、顺序与代码值一致
4+
- [x] 1.2 在应用 DI 注册语言目录单例并补充构造注入路径;AC: `SetupWizardViewModel``SettingsViewModel``OverlayViewModel` 均可解析到目录实例
5+
6+
## 2. ViewModel 行为解耦改造
7+
8+
- [x] 2.1 `SetupWizardViewModel` 改为使用目录语言列表,移除本地重复语言定义;AC: 向导语言下拉来源仅为目录
9+
- [x] 2.2 `SettingsViewModel` 改为使用目录语言列表,移除 UI 语言选择对 `ITranslationEngine.SupportedLanguages` 的依赖;AC: 设置页源/目标语言可见项与目录完全一致
10+
- [x] 2.3 `OverlayViewModel` 改为使用目录语言列表并移除翻译前 `SupportsLanguagePair` 主动阻断;AC: 不支持语对时仍会调用 pipeline,错误走既有失败路径
11+
12+
## 3. 视图绑定与文案一致性
13+
14+
- [x] 3.1 更新 Wizard/Settings/Overlay 绑定,确保都消费对应 ViewModel 的目录语言数据与本地化文案;AC: 三处界面可选语言一致且不随模型状态变化
15+
- [x] 3.2 校准“不支持/未下载模型”错误提示文案为可操作引导(去设置下载模型);AC: 失败提示不再出现前置支持性拦截语义
16+
17+
## 4. 验证与回归
18+
19+
- [x] 4.1 新增/更新单测:目录服务、ViewModel 数据源、Overlay 非预检行为;AC: 覆盖“可选语言固定”“不支持语对仍触发翻译尝试”两个核心场景
20+
- [x] 4.2 执行桌面端受影响测试集并修复回归;AC: 相关测试全部通过且无新增 lint 错误
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-03-09
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# unify-model-readiness-and-postprocess-flow
2+
3+
Unify model readiness checks, decouple post-processing semantics, and remove FastText from required downloads.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
## Context
2+
3+
当前实现中,模型就绪逻辑分布在多个层级:
4+
5+
1. `MarianOnnxEngine` 内部按需 `EnsureModelAsync`(翻译模型)。
6+
2. `OverlayViewModel` 在调用 Pipeline 前检查 Qwen 是否已安装(后处理模型)。
7+
3. `QwenModelHost` 自身使用硬编码目录名解析模型路径。
8+
9+
这会造成以下问题:
10+
- 逻辑分散导致行为不一致(翻译模型与后处理模型的就绪判断路径不同)。
11+
- UI 层出现告警噪音(未启用后处理场景也容易出现误解)。
12+
- 路径约定重复(`ModelRegistry``QwenModelHost` 各自维护目录规则)。
13+
- Setup Wizard 仍下载 FastText,但默认检测链路基于 `ScriptBasedDetector`,存在“下载却未使用”。
14+
15+
## Goals / Non-Goals
16+
17+
**Goals:**
18+
- 在 Core 层统一模型就绪判定入口,并提供标准异常语义。
19+
- 将模型就绪检查集中到 Pipeline,移除 Overlay 预检分叉逻辑。
20+
- 统一 Qwen 模型路径解析到 `IModelManager.GetModelDirectory()`
21+
- 明确 UI 语义:`Active Model` 只对应翻译模型;后处理由 `Processing.DefaultMode` 控制。
22+
- 移除 FastText 作为向导必下模型,降低首次下载成本。
23+
24+
**Non-Goals:**
25+
- 不引入自动下载策略(仍由用户在 Models 页主动下载)。
26+
- 不重构 Marian/Qwen 推理实现本身(仅调整编排与就绪判断)。
27+
- 不修改现有设置文件结构(不新增 schema 迁移)。
28+
29+
## Decisions
30+
31+
### D1: 新增模型就绪服务(Core)
32+
33+
**Decision**: 新增 `IModelReadinessService`(Core),统一暴露模型就绪判断与按能力检查接口,例如:
34+
35+
```csharp
36+
public interface IModelReadinessService
37+
{
38+
bool IsInstalled(string modelId);
39+
void EnsureTranslationModelReady(string sourceLanguage, string targetLanguage);
40+
void EnsurePostProcessingModelReady();
41+
}
42+
```
43+
44+
并引入 `ModelNotReadyException`,携带 `ModelType``ModelId` 与推荐动作,供上层统一处理。
45+
46+
**Rationale**:
47+
- 消除 UI 层与 Core 层重复判断。
48+
- 使“模型缺失”成为可测试、可观测的领域错误,而非字符串约定。
49+
50+
**Alternatives**:
51+
- 继续在 `OverlayViewModel` 预检:职责继续分散,且易产生体验噪音。
52+
- 仅依赖底层 `FileNotFoundException`:语义不稳定,提示不可控。
53+
54+
### D2: Pipeline 统一前置检查
55+
56+
**Decision**: 在 `TranslationPipeline.ProcessAsync` 前置阶段统一调用 `IModelReadinessService`
57+
- 始终检查翻译模型就绪(按语言对)。
58+
-`request.PostProcessing != null` 时检查后处理模型就绪。
59+
60+
后处理模型未就绪时抛 `ModelNotReadyException(ModelType.PostProcessing, "qwen25-1.5b", ...)`
61+
62+
**Rationale**:
63+
- 由 Core 统一判定“是否能执行该请求”,减少 UI 推测。
64+
- 错误出口一致,便于 Overlay 做“提示 + 降级”。
65+
66+
### D3: Qwen 路径由 ModelManager 统一解析
67+
68+
**Decision**: `QwenModelHost` 不再拼接硬编码目录,改为通过 `IModelManager.GetModelDirectory(ModelRegistry.Qwen25_15B.Id)` 定位模型目录,再在目录下查找 `.gguf`
69+
70+
**Rationale**:
71+
- 路径规则单一来源,避免目录名漂移。
72+
-`ModelManager.ListInstalled()` 语义对齐。
73+
74+
### D4: Desktop 语义收敛与降级策略
75+
76+
**Decision**:
77+
- `SettingsViewModel``AvailableTranslationModels` 仅保留已安装 `ModelType.Translation`
78+
- Overlay 不再主动预检 Qwen;当捕获 `ModelNotReadyException(PostProcessing)` 时:
79+
1) 更新状态文案为可操作提示(去 Settings -> Models 下载);
80+
2) 自动降级为 translation-only 再执行一次请求(保留主链路可用性);
81+
3) 日志降级到 `Information/Debug`,避免误导性 `Warning` 噪音。
82+
83+
**Rationale**:
84+
- 让“翻译主链路可用”成为默认。
85+
- 后处理缺失不会误伤主流程,也不会制造高等级噪音。
86+
87+
### D5: FastText 从 required 集合移除
88+
89+
**Decision**: `ModelRegistry.RequiredModels``GetRequiredModelsForLanguagePair` 移除 `FastTextLid`,向导 Step 2 基线下载改为 Marian-only。
90+
91+
**Rationale**:
92+
- 当前默认检测实现未使用 FastText,必下策略与实际执行路径不一致。
93+
94+
## Risks / Trade-offs
95+
96+
- **[Risk] Overlay 降级重试可能导致一次额外请求延迟** -> **Mitigation**: 仅在捕获 `PostProcessing` 缺失异常时触发一次重试。
97+
- **[Risk] 现有测试依赖旧日志/异常文本** -> **Mitigation**: 用类型化异常与行为断言替代字符串断言。
98+
- **[Trade-off] FastText 不再必下后,未来若切回 FastText 检测需补充下载入口** -> **Mitigation**: 保留 `FastTextLid` 描述符但不纳入 required,后续可按能力再开启。
99+
100+
## Migration Plan
101+
102+
1. 引入 Core readiness 服务与异常类型,接入 DI。
103+
2. 改造 Pipeline 调用 readiness,完成单元测试。
104+
3. 改造 QwenModelHost 路径解析,验证缺模型与已安装路径。
105+
4. 改造 Overlay 捕获/降级行为与提示文案。
106+
5. 调整 ModelRegistry required 语义与 Setup Wizard 文案。
107+
6. 完成 Desktop/Core 回归测试并清理旧日志断言。
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
## Why
2+
3+
当前模型使用路径在 Core 与 Desktop 间职责分散:翻译模型由引擎按需确保,后处理模型由 Overlay 预检并记录告警,导致“未下载 Qwen 但出现噪音日志”的体验问题。同时,Setup Wizard 仍把 FastText 作为必下模型,但当前默认检测链路并未使用该模型,造成下载成本与认知负担。
4+
5+
## What Changes
6+
7+
- 在 Core 层新增统一模型就绪编排能力,集中翻译/后处理模型的就绪判断与异常语义。
8+
- 将 Pipeline 作为模型就绪检查入口,移除 Overlay 的分散预检逻辑,统一错误出口。
9+
- 统一 Qwen 模型路径解析方式:改为通过 `IModelManager.GetModelDirectory()` 获取,移除硬编码目录。
10+
- 收敛 Settings/Overlay 模型语义:`Active Model` 仅表达翻译模型;后处理仅由 `Processing.DefaultMode` 控制。
11+
- 将 FastText 从向导“必下模型”集合移除,仅保留翻译主链路所需 Marian 基线模型。
12+
13+
## Capabilities
14+
15+
### New Capabilities
16+
17+
- `model-readiness-orchestration`: Core 提供统一模型就绪服务与标准化异常,供 Pipeline/UI 一致消费。
18+
19+
### Modified Capabilities
20+
21+
- `translation-pipeline`: 从“仅编排翻译处理”扩展为“编排 + 模型就绪前置检查 + 统一错误语义”。
22+
- `qwen-model-host`: 模型目录解析由硬编码切换为 `IModelManager` 驱动。
23+
- `postprocess-ui`: 缺失后处理模型时改为可操作引导与翻译降级,不再由 UI 预检告警驱动体验。
24+
- `model-management`: 调整 required 模型语义(FastText 不再属于向导必下集)。
25+
- `setup-wizard`: Step 2 下载基线改为 Marian-only。
26+
27+
## Impact
28+
29+
- Affected code:
30+
- `src/LiveLingo.Core/Translation/TranslationPipeline.cs`
31+
- `src/LiveLingo.Core/ServiceCollectionExtensions.cs`
32+
- `src/LiveLingo.Core/Processing/QwenModelHost.cs`
33+
- `src/LiveLingo.Core/Models/ModelRegistry.cs`
34+
- `src/LiveLingo.Desktop/ViewModels/OverlayViewModel.cs`
35+
- `src/LiveLingo.Desktop/ViewModels/SettingsViewModel.cs`
36+
- `src/LiveLingo.Desktop/ViewModels/SetupWizardViewModel.cs`
37+
- `src/LiveLingo.Desktop/Resources/i18n/*.json`
38+
- Behavior impact:
39+
- 未启用后处理时,不再触发 Qwen 缺失噪音告警。
40+
- 启用后处理且 Qwen 缺失时,用户得到一致可操作提示并可继续 translation-only 主链路。
41+
- 首次向导下载体积与等待时间下降(移除 FastText 必下)。
42+
- Risks:
43+
- Pipeline 前置检查后,异常语义变化可能影响现有测试;需同步回归用例与日志断言。

0 commit comments

Comments
 (0)