Commit Graph

5 Commits

Author SHA1 Message Date
Chiguyong 0daa4d21b0 fix(review): avoid double broadcast when stream breaks mid-way
Code review finding (P2): when chat_stream successfully emits the first
chunk (broadcasted to UI) but then breaks during subsequent chunks, the
fallback path called gateway.chat() and broadcasted the FULL content
via _replay_stream — causing the UI to see partial streamed content
followed by duplicated full content.

Fix: if `total` already contains partial content from first_chunk,
return it directly without calling gateway.chat(). Partial content is
better than duplicated content. The non-streaming fallback only runs
when no chunks were broadcasted (total is empty).

Add test_stream_breaks_after_first_chunk_returns_partial covering this
edge case: first chunk yields "部分内容", second __anext__ raises
RuntimeError. Asserts result is the partial content, gateway.chat() is
never called, and broadcast_event is called exactly once (for the
first chunk only).
2026-07-06 15:53:27 +08:00
Chiguyong ed1e289785 fix(board): add first-chunk timeout to _stream_expert_speech
Commit 36b0296 changed expert speech generation from gateway.chat()
(non-streaming) to gateway.chat_stream() (streaming) for progressive
UI output. However, DashScope/DeepSeek via LiteLLM occasionally accept
the streaming request but never emit the first SSE chunk — the async
generator hangs indefinitely with no error and no timeout, freezing
the entire board.

Root cause: `async for chunk in gateway.chat_stream(...)` blocks
forever when the provider silently stalls. The moderator path
(gateway.chat) still works because it's synchronous — that's why
the moderator opening always succeeds but experts hang.

Fix: pull the first chunk via `__anext__()` wrapped in
`asyncio.wait_for(timeout=30s)`. If no chunk arrives within 30s,
close the stream and fall back to non-streaming gateway.chat() +
_replay_stream() (which splits the response into small chunks for
progressive UI rendering). This preserves the "逐个输出" UX while
guaranteeing the board never hangs on a stalled streaming provider.

Add 4 unit tests covering:
- Normal streaming works (existing path)
- First-chunk timeout → fallback to chat()
- Empty stream → fallback to chat()
- chat_stream raising → fallback to chat()

E2E verification: 5-expert board with DashScope completes in ~277s
(status=completed), all experts produce content, no hang.

Same branch as the earlier _handle_llm_gateway fix (commit d0fe661).
2026-07-06 15:35:15 +08:00
chiguyong 1599d193c7 test: fix async generator mock for U3 streaming orchestrator
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
U3 streaming refactor switched orchestrator from agent.execute() to
agent.execute_stream() (async gen), but tests still mocked execute().
AsyncMock() returns a coroutine lacking __aiter__, causing:
- 'async for' requires an object with __aiter__ method, got coroutine
- RuntimeWarning: coroutine was never awaited

Add shared helpers in tests/unit/experts/_helpers.py:
- make_chat_stream_mock: async gen for gateway.chat_stream
- make_execute_stream_mock: async gen yielding final_answer event
- make_execute_stream_raising_mock: async gen that raises (for failure tests)

Update 3 test files to use the helpers:
- test_team_orchestrator.py: _make_mock_expert, _make_mock_pool,
  failure tests (phase_failed, all_phases_fail, fallback_uses_lead,
  phase_failure_marks_dependents), assertion updates (execute_stream
  instead of execute), synthesizer warning cleanup
- test_pm_collaboration.py: _make_mock_expert, _make_mock_llm_gateway,
  collaboration/risk/rework assertions
- test_board_orchestrator.py: _make_mock_gateway (warning cleanup)

All 483 experts/ tests pass with 0 warnings.
2026-07-02 22:52:10 +08:00
chiguyong 78a7faa17b refactor: remove all emoji from agentkit
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
Replace emoji across codebase: YAML avatars -> first char, frontend banners -> Ant Design Vue components, CLI status -> OK/FAIL/WARN labels, terminal -> [WARN]/[OK]/[PENDING], Bitable DB default -> table, App.vue font cleanup, test fixtures -> first char letters. shell.avatar type upgraded to string | Component.
2026-07-02 01:33:28 +08:00
chiguyong dddcbd24e3 feat: 私董会讨论模式 + 回测集成 + WS持久化修复
私董会讨论模式 (Board Meeting Mode):
- BoardRouter: @board 前缀路由, 专家名验证, 模板回退
- BoardTeam: 讨论容器, 状态机 (FORMING->DISCUSSING->CONCLUDING->COMPLETED)
- BoardOrchestrator: 多轮自主循环讨论引擎, 主持人小结, 停止命令检测
- 9个预设名人专家 YAML (马斯克/贝佐斯/张小龙/芒格等)
- 前端 BoardStatusView 群聊式 UI + WebSocket 事件处理
- 后端 chat.py 集成 @board 路由到主聊天流程

回测集成:
- benchmark.py: 新增 board_meeting 维度 (18 tasks, 6 categories)
- benchmark_dataset.py: 新增 BOARD_BENCHMARKS (11 E2E cases)
- test_board_backtest.py: 66 个回测测试 (9 test classes)

Bug 修复:
- resolve_expert_configs: deep-copy 防止 is_lead 修改污染共享模板
- 所有专家名无效时回退到默认模板
- board_router: 非匹配路径 topic 未 strip
- benchmark_dataset: board-name-invalid-001 输入修正

WebSocket 持久化修复:
- chat.py: 三层防御机制确保任务结果不丢失
- chat store: 断线恢复逻辑

部署配置:
- Gitea Actions CI/CD workflow
- docker-compose.deploy.yaml 部署编排
- scripts/deploy.sh 自动化部署脚本

测试结果: 120 单元测试通过, 71 benchmark 测试 100% 通过, ruff 全部通过
2026-06-17 23:52:53 +08:00