feat(core): add Devin CLI SQLite integration for local token accounting - #196
feat(core): add Devin CLI SQLite integration for local token accounting#196vlln wants to merge 3 commits into
Conversation
Read assistant token metrics from the Devin CLI session store at ~/.local/share/devin/cli/sessions.db. The integration joins message_nodes with sessions, extracts the four metadata.metrics token buckets (input/output/cache_read/cache_creation) from assistant chat_message blobs, and maps them to TokenBreakdown. Twin child nodes sharing a message_id are deduplicated by (session_id, message_id) so each assistant turn is counted once per session. The forest structure is not reconstructed; Tokscale accounts for token usage only. Session model labels are preserved verbatim and canonicalized by the shared pricing/identity pipeline. Provider attribution is inferred from the model id; unresolvable providers stay unknown. Vendor credit_cost metadata is ignored per ADR 0001 — cost is derived from token buckets and the Tokscale pricing table. Hidden sessions are excluded; zero-token rows are filtered; malformed JSON and missing model/timestamp are reported as record rejections without aborting the scan. Discovery resolves the default sessions.db plus scanner.extraScanPaths roots, opens the database read-only with committed WAL state, and uses the uncached parse path since Devin maintains a single live database. ADR 0031 documents the scope, credit-cost boundary, and rejection behavior. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Walkthrough新增 Devin 客户端目录项与解码器标识,解析本地 SQLite 会话中的 token 使用量,支持数据库发现、WAL 指纹、消息去重和拒绝记录,并接入 CLI/TUI、缓存流水线及相关文档。 ChangesDevin 客户端集成
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant DevinIntegration
participant sessions.db
participant DevinDecoder
participant MessageCache
CLI->>DevinIntegration: 请求发现并解析 Devin 输入
DevinIntegration->>sessions.db: 查找默认及额外数据库
DevinIntegration->>DevinDecoder: 解析每个 InputUnit
DevinDecoder->>sessions.db: 读取会话与消息节点
DevinDecoder-->>DevinIntegration: 返回消息与拒绝记录
DevinIntegration->>MessageCache: 去重并折叠解析结果
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
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 `@crates/tokscale-core/src/integrations/devin/decode.rs`:
- Around line 222-258: 负数 token 当前会被 extract_assistant_usage 当作缺失值并静默归零,与
non_negative_i64 的文档及 malformed-record 预期不符。更新 non_negative_i64 与
extract_assistant_usage 的处理,使字段缺失仍按零处理,但检测到负数时拒绝整条记录并上报
malformed-record;保留现有正常值解析行为。
- Around line 46-56: Update the query and extraction flow around
extract_assistant_usage to select a node-level identifier such as n.node_id or
n.row_id and pass it through. Change node_fallback and dedup_key construction so
assistant messages without message_id use a unique node-<id> fallback instead of
an empty message_id-derived value, while preserving message_id-based
deduplication. Extend node_without_message_id_uses_node_fallback_for_dedup with
two such nodes in one session and verify both are retained.
🪄 Autofix (Beta)
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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 33c4e2c3-b2bc-47d3-8ccd-4b8c8ff80738
📒 Files selected for processing (10)
README.mdREADME.zh-cn.mdcrates/tokscale-cli/src/tui/data/mod.rscrates/tokscale-core/client-catalog.jsoncrates/tokscale-core/src/integrations/devin/decode.rscrates/tokscale-core/src/integrations/devin/mod.rscrates/tokscale-core/src/integrations/mod.rscrates/tokscale-core/src/message_cache.rsdocs/adr/0031-devin-cli-sqlite-integration.mddocs/clients.md
… buckets Two data-integrity fixes to the Devin decoder, both flagged by CodeRabbit review on PR makoMakoGo#196: 1. Assistant messages without a message_id were collapsed into a single empty-string dedup key per session, so all but the first were silently dropped by the fold-layer dedup. The SQL query now selects n.node_id and extract_assistant_usage builds a unique `node-<id>` fallback, matching the module doc and ADR 0031. End-to-end against real local data recovers ~6.67M previously-lost tokens. 2. Negative token buckets were silently zeroed via unwrap_or(0) instead of being rejected as malformed-record, contradicting the non_negative_i64 doc. Replaced with token_bucket, which returns Ok(None) for absent/null fields (treated as zero) but Err(NegativeToken) for negative values, so the caller rejects the row as malformed-record. Regression tests cover two message_id-less nodes in one session (both retained with distinct dedup keys) and a negative input_tokens row (rejected while a healthy sibling is kept). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
There was a problem hiding this comment.
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 `@crates/tokscale-core/src/integrations/devin/decode.rs`:
- Around line 202-204: 在记录解码逻辑及其 token 桶转换处,更新 as_f64 到 i64
的处理:转换前先拒绝负数、非整数值以及超出 i64 范围的浮点数,再执行安全转换并返回 NegativeToken 等现有错误。保留
malformed-record 的现有拒绝流程,并在 negative_token_bucket_is_rejected_as_malformed
附近补充非负小数和可溢出浮点数的回归测试。
- Line 110: 在解析行的逻辑中更新 node_id 读取,移除 row.get(4) 的 unwrap_or(-1) 回退值;读取 NULL
或类型错误时记录 MalformedRecord 并跳过当前行,避免生成重复的伪节点 ID。保留有效 node_id 记录的现有处理流程。
🪄 Autofix (Beta)
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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 084bfaf7-bf2a-4735-ada2-c010b7876580
📒 Files selected for processing (1)
crates/tokscale-core/src/integrations/devin/decode.rs
…version Two more data-integrity fixes from CodeRabbit review on PR makoMakoGo#196: 1. row.get(4).unwrap_or(-1) collapsed any NULL or type-mismatched node_id into a shared `node--1` fallback key, so multiple malformed rows would be deduplicated to one and silently lose usage. Now a read failure records MalformedRecord and skips the row. 2. as_f64().map(|v| v as i64) silently truncated problematic floats: -0.5 became 0 (bypassing the negative check), 1.5 became 1 (losing data), and 1e19 overflowed to a wrapped value. token_bucket now validates finiteness, non-negativity, integrality, and i64 range before converting, returning NegativeToken otherwise. Regression tests cover a non-integer node_id (rejected as malformed) and fractional/overflowing float token buckets (-0.5, 1.5, 1e19 all rejected; 10.0 accepted). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
devinclient identity toclient-catalog.jsonand wireDecoderId::Devininto the message cache andintegration_fordispatchintegrations/devinmodule reads assistant token metrics from the Devin CLI SQLite store at~/.local/share/devin/cli/sessions.db, joiningmessage_nodeswithsessionsand extracting the fourmetadata.metricstoken buckets intoTokenBreakdownmessage_idare deduplicated by(session_id, message_id)so each assistant turn is counted once per session; the forest structure is not reconstructedunknowntotal_credit_cost/total_acu_costmetadata is ignored per ADR 0001 — cost is derived from token buckets and the Tokscale pricing tablesessions.dbplusscanner.extraScanPathsroots, opens the database read-only with committed WAL state, and uses the uncached parse pathdocs/clients.mdand both READMEs list the new clientTest plan
cargo test -p tokscale-core --lib devin— 12 new unit tests pass (schema error, assistant metrics extraction, twin dedup, zero-token filter, hidden exclusion, credit-cost ignored, missing-model rejection, unknown provider, malformed JSON, node-fallback dedup, discovery)cargo test -p tokscale-cli --bin tokscale— 750 tests pass (updatedtest_client_allordering assertion)cargo build -p tokscale-cli— clean buildcargo fmt --check -p tokscale-core— cleantokscale models --client devin --no-spinnerreports 315M tokens / $223.56, with unpriceable models (glm-5-2,swe-1-6-slow) explicitly at $0.00Generated with Devin
Summary by CodeRabbit