资讯动态

ccusage Droid 适配器深度解析:从 Factory Droid 会话文件到用量报告

发布时间:2026/9/21 2:46:20 来源:尧图企业网站定制
ccusage Droid 适配器深度解析从 Factory Droid 会话文件到用量报告【免费下载链接】ccusagenpx ccusage项目地址: https://gitcode.com/gh_mirrors/cc/ccusage本指南以ccusage-adapter-droid位于 rust/adapters/droid/README.md为骨架完整讲解该适配器如何将 Factory Droid 写入磁盘的*.settings.json会话文件转换为 ccusage 报告渲染所需的用量条目usage entries覆盖文件发现、并行读取、Token 解析、模型归一化、会话去重、定价与报告生成的全链路。读完本文你将掌握ccusage droid命令的数据来源、配置方式与实现原理能够理解甚至自定义该适配器的行为边界。适配器职责一份只做转换、不做通用的代码ccusage-adapter-droid的定位非常明确把 Factory Droid 的 session JSON 文件变成报告可渲染的用量条目。它是一个源专属source-specific适配器其设计哲学在 README 中被一句话点破Anything that is not specific to this source belongs inccusage-coreorccusage-adapter-commoninstead.凡不属于该数据源特有的逻辑都应放进ccusage-core或ccusage-adapter-common。这意味着整个 crate 只有 4 个源文件各自承担单一职责模块职责loader.rs读取数据源、按 session 去重、日期过滤parser.rs原始记录解析、Token 字段映射、模型命名归一化paths.rs环境变量、默认目录、文件发现report.rs与共享形状不同的 JSON 与表格输出目录结构也印证了这一点src/lib.rs只做模块声明与run入口编排通用能力文件遍历、并行读取、日期过滤、表格渲染全部复用ccusage-adapter-common与ccusage-coreCargo.toml见 rust/adapters/droid/Cargo.toml声明的运行时依赖仅有ccusage-adapter-common、ccusage-core、jiff时区/时间处理与serde_json四项。数据源与文件发现机制README 给出的数据源定义是${DROID_SESSIONS_DIR:-~/.factory/sessions}/**/*.json即默认读取~/.factory/sessions下的所有 JSON 文件若设置了环境变量DROID_SESSIONS_DIR则改用它指定的目录。源码 paths.rs 在此基础上做了三层细化多路径支持DROID_SESSIONS_DIR支持用逗号分隔多个目录每个目录都会被依次扫描后缀过滤虽然遍历时收集所有*.json文件但最终只保留文件名以.settings.json结尾的文件discover_settings_files避免把同一会话目录下无关的 JSON 配置混入统计目录去重对解析出的路径做HashSet去重重复或不存在非目录的路径会被跳过。在没有设置环境变量且HOME不可用时droid_session_paths会返回home directory is not set错误见 paths.rs 第 31-33 行。文件读取大小均衡分块 有序并行README 特别强调文件读取通过ccusage-adapter-common完成它负责 walking、size-balanced chunking 与 ordered parallel reads。对应实现位于 rust/adapters/common/src/lib.rscollect_files_with_extension递归遍历目录收集指定扩展名的文件chunk_file_indexes_by_size按文件字节大小做加权排序再用贪心算法把索引分配到各 chunk使每个 worker 处理的字节总量尽量均衡避免一个大文件拖慢整体read_files_parallel依据available_parallelism决定 worker 数量--single-thread时降为 1通过thread::scope并行读取并按原始文件顺序重组结果——droid 的load_entries_inner在注释中明确说明并行读回后必须保持排序后的文件顺序才能保证随后的稳定排序与最新快照优先去重结果与单线程读取一致见 loader.rs 第 26-28 行。每个文件由load_settings_file解析解析失败如 JSON 语法错误不会中断整体流程而是记入 debug 日志后跳过该文件返回None。Token 解析tokenUsage字段映射与兜底load_settings_file的核心是读取会话文件 JSON 中的tokenUsage对象parser.rs 中parse_token_usage按如下字段映射到 ccusage 的通用TokenUsageRawFactory Droid 字段ccusage 内部字段含义inputTokensinput_tokens输入 token 数outputTokensoutput_tokens输出 token 数cacheCreationTokenscache_creation_input_tokens缓存创建 token 数cacheReadTokenscache_read_input_tokens缓存读取 token 数thinkingTokensextra_total_tokens经reasoning_tokens思考 token 数单独计入总量totalTokens—兜底字段见下关键兜底逻辑当上述分项缺失时apply_total_token_fallback会尝试用totalTokens补齐对应测试falls_back_to_total_tokens_when_droid_parts_are_missing中仅给{totalTokens: 456}时output_tokens被置为 456。若五类 token 之和为 0则该文件被判定为无有效用量而跳过。thinkingTokens被单独保存在reasoning_tokens最终写入LoadedEntry.extra_total_tokens见 loader.rs 第 83 行并在报告统计时计入总 token 数——测试report_total_includes_thinking_tokens验证了这一点输入 100 输出 50 缓存创建 20 缓存读取 10 思考 5 总 token 185。模型与 Provider 归一化三路取模Droid 的模型名字符串很脏如custom:Claude-Opus-4.5-Thinking-[Anthropic]-0normalize_droid_model_name会按顺序处理剥离custom:前缀删除方括号[...]包裹的片段如[Anthropic]转小写并把.、空白、-统一折叠为单个-同时修剪首尾与连续连字符。结果custom:Claude-Opus-4.5-Thinking-[Anthropic]-0→claude-opus-4-5-thinking-0gemini-2.5-pro→gemini-2-5-pro见 loader.rs 测试normalizes_droid_model_names。模型的来源按优先级有三路model字段存在则直接归一化sidecar JSONLextract_model_from_sidecar_jsonl查找与session.settings.json同名的session.jsonl在前 500 行中扫描形如Model: xxx的行提取模型名对应测试falls_back_to_sidecar_jsonl_modelProvider 默认名都拿不到时按 provider 回退为claude-unknown、gpt-unknown、gemini-unknown、grok-unknown或unknown。Provider 同样有两级推断先看providerLock字段normalize_droid_provider会把claude/anthropic、google_ai/gemini/vertex_ai、x_ai/grok等别名归一到anthropic/google/xai若为unknown再由模型名特征反推含claude/opus/sonnet/haiku判为 anthropicgpt-/chatgpt/o数字 判为 openai含gemini判为 google含grok判为 xai。时间戳与定价以providerLockTimestamp为准会话条目需要一个时间戳用于日期分组与排序。settings_timestamp优先使用providerLockTimestampRFC 3339 格式经parse_ts_timestamp解析并统一序列化为毫秒精度字段缺失时兜底使用文件系统 mtime。这一选择对定价有直接影响calculate_droid_cost会带上pricing_timestamp即providerLockTimestamp调用calculate_cost_for_usage_at让费用按锁定 provider 时的价格计算而非按报告生成时的最新价。对应测试分别验证了preserves_provider_lock_timestamp_for_pricing设置文件带providerLockTimestamp时pricing_timestamp等于该时刻leaves_pricing_timestamp_empty_when_only_file_metadata_is_available仅能拿到 mtime 时pricing_timestamp为Nonedoes_not_use_display_timestamp_for_droid_pricingdeepseek 模型按 providerLock 时刻计价100 万输入 token × 0.00000014 0.14 美元。定价的模型候选由droid_model_candidates生成先尝试裸模型名再按 provider 加前缀如 anthropic 会依次尝试anthropic/model、openrouter/anthropic/modelopenai 尝试openai/、openrouter/openai/google 尝试google/、vertex_ai/、openrouter/google/xai 尝试xai/、openrouter/x-ai/取第一个能算出正费用的候选。注意calculate_droid_cost会把reasoning_tokens并入output_tokens参与计费且成本模式固定为CostMode::Calculate。会话去重最新快照优先latest-winsFactory Droid 的会话可能被多次写入快照例如archive/session-c.settings.json与根目录下的session-c.settings.json并存。load_entries_inner的去重策略是所有条目按时间戳升序排序从最新到最旧遍历用HashSet记录已见过的session_id每个session_id只保留时间戳最新的一条。测试keeps_latest_snapshot_for_duplicate_session_ids验证了这一点两个session-c快照05-01 与 05-02最终只产出 1 条且取 05-02 的用量输入 100、输出 200。session_id的取值来自文件名去掉.settings.json后缀即得如session-a.settings.json→session-a无法识别时回退为unknown。每个条目的 message id 统一写成droid:session_id格式项目名固定为droid、项目路径显示为Droid见to_loaded_entry。报告形状四类聚合与 JSON 输出report.rs 的summarize_entries按报告类型聚合Daily按entry.date分组Weekly / Monthly先按天聚合再通过summarize_summaries_by_bucket以周日为一周起点重新分桶Session按session_id分组并把分组键放入session_id字段。report_from_rows生成的 JSON 形状为{ daily | weekly | monthly | sessions: [...], totals: {...} }行内数据复用共享的agent_summary_json总额由totals_json计算——因此 droid 在 JSON 层面几乎没有重复代码这正是 README 所述只在与共享形状不同处做覆盖的体现。CLI 集成与运行流程Droid 适配器通过Command::Droid接入 ccusage 主程序rust/crates/ccusage/src/main.rs 第 39 行Some(Command::Droid(args)) adapter::droid::run(args)并在 rust/crates/ccusage-cli-parser/src/cli-commands.json 中注册了droid、droid daily、droid monthly、droid session等子命令。last_window.rs与timezone.rs也将 Droid 纳入--last-window与时区推导的适用命令列表。runlib.rs的完整流程是用PricingMap::load_with_overrides加载价格表支持--offline、日志级别与--pricing-overrides自定义load_entries并行读取、解析并去重filter_loaded_entries_by_date按--since/--until过滤日期summarize_entries按daily/weekly/monthly/session聚合sort_summaries按--order排序若--json含--jq、--no-cost则输出 JSON否则渲染标题为 Droid Token Usage Report 的表格。公共 API 与 README 声明完全一致loader::load_entries、report::report_from_rows、report::summarize_entries与run。依赖与构建层Cargo.toml的依赖全部走 workspace 版本ccusage-adapter-common文件遍历与并行读取、ccusage-coreLoadedEntry、PricingMap、汇总/输出通用逻辑、jiff时区换算与serde_json开发依赖ccusage-test-support提供fs_fixture!与EnvVarGuard等测试工具。构建上droid 属于adaptersCrane artifact 层该层在一次 Cargo 调用中同时编译全部适配器因此各适配器可并发构建。测试矩阵行为即契约droid 适配器的测试全部内联在 loader.rs 与 parser.rs 的#[cfg(test)]模块中覆盖了适配器全部关键行为模型名归一化normalizes_droid_model_namestotalTokens兜底falls_back_to_total_tokens_when_droid_parts_are_missing从 settings 文件加载用量loads_usage_from_droid_settings_filessidecar JSONL 取模型falls_back_to_sidecar_jsonl_model重复 session 取最新快照keeps_latest_snapshot_for_duplicate_session_idsthinking tokens 计入报告总量report_total_includes_thinking_tokens定价时间戳的三个分支providerLock 优先、mtime 兜底、deepseek 按锁定时刻计价。这些测试同时充当了可运行的行为契约任何对解析、去重或定价逻辑的改动都必须保持上述语义不变。如果你要基于 Factory Droid 的会话文件做自定义统计这套映射与兜底规则就是最可靠的参考蓝本。【免费下载链接】ccusagenpx ccusage项目地址: https://gitcode.com/gh_mirrors/cc/ccusage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价