mirror of
https://github.com/lobehub/lobehub.git
synced 2026-09-20 04:56:13 +08:00
fix/topic-switch-message-refresh
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
edbcbe3233 |
✨ feat(cli): record local hetero agent runs as execution snapshots (#18874)
* ✨ feat(cli): record local hetero agent runs as execution snapshots A heterogeneous agent run executed by the CLI left no durable record of itself: server-ingest runs persisted messages, standalone `lh hetero exec` runs persisted nothing, and neither produced the `ExecutionSnapshot` a native agent run writes. A process killed mid-run was simply gone. Record every run — server-ingest or standalone — into the same snapshot format the server already uses, under `~/.lobehub/traces`, so `lh trace op list` / `inspect` read local and production runs without branching. `HeteroTraceRecorder` folds the adapter's event stream into steps: `stream_start` … `step_complete{turn_metadata}` becomes a `call_llm` step carrying that turn's model and usage, `tool_start` … `tool_end` becomes its own `call_tool` step, and `step_complete{result_usage}` supplies the session grand total. Turns after the first open lazily, so adapters that emit `stream_start` once per session (Claude Code) and per turn segment alike. Writes are serialized and coalesced, and go straight through rather than re-reading the partial on every step as `appendStepToPartial` does. Three fixes in the tracing store that this depends on: - `savePartial` now writes atomically (temp file + rename). A partial exists precisely so a killed process can be recovered from it, and a plain `writeFile` interrupted by SIGKILL leaves truncated JSON — unparseable at exactly the moment it is needed. - `FileSnapshotStore` takes a `dirName`, so a host owning its own layout does not end up with a nested hidden `.agent-tracing/` inside it. - The `latest.json` symlink no longer fails the save when two runs race on it. `lh connect` sweeps the store on start: partials whose last step is older than six hours become `interrupted` snapshots (otherwise a crashed run — the one most worth finding — is the only one `list` cannot show), and snapshots past a fourteen-day window are deleted. Also isolates the CLI home in tests. Driving a command end to end previously wrote into the developer's real `~/.lobehub`, which this change would have turned into hundreds of stray trace files per test run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🐛 fix(cli): keep subagent work out of the main trace spine Two issues from review. Claude Code's Task tool emits the subagent's turns and tool calls on the SAME stream as the main agent's, tagged with a `subagent` peer field. The recorder consumed them as main-agent events, so the child's text was appended to the parent's turn, the child's usage closed that turn early, and the child's tools surfaced as top-level calls — corrupting the trace precisely for the runs that use Agent/Task. Skip subagent-scoped events: the parent's own `Task` step still records what the child returned, which mirrors how the native runtime keeps a sub-agent in its own child operation rather than the parent's. `lh trace op inspect`/`replay` resolved the default `latest` target from the CLI-home store alone, and each store answers `latest` with its own newest entry — so a developer who had run both `lh hetero exec` and a local server inspected a stale run while `lh trace op list`, which merges and sorts both stores, showed the newer one first. Compare the two stores for the default target; an explicit id still needs no comparison. Both are covered by tests that fail without the fix — the subagent one drives the real `ClaudeCodeAdapter` through a Task sequence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3280844f5d |
⚡️ perf(test): cut root suite from 397s to 287s, dedupe logger mocks, fix blind stream tests (#18804)
* ⚡️ perf(test): stop loading antd in the global vitest setup setupFiles re-evaluate per test file, so `import { theme } from 'antd'` — present only to set `theme.defaultConfig.hashed = false` — cost every one of the 1836 root-suite files ~596ms of module evaluation. Nothing asserts on unhashed antd class names, so the flag can go with it. Full root suite: 396.95s -> 283.88s wall, setup 2027s -> 256s, no test changes. Prebundling antd via `deps.optimizer` was measured as an alternative and rejected: a prebundled module resolves to the optimizer chunk while `vi.mock('<bare-specifier>')` registers on the bare id, so the mock silently stops applying (23 suites started reading the real implementation). Claude-Session: https://claude.ai/code/session_01K9fwqjBqMYMsYDddo9b4JN * ✅ test(desktop,cli): lift per-file logger mocks into the setup files 52 desktop suites and 38 CLI suites each carried an identical `vi.mock('…/utils/logger')` whose only job was silencing output — the desktop logger also pulls electron-log and `@/env` in at module scope. Register one stub per app instead: - desktop `__mocks__/setup.ts` stubs `createLogger` - new `apps/cli/tests/setup.ts` spreads the real module and replaces every `log` method with a spy, so suites that assert on log calls keep working Suites needing different behavior still override with their own `vi.mock`; `logger.test.ts` in each app opts out via `vi.unmock`. desktop 122/122 pass. CLI unit failures go 9 -> 8 (all pre-existing; `src/settings/index.test.ts` now passes). Claude-Session: https://claude.ai/code/session_01K9fwqjBqMYMsYDddo9b4JN * 🐛 fix(test): make model-runtime stream tests actually assert Three suites drove a stream end to end and then asserted only that something existed, so they stayed green against a broken implementation. - zhipu `handleStream`: 8 cases built tool_call chunks, read the whole stream into `chunks`, then asserted `expect(chunks).toBeDefined()`. Driving `instance.chat()` with a mocked `create` yields `choices: []`, so the transform under test never ran. Rewritten to call `params.chatCompletion.handleStream` directly and assert the emitted SSE `event: tool_calls` indices — 365 -> 147 lines, and the all-placeholders branch is now covered. - google `should handle text messages correctly`: the mock enqueued a bare string, which actually throws `Cannot use 'in' operator to search for 'promptFeedback'` once the body is read. Now enqueues a real candidates chunk and asserts the SSE text payload. - azureOpenai `should handle o1 series models without streaming` asserted only that `transformResponseToStream` ran, never that o1 forces `stream: false`. Each was mutation-verified: deleting the negative-index fix, the placeholder filter, the google thought filter, or the o1 stream override now fails these tests and previously did not. 186 files / 4650 tests pass. Claude-Session: https://claude.ai/code/session_01K9fwqjBqMYMsYDddo9b4JN |