Skip to content

feat(ai): show safe review progress - #187

Merged
cg8-5712 merged 1 commit into
mainfrom
feature/issue-186-ai-review-progress
Aug 5, 2026
Merged

cg8-5712 merged 1 commit into
mainfrom
feature/issue-186-ai-review-progress

Conversation

@cg8-5712

@cg8-5712 cg8-5712 commented Aug 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • 新功能

    • AI 问询画像生成和初始审核支持实时进度展示。
    • 新增排队、准备、生成、校验及规则回退等阶段提示。
    • 流式响应完成前可接收安全的进度事件,完成事件后获取最终结果。
    • 中断的流式响应将不再被视为有效审核结果。
  • 文档

    • 更新相关 API 与 OpenAPI 说明,明确 SSE 响应格式、事件约束及错误处理规则。
  • 测试

    • 增加进度事件顺序、最终结果及敏感信息不泄露的验证。

Copilot AI lite review requested due to automatic review settings August 5, 2026 10:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

问询画像生成和初始审核接口改为 SSE 响应。后端发送安全阶段事件。前端转发事件并显示排队、生成、校验和回退进度。测试覆盖事件顺序、最终结果和敏感内容隔离。

Changes

AI SSE 合约与后端事件

Layer / File(s) Summary
SSE 合约和后端事件
docs/API.md, docs/openapi.yaml, src/api/routes/intake_sessions.rs, tests/api/intake_session_confirm_post.rs
两个 AI 接口改为 text/event-stream。后端发送 queued、preparing、generating、validating 或 fallback 事件,最后发送 completed 事件。测试验证事件顺序和敏感内容不泄露。

客户端事件转发

Layer / File(s) Summary
SSE 监听器和接口传递
frontend/src/api/client.ts, frontend/src/api/intake.ts, frontend/src/api/client.test.ts
新增 SseEvent 和 SseEventListener。apiSseRequest 转发解析后的事件。问询 API 将监听器传递到底层请求。测试覆盖进度事件和最终结果。

前端进度展示

Layer / File(s) Summary
审核阶段状态和进度组件
frontend/src/components/AiReviewProgress.tsx, frontend/src/components/AiReviewProgress.test.tsx, frontend/src/pages/FamilyIntakeForm.tsx, frontend/src/pages/CaseWorkspacePage.tsx
新增四步审核进度组件和回退状态。问询表单及案件工作区根据 SSE 事件显示生成进度,并在完成或失败后清理状态。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

  • AG-Angui/Angui#174:本 PR 在相同的问询审核接口和工作流上扩展 SSE 进度事件。
  • AG-Angui/Angui#179:两者都修改 apiSseRequest 和问询 SSE 端点,以支持事件转发和消费。

Suggested reviewers: copilot

Sequence Diagram(s)

sequenceDiagram
  participant FamilyIntakeForm
  participant intakeApi
  participant IntakeRoute
  participant AiReviewProgress
  FamilyIntakeForm->>intakeApi: start AI review with event listener
  intakeApi->>IntakeRoute: send SSE request
  IntakeRoute-->>intakeApi: send progress event
  intakeApi-->>FamilyIntakeForm: forward parsed event
  FamilyIntakeForm->>AiReviewProgress: render current stage
  IntakeRoute-->>intakeApi: send completed event
  intakeApi-->>FamilyIntakeForm: return final review result
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了本次变更的主要内容,即展示 AI 审核过程中的安全进度。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-186-ai-review-progress

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 oasdiff (1.27.0)
docs/openapi.yaml

Error: failed to load revision spec from "docs/openapi.yaml": failed to unmarshal data: json error: invalid character 'o' looking for beginning of value, yaml error: error converting YAML to JSON: yaml: line 352: mapping values are not allowed in this context


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/openapi.yaml`:
- Line 352: 修复 OpenAPI YAML 中该 description 的语法:在包含 “SSE: optional” 的 description
值上使用引号或折叠块标量,确保整段描述可被 YAML 正确解析,并保留现有描述内容。

In `@tests/api/intake_session_confirm_post.rs`:
- Around line 298-316: Extend tests/api/intake_session_confirm_post.rs:298-316
to parse each SSE frame’s data payload, assert progress payloads contain only
the allowed stage field, reject unknown stages, and verify the final stage is
validating or fallback while preserving ordering and completion checks. Extend
frontend/src/api/client.test.ts:121-161 with an onEvent error-frame case,
asserting the listener receives the error event before the request rejects with
stream_failed.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d5de7f72-5b0c-4be2-9694-887089482305

📥 Commits

Reviewing files that changed from the base of the PR and between c691f8d and 92b86b0.

📒 Files selected for processing (11)
  • docs/API.md
  • docs/openapi.yaml
  • frontend/src/api/client.test.ts
  • frontend/src/api/client.ts
  • frontend/src/api/intake.ts
  • frontend/src/components/AiReviewProgress.test.tsx
  • frontend/src/components/AiReviewProgress.tsx
  • frontend/src/pages/CaseWorkspacePage.tsx
  • frontend/src/pages/FamilyIntakeForm.tsx
  • src/api/routes/intake_sessions.rs
  • tests/api/intake_session_confirm_post.rs

Comment thread docs/openapi.yaml
operationId: startIntakeAiInitialReview
summary: Start the family-visible AI initial review
description: The session creator submits the profile snapshot after first confirmation. The service validates model JSON and policy boundaries; the Gateway performs at most two total HTTP calls (one JSON repair or one compliant provider failover, never both), while unavailable providers, invalid output, and execution failures return a rule-based review fallback. This operation never creates a case.
description: The session creator submits the profile snapshot after first confirmation. The service validates model JSON and policy boundaries; the Gateway performs at most two total HTTP calls (one JSON repair or one compliant provider failover, never both), while unavailable providers, invalid output, and execution failures return a rule-based review fallback. This operation never creates a case. The response is SSE: optional safe `progress` events contain only a stage (`queued`, `preparing`, `generating`, `validating`, or `fallback`), and the final `completed` event contains the review result. Partial streams are not a valid review result.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
yamllint docs/openapi.yaml

Repository: AG-Angui/Angui

Length of output: 29805


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

path = Path("docs/openapi.yaml")
lines = path.read_text().splitlines()
line = lines[351]
print(f"line_352={line}")
print(f"sse_index={line.index('SSE: optional') if 'SSE: optional' in line else -1}")

try:
    import yaml
except ImportError:
    print("PyYAML unavailable")
else:
    for label, text in (
        ("current", path.read_text()),
        ("folded", "\n".join(
            lines[:351]
            + [
                "      description: >-",
                "        " + line.split("description: ", 1)[1],
            ]
            + lines[352:]
        ) + "\n"),
    ):
        try:
            yaml.safe_load(text)
            print(f"{label}=parse_ok")
        except Exception as exc:
            print(f"{label}=parse_error:{type(exc).__name__}:{exc}")
PY

Repository: AG-Angui/Angui

Length of output: 851


修复 OpenAPI YAML 语法。

docs/openapi.yaml:352 的未加引号 description 值包含 SSE: optional,导致 YAML 解析失败。请使用带引号的字符串或折叠块标量。

🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 352-352: syntax error: mapping values are not allowed here

(syntax)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/openapi.yaml` at line 352, 修复 OpenAPI YAML 中该 description 的语法:在包含 “SSE:
optional” 的 description 值上使用引号或折叠块标量,确保整段描述可被 YAML 正确解析,并保留现有描述内容。

Source: Linters/SAST tools

Comment on lines +298 to +316
let events = stream
.split("\n\n")
.filter_map(|frame| {
frame
.lines()
.find_map(|line| line.strip_prefix("event:").map(str::trim))
})
.collect::<Vec<_>>();

assert_eq!(events.first(), Some(&"started"));
assert!(
events
.windows(2)
.any(|pair| pair == ["started", "progress"])
);
assert!(events.iter().filter(|event| **event == "progress").count() >= 3);
assert_eq!(events.last(), Some(&"completed"));
assert!(!stream.contains("system_instruction"));
assert!(!stream.contains("raw_prompt"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

补充 SSE 失败和安全载荷的契约测试。

当前测试只验证成功事件名。它们无法发现未知阶段、progress 事件中的额外敏感字段,或 error 事件未回调的问题。

  • tests/api/intake_session_confirm_post.rs#L298-L316: 解析每个 SSE 帧的 data:。断言 progress 载荷只含允许的 stage,并断言阶段顺序及最终 validating 或 fallback 阶段。
  • frontend/src/api/client.test.ts#L121-L161: 增加带 onEvent 的 error 帧测试。断言监听器先收到该事件,然后请求以 stream_failed 拒绝。

As per path instructions, tests/**: Check that changed behavior has meaningful positive, negative, and boundary-case coverage. Do not request tests for style-only changes.

📍 Affects 2 files
  • tests/api/intake_session_confirm_post.rs#L298-L316 (this comment)
  • frontend/src/api/client.test.ts#L121-L161
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/api/intake_session_confirm_post.rs` around lines 298 - 316, Extend
tests/api/intake_session_confirm_post.rs:298-316 to parse each SSE frame’s data
payload, assert progress payloads contain only the allowed stage field, reject
unknown stages, and verify the final stage is validating or fallback while
preserving ordering and completion checks. Extend
frontend/src/api/client.test.ts:121-161 with an onEvent error-frame case,
asserting the listener receives the error event before the request rejects with
stream_failed.

Source: Path instructions

@cg8-5712
cg8-5712 merged commit a0ffed4 into main Aug 5, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants