Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

feat(core): add Devin CLI SQLite integration for local token accounting - #196

Open
vlln wants to merge 3 commits into
makoMakoGo:personal/local-clientsfrom
vlln:feat/devin-cli-sqlite-integration
Open

feat(core): add Devin CLI SQLite integration for local token accounting#196
vlln wants to merge 3 commits into
makoMakoGo:personal/local-clientsfrom
vlln:feat/devin-cli-sqlite-integration

Conversation

@vlln

@vlln vlln commented Jul 28, 2026

Copy link
Copy Markdown

Summary

  • Add devin client identity to client-catalog.json and wire DecoderId::Devin into the message cache and integration_for dispatch
  • New integrations/devin module reads assistant token metrics from the Devin CLI SQLite store at ~/.local/share/devin/cli/sessions.db, joining message_nodes with sessions and extracting the four metadata.metrics token buckets into 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
  • Session model labels are preserved verbatim and canonicalized by the shared pricing/identity pipeline; provider attribution is inferred from the model id, with unresolvable providers staying unknown
  • Vendor total_credit_cost / total_acu_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
  • ADR 0031 documents the scope, credit-cost boundary, and rejection behavior; docs/clients.md and both READMEs list the new client

Test 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 (updated test_client_all ordering assertion)
  • cargo build -p tokscale-cli — clean build
  • cargo fmt --check -p tokscale-core — clean
  • End-to-end against real local Devin data: tokscale models --client devin --no-spinner reports 315M tokens / $223.56, with unpriceable models (glm-5-2, swe-1-6-slow) explicitly at $0.00

Generated with Devin

Summary by CodeRabbit

  • 新功能
    • 新增 Devin 客户端支持:可在本地扫描并读取 sessions.db,提取助理消息的 Token 用量并对重复消息进行去重。
    • Devin 用量将出现在 CLI、TUI 及缓存结果中。
  • 文档
    • 更新 README 与中文文档,扩充“支持的客户端”列表以包含 Devin。
    • 新增 ADR,补充 Devin CLI SQLite 集成的解析与边界行为说明,并更新客户端发现说明。
  • 测试
    • 扩展并更新 Devin 会话发现、解码、去重与异常/过滤场景相关用例。

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>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: beeef796-0196-427d-8ce5-2d6cf8dd2cbd

📥 Commits

Reviewing files that changed from the base of the PR and between 32dd3c1 and 7995296.

📒 Files selected for processing (1)
  • crates/tokscale-core/src/integrations/devin/decode.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/tokscale-core/src/integrations/devin/decode.rs

Walkthrough

新增 Devin 客户端目录项与解码器标识,解析本地 SQLite 会话中的 token 使用量,支持数据库发现、WAL 指纹、消息去重和拒绝记录,并接入 CLI/TUI、缓存流水线及相关文档。

Changes

Devin 客户端集成

Layer / File(s) Summary
客户端目录与解码器契约
crates/tokscale-core/client-catalog.json, crates/tokscale-core/src/message_cache.rs, crates/tokscale-cli/src/tui/data/mod.rs, README.md, README.zh-cn.md, docs/adr/..., docs/clients.md
注册 Devin 客户端和 devin 解码器,更新客户端顺序断言、双语 README、ADR 及发现文档。
SQLite 会话解码
crates/tokscale-core/src/integrations/devin/decode.rs
读取可见会话及 assistant token 指标,处理模型、时间戳、JSON、零值和重复消息,并测试各类边界行为。
发现、缓存与集成流水线
crates/tokscale-core/src/integrations/devin/mod.rs, crates/tokscale-core/src/integrations/mod.rs
发现默认和额外 sessions.db,使用 Devin 解码器解析输入,在折叠阶段去重消息,并将 ClientId::Devin 路由到该集成。

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: 去重并折叠解析结果
Loading

Possibly related PRs

Poem

小兔敲键盘,Devin 入目录,
SQLite 里 token 闪亮亮。
去重胡萝卜排成行,
缓存流水轻轻淌,
新客户端快乐登场!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% 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 标题准确概括了核心变更:新增 Devin CLI SQLite 集成用于本地 token 统计。
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

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

@coderabbitai coderabbitai Bot 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.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8a772ca and d3fc1da.

📒 Files selected for processing (10)
  • README.md
  • README.zh-cn.md
  • crates/tokscale-cli/src/tui/data/mod.rs
  • crates/tokscale-core/client-catalog.json
  • crates/tokscale-core/src/integrations/devin/decode.rs
  • crates/tokscale-core/src/integrations/devin/mod.rs
  • crates/tokscale-core/src/integrations/mod.rs
  • crates/tokscale-core/src/message_cache.rs
  • docs/adr/0031-devin-cli-sqlite-integration.md
  • docs/clients.md

Comment thread crates/tokscale-core/src/integrations/devin/decode.rs
Comment thread crates/tokscale-core/src/integrations/devin/decode.rs Outdated
… 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>

@coderabbitai coderabbitai Bot 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.

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

📥 Commits

Reviewing files that changed from the base of the PR and between d3fc1da and 32dd3c1.

📒 Files selected for processing (1)
  • crates/tokscale-core/src/integrations/devin/decode.rs

Comment thread crates/tokscale-core/src/integrations/devin/decode.rs Outdated
Comment thread 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>
@vlln

vlln commented Jul 28, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant