[Feature] add ChatLuna usage tracking dashboard#870
Conversation
|
Warning Review limit reached
Your plan currently allows 2 reviews/hour. Refill in 9 minutes and 50 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
Walkthrough本 PR 在 core/adapter/extension 前后端全链路新增模型使用量上报:类型与事件、stack 源解析、token 估算、model reporter 注入、LLM/embeddings/rerank 的成功/失败上报、持久化服务与前端仪表盘展示。 Changes使用量追踪与上报系统
🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive usage tracking system for ChatLuna, enabling the monitoring of token consumption for LLM, embedding, and reranker operations. It adds a new usage service to the core package that leverages AsyncLocalStorage for context management and employs a proxy mechanism to automatically report usage data. Additionally, a new extension-usage package is included to facilitate the persistence of these records into a database. Review feedback suggests improving the robustness of the usage source inference logic by adding error handling when reading and parsing package.json files to prevent potential service crashes.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@packages/core/src/services/chat.ts`:
- Around line 148-151: The ready handler calls warmupTokenEncoder() without
awaiting or handling errors, risking unhandled Promise rejections; update the
this.ctx.on('ready', ...) callback to await warmupTokenEncoder() and wrap it in
a try/catch (after awaiting this._dedupeConstraintNames()) and on error log the
failure (use the existing logger on this.ctx or a suitable logger) so failures
are observed instead of causing unhandled rejections; reference the
this.ctx.on('ready') handler, the _dedupeConstraintNames method, and the
warmupTokenEncoder function when making this change.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: fc92a471-3177-4b39-906c-7c7fb17ddc9e
⛔ Files ignored due to path filters (5)
packages/core/package.jsonis excluded by!**/*.jsonpackages/core/src/locales/en-US.schema.ymlis excluded by!**/*.ymlpackages/core/src/locales/zh-CN.schema.ymlis excluded by!**/*.ymlpackages/extension-usage/package.jsonis excluded by!**/*.jsonpackages/extension-usage/tsconfig.jsonis excluded by!**/*.json
📒 Files selected for processing (19)
packages/core/src/config.tspackages/core/src/index.tspackages/core/src/llm-core/chain/agent_chat_chain.tspackages/core/src/llm-core/chain/chat_chain.tspackages/core/src/llm-core/platform/client.tspackages/core/src/llm-core/platform/model.tspackages/core/src/llm-core/platform/rerank.tspackages/core/src/llm-core/platform/usage.tspackages/core/src/middlewares/model/test_model.tspackages/core/src/services/chat.tspackages/core/src/services/conversation_runtime.tspackages/core/src/services/types.tspackages/core/src/services/usage.tspackages/core/src/services/usage_source.tspackages/core/src/utils/error.tspackages/core/src/utils/request.tspackages/core/tests/model-usage.spec.tspackages/extension-usage/src/index.tspackages/extension-usage/tests/usage.spec.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@packages/core/src/middlewares/model/test_model.ts`:
- Around line 85-87: The code reads context.options.model into the local
variable model and then calls model.includes('/'), but context.options.model is
optional (model?: string) and may be undefined; update the check to guard
against undefined by verifying model is a string before calling includes (e.g.,
use a typeof or nullish-default guard) so that the code only calls includes on a
valid string and handles the absent-model case gracefully; adjust any downstream
logic that assumed model was present (in the same block where model is used) to
handle the fallback/early-return when model is missing.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: a98b29d1-e8f2-4d65-b35f-114f7ffa01d4
📒 Files selected for processing (3)
packages/core/src/middlewares/model/test_model.tspackages/core/src/services/chat.tspackages/core/src/services/usage_source.ts
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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 `@packages/core/src/llm-core/platform/client.ts`:
- Around line 208-221: The _reportUsage method currently awaits
this.ctx.root.parallel('chatluna/model-usage', payload) directly so any listener
error can bubble up and break the main request; wrap the parallel call in a
try/catch, log the caught error (e.g., use this.ctx.logger?.error or
console.error) and do not rethrow so usage-reporting failures are swallowed and
don't affect the core model call; keep the existing payload formation
(ModelUsagePayload) and only change the invocation of this.ctx.root.parallel
inside _reportUsage to a safe, non-throwing call.
- Around line 195-203: createModel currently caches by model only, so the first
computed source (from usageSourceFromStack) is baked into the instance and
reused for other callers causing incorrect usage attribution; change caching to
include source (e.g., compute source = usageSourceFromStack(new Error().stack)
for each call and make the cache key composite like `${model}:${source}` or
convert _modelPool to a nested map keyed by model then source, then call
this._createModel(model, usage => this._reportUsage(model, source, usage),
source) so each distinct source gets its own model instance; update references
to _modelPool, createModel, _createModel, usageSourceFromStack, and _reportUsage
accordingly.
In `@packages/core/src/llm-core/platform/model.ts`:
- Around line 1054-1065: The current logic sets estimated: usage == null, which
misses cases where a usage object exists but token fields are absent; update the
logic around the inputTokens/totalTokens computation and the call to
this._report so that you treat the call as estimated when usage is null OR when
none of the token fields (input_tokens, output_tokens, total_tokens) are present
on usage. Concretely, compute a boolean like hasTokenFields = Boolean(usage &&
(usage.input_tokens != null || usage.output_tokens != null || usage.total_tokens
!= null)), use estimateTextTokens(input) whenever input_tokens is missing to
derive inputTokens, compute totalTokens falling back to inputTokens when
total_tokens is missing, and set estimated = !hasTokenFields before calling
this._report (functions/identifiers to locate: usage, inputTokens,
estimateTextTokens, totalTokens, this._report).
In `@packages/core/src/llm-core/platform/rerank.ts`:
- Around line 180-191: The estimated flag is only set when usage == null, but
you still call estimateTextTokens(input) when usage exists but lacks token
fields; update the logic around the call to this._report in rerank.ts so
estimated is true when token fields are missing (e.g., usage == null ||
(usage.input_tokens == null && usage.total_tokens == null)), keep inputTokens
computed as now (usage?.input_tokens ?? usage?.total_tokens ?? await
estimateTextTokens(input)), and ensure totalTokens and outputTokens fallback
behavior remains unchanged while passing the corrected estimated boolean to
this._report.
In `@packages/extension-usage/src/index.ts`:
- Around line 56-64: The event handler registered with
ctx.on('chatluna/model-usage') directly awaits ctx.database.create and can throw
into the event loop; wrap the handler body in a try/catch, call
ctx.database.create(...) inside the try, and in the catch log the error using
the platform logger (e.g., ctx.logger.error(error) or similar) so database
failures don’t bubble up and affect the main model flow; reference the existing
listener name 'chatluna/model-usage', the call site 'ctx.database.create', and
the logger method 'ctx.logger.error' when making the change.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f13ba199-e944-42ed-a162-f2bfed463d09
⛔ Files ignored due to path filters (1)
packages/core/package.jsonis excluded by!**/*.json
📒 Files selected for processing (64)
packages/adapter-azure-openai/src/client.tspackages/adapter-azure-openai/src/requester.tspackages/adapter-claude/src/client.tspackages/adapter-deepseek/src/client.tspackages/adapter-deepseek/src/requester.tspackages/adapter-dify/src/client.tspackages/adapter-doubao/src/client.tspackages/adapter-doubao/src/requester.tspackages/adapter-gemini/src/client.tspackages/adapter-gemini/src/requester.tspackages/adapter-hunyuan/src/client.tspackages/adapter-hunyuan/src/requester.tspackages/adapter-ollama/src/client.tspackages/adapter-ollama/src/requester.tspackages/adapter-openai-like/src/client.tspackages/adapter-openai-like/src/requester.tspackages/adapter-openai/src/client.tspackages/adapter-openai/src/requester.tspackages/adapter-qwen/src/client.tspackages/adapter-qwen/src/requester.tspackages/adapter-rwkv/src/client.tspackages/adapter-rwkv/src/requester.tspackages/adapter-spark/src/client.tspackages/adapter-wenxin/src/client.tspackages/adapter-wenxin/src/requester.tspackages/adapter-zhipu/src/client.tspackages/adapter-zhipu/src/requester.tspackages/core/src/chains/chain.tspackages/core/src/conversation_types.tspackages/core/src/index.tspackages/core/src/llm-core/chat/app.tspackages/core/src/llm-core/chat/infinite_context.tspackages/core/src/llm-core/memory/message/database_history.tspackages/core/src/llm-core/platform/api.tspackages/core/src/llm-core/platform/client.tspackages/core/src/llm-core/platform/model.tspackages/core/src/llm-core/platform/rerank.tspackages/core/src/llm-core/platform/service.tspackages/core/src/llm-core/platform/usage.tspackages/core/src/middlewares/chat/message_delay.tspackages/core/src/middlewares/chat/rollback_chat.tspackages/core/src/middlewares/conversation/request_conversation.tspackages/core/src/middlewares/conversation/resolve_conversation.tspackages/core/src/middlewares/preset/delete_preset.tspackages/core/src/middlewares/system/conversation_manage.tspackages/core/src/migration/room_to_conversation.tspackages/core/src/migration/validators.tspackages/core/src/services/chat.tspackages/core/src/services/conversation.tspackages/core/src/services/conversation_runtime.tspackages/core/src/services/types.tspackages/core/src/utils/archive.tspackages/core/src/utils/conversation.tspackages/core/src/utils/string.tspackages/core/src/utils/usage_source.tspackages/core/tests/conversation-archive.spec.tspackages/core/tests/conversation-migration.spec.tspackages/core/tests/conversation-resolve.spec.tspackages/core/tests/conversation-service.spec.tspackages/core/tests/conversation-utils.spec.tspackages/core/tests/helpers.tspackages/extension-usage/src/index.tspackages/service-embeddings/src/embeddings/huggingface.tspackages/shared-adapter/src/requester.ts
✅ Files skipped from review due to trivial changes (14)
- packages/core/src/middlewares/conversation/request_conversation.ts
- packages/core/src/middlewares/preset/delete_preset.ts
- packages/core/src/middlewares/chat/message_delay.ts
- packages/core/tests/conversation-archive.spec.ts
- packages/core/src/llm-core/chat/app.ts
- packages/core/src/middlewares/chat/rollback_chat.ts
- packages/core/tests/conversation-service.spec.ts
- packages/core/tests/conversation-migration.spec.ts
- packages/core/src/chains/chain.ts
- packages/core/src/llm-core/chat/infinite_context.ts
- packages/adapter-deepseek/src/requester.ts
- packages/core/src/utils/string.ts
- packages/core/src/migration/room_to_conversation.ts
- packages/core/src/llm-core/platform/service.ts
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (1)
packages/core/src/llm-core/platform/rerank.ts (1)
183-200:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
estimated标记逻辑仍然不正确,会把估算的 token 误标为非估算。当前代码仅在所有三个字段(
input_tokens、output_tokens、total_tokens)都为null时才标记estimated = true。但实际上,只要input_tokens和total_tokens都为null,就会调用estimateTextTokens来估算inputTokens,无论output_tokens是否存在。例如,若
usage = { output_tokens: 10 }:
estimated会被计算为false(因为output_tokens != null)- 但
inputTokens仍会通过await estimateTextTokens(input)估算得出- 最终上报时
estimated: false,但实际使用了估算值💡 建议修改
try { const estimated = - usage?.input_tokens == null && - usage?.output_tokens == null && - usage?.total_tokens == null + usage?.input_tokens == null && usage?.total_tokens == null const inputTokens = usage?.input_tokens ?? usage?.total_tokens ?? (await estimateTextTokens(input))🤖 Prompt for 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. In `@packages/core/src/llm-core/platform/rerank.ts` around lines 183 - 200, The estimated flag logic in rerank.ts is wrong: compute estimated based on whether we had to call estimateTextTokens (i.e., if usage?.input_tokens == null && usage?.total_tokens == null) rather than requiring all three fields to be null; update the calculation of estimated accordingly so that when inputTokens is derived via await estimateTextTokens(input) you set estimated = true, and keep the rest of the report payload construction (inputTokens, output: usage?.output_tokens ?? 0, total: usage?.total_tokens ?? inputTokens) and the this._report call unchanged; adjust the code around the estimated and inputTokens assignments (references: estimated, inputTokens, estimateTextTokens, this._report) to reflect this new condition.
🧹 Nitpick comments (11)
packages/extension-usage/client/charts/model-line.ts (2)
11-11: 💤 Low value参数命名违反 lint 规则。
静态分析提示
chatluna_usage不符合 camelCase 命名规范。由于这是从 store 中解构出的属性键,建议通过重命名解构来满足规范:♻️ 建议改动
- options({ chatluna_usage }) { - const dates = chatluna_usage.timeline.map((row) => row.date) - const models = chatluna_usage.models + options({ chatluna_usage: usage }) { + const dates = usage.timeline.map((row) => row.date) + const models = usage.models .slice(0, 5) .map((row) => row.key) if (!dates.length || !models.length) return ... - series: chatluna_usage.modelTimeline + series: usage.modelTimeline同样的问题在
token-line.ts、source-bar.ts、model-pie.ts中也存在,建议统一处理。🤖 Prompt for 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. In `@packages/extension-usage/client/charts/model-line.ts` at line 11, The parameter name chatluna_usage in the options({ chatluna_usage }) destructuring violates camelCase lint rules; update the destructuring to rename the key to a camelCase local variable (e.g., options({ chatluna_usage: chatlunaUsage })) and then use that new local symbol (chatlunaUsage) inside the options function; apply the same pattern to the other files mentioned (token-line.ts, source-bar.ts, model-pie.ts) to keep naming consistent.
13-15: 💤 Low value魔法数字 5 建议提取为常量或配置。
slice(0, 5)硬编码了图例中展示的模型数量上限,若产品上希望调整或与model-pie等其他图表保持一致,建议提取为命名常量(如TOP_MODEL_COUNT)。🤖 Prompt for 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. In `@packages/extension-usage/client/charts/model-line.ts` around lines 13 - 15, Replace the hardcoded slice(0, 5) with a named constant (e.g., TOP_MODEL_COUNT) and use that constant when building the models array in model-line.ts (the expression starting with const models = chatluna_usage.models.slice...). Declare TOP_MODEL_COUNT near the top of the module (or in a shared config if other charts like model-pie need the same value) so the max shown models is configurable and consistent across charts; update any other occurrences (e.g., model-pie) to reference the same constant.packages/extension-usage/client/charts/token-line.ts (2)
10-10: 💤 Low value参数命名违反 lint 规则(与
model-line.ts同因)。
chatluna_usage不符合 camelCase 规范,建议通过重命名解构修正:♻️ 建议改动
- options({ chatluna_usage }) { - if (!chatluna_usage.timeline.length) return + options({ chatluna_usage: usage }) { + if (!usage.timeline.length) return(后续
chatluna_usage.timeline.map(...)三处也同步替换为usage.timeline.map(...))🤖 Prompt for 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. In `@packages/extension-usage/client/charts/token-line.ts` at line 10, The parameter name chatluna_usage in the options destructuring violates camelCase; change the destructured name to usage (e.g., options({ chatluna_usage: usage }) or directly options({ usage }) if the caller provides that) and update all references of chatluna_usage.timeline.map(...) to usage.timeline.map(...) (there are three occurrences) so the function (options) and its internal usage variable follow camelCase and lint rules.
22-54: 💤 Low value建议减少 timeline 的重复遍历。
chatluna_usage.timeline当前被遍历 4 次(xAxis + 3 个 series)。对长 timeline 而言,可一次遍历构造各序列数据以略微提升性能与可读性:♻️ 建议改动
- return { + const dates: string[] = [] + const input: number[] = [] + const output: number[] = [] + const total: number[] = [] + for (const row of chatluna_usage.timeline) { + dates.push(row.date) + input.push(row.inputTokens) + output.push(row.outputTokens) + total.push(row.totalTokens) + } + return { ... - xAxis: { - type: 'category', - data: chatluna_usage.timeline.map((row) => row.date) - }, + xAxis: { type: 'category', data: dates }, ... series: [ - { name: '输入', type: 'line', smooth: true, - data: chatluna_usage.timeline.map((row) => row.inputTokens) }, - { name: '输出', type: 'line', smooth: true, - data: chatluna_usage.timeline.map((row) => row.outputTokens) }, - { name: '总量', type: 'line', smooth: true, - data: chatluna_usage.timeline.map((row) => row.totalTokens) } + { name: '输入', type: 'line', smooth: true, data: input }, + { name: '输出', type: 'line', smooth: true, data: output }, + { name: '总量', type: 'line', smooth: true, data: total } ] }🤖 Prompt for 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. In `@packages/extension-usage/client/charts/token-line.ts` around lines 22 - 54, chatluna_usage.timeline is being mapped four separate times (xAxis.data and three series data); refactor to a single loop that builds arrays for dates, inputTokens, outputTokens and totalTokens and then assign those arrays to xAxis.data and each series.data to avoid repeated traversal; update the code around xAxis and series (the mapping logic referencing chatluna_usage.timeline, xAxis, and series entries named '输入','输出','总量') to use the prebuilt arrays.packages/extension-usage/client/state.ts (2)
21-30: ⚡ Quick win错误信息丢失,影响可观测性
Line 25 的空
catch块仅向用户展示通用错误提示,但未保留原始错误对象的详细信息(如堆栈、错误类型等)。在调试查询失败时,开发者无法从日志中获取足够的上下文。🔍 建议记录错误详情
export async function refresh() { try { loading.value = true store.chatluna_usage = await send('chatluna-usage/query', query) - } catch { + } catch (e) { + console.error('Failed to query ChatLuna usage:', e) ElMessage.error('查询 ChatLuna 用量失败') } finally { loading.value = false } }🤖 Prompt for 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. In `@packages/extension-usage/client/state.ts` around lines 21 - 30, The catch in refresh() swallows the error; update it to capture the error object (e.g., catch (err)) and log the full error for observability and debugging, then show a user-friendly message; specifically, in refresh() after await send('chatluna-usage/query', query) catch the error, call console.error or your logger with the error and context (including query and any relevant state like store.chatluna_usage), and keep the ElMessage.error call (optionally append a short err.message) before finally clearing loading.value.
56-81: ⚡ Quick win默认值重复定义,存在维护风险
resetFilters函数在 Line 58-79 手动重新分配了所有查询字段的默认值,与 Line 8-17 定义的初始query默认值重复。当需要调整默认值时(例如修改默认pageSize),必须同时修改两处,容易遗漏。♻️ 建议提取默认值以消除重复
+const defaultQuery: ChatLunaUsage.Query = { + period: 'day', + groupBy: 'model', + sortBy: 'totalTokens', + desc: true, + listSortBy: 'createdAt', + listDesc: true, + page: 1, + pageSize: 50 +} + -export const query = reactive<ChatLunaUsage.Query>({ - period: 'day', - groupBy: 'model', - sortBy: 'totalTokens', - desc: true, - listSortBy: 'createdAt', - listDesc: true, - page: 1, - pageSize: 50 -}) +export const query = reactive<ChatLunaUsage.Query>({ ...defaultQuery }) export function resetFilters() { range.value = undefined - Object.assign(query, { - period: 'day', - groupBy: 'model', - sortBy: 'totalTokens', - desc: true, - listSortBy: 'createdAt', - listDesc: true, - page: 1, - pageSize: 50, + Object.assign(query, { + ...defaultQuery, start: undefined, end: undefined, source: undefined, model: undefined, platform: undefined, chatPlatform: undefined, callType: undefined, guildId: undefined, userId: undefined, success: undefined, estimated: undefined, keyword: undefined }) refresh() }🤖 Prompt for 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. In `@packages/extension-usage/client/state.ts` around lines 56 - 81, resetFilters 当前把 query 的默认值硬编码一遍,和最初创建 query 的默认值重复,易导致维护不一致;将初始默认值提取为一个共享常量(例如 DEFAULT_QUERY)并在初始化 query 时和 resetFilters 中复用(在 resetFilters 使用 Object.assign(query, DEFAULT_QUERY) 以保持响应式对象而不是替换引用),同时保留对 range.value 和 refresh() 的现有处理;更新代码中与 query 初始化相关的函数/变量(query、resetFilters、range、refresh)以引用该常量即可消除重复。packages/extension-usage/client/charts/utils.ts (2)
52-75: 💤 Low value类型断言可能掩盖类型不匹配
Tooltip.item和Tooltip.axis返回值使用as unknown as echarts.TooltipComponentOption进行双重类型断言。这种模式通常表明返回的对象结构与TooltipComponentOption类型定义不完全匹配。建议检查echarts的类型定义,确保formatter参数类型与实际传入的一致,或者直接返回满足类型的对象而不使用类型断言。🤖 Prompt for 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. In `@packages/extension-usage/client/charts/utils.ts` around lines 52 - 75, The Tooltip.item and Tooltip.axis helpers are using a double cast to echarts.TooltipComponentOption which hides real type mismatches; update the return types and formatter signatures to match echarts' definitions instead of using "as unknown as". Specifically, import or reference echarts.TooltipComponentOption and change item<T> to return an object conforming to that interface (e.g., set formatter: (params: any | echarts.TooltipFormatterCallbackParams) => string or the exact echarts formatter type) and similarly update axis<T> formatter to the echarts axis formatter signature, removing the "as unknown as" casts so the returned object shape (trigger, axisPointer, formatter) satisfies echarts.TooltipComponentOption at compile time (adjust generic T and parameter shapes in Tooltip.item and Tooltip.axis to match echarts types).
25-45: ⚡ Quick win存在重复的条件判断
Line 25 已经根据
store.chatluna_usage的真值性决定了option的值(有数据时调用options(store),无数据时为undefined)。Line 42-44 又再次检查store.chatluna_usage来决定空态文案,但其实只需判断option是否为真即可。这种重复检查会在store.chatluna_usage存在但options(store)返回 falsy 值时产生不一致的行为。♻️ 建议简化条件判断
const option = store.chatluna_usage ? options(store) : undefined return h( resolveComponent('k-card'), { class: 'frameless chatluna-usage-chart' }, { header: () => h('span', { class: 'left' }, [title]), default: () => option ? h(VChart, { option, autoresize: true, onClick: click ? (event: unknown) => click(event as ChartClickEvent) : undefined }) : h('div', { class: 'chart-empty' }, [ - store.chatluna_usage - ? '暂无用量数据' - : '正在加载用量数据' + '正在加载用量数据' ]) } )🤖 Prompt for 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. In `@packages/extension-usage/client/charts/utils.ts` around lines 25 - 45, The render logic redundantly checks store.chatluna_usage twice which can lead to inconsistent messages when options(store) returns a falsy value; instead base the empty-state text on the computed option variable. Update the conditional inside the default slot of the k-card render so that it uses option (set by options(store)) to decide whether to render VChart or the chart-empty div and change the text to reflect option being absent (e.g., "暂无用量数据" vs "正在加载用量数据") accordingly; touch the code around the option assignment and the default slot where VChart is instantiated (references: option, options(store), store.chatluna_usage, VChart) to remove the duplicate store.chatluna_usage check.packages/core/src/utils/usage_source.ts (2)
31-78: ⚡ Quick win路径过滤检查顺序低效
Line 71-78 在读取并解析
package.json之后才检查路径是否为核心框架路径(/packages/core/src/等)并跳过。这意味着对于核心框架的栈帧,函数会先执行文件系统遍历(Line 32-37)、文件读取和 JSON 解析(Line 36),然后才决定跳过。⚡ 建议提前过滤核心路径以避免无效 I/O
for (const line of stack.split('\n')) { logger?.debug(`stack line: ${line}`) const match = line.match( /\(?((?:[A-Za-z]:[\\/]|\/|file:\/\/\/).+):\d+:\d+\)?$/ ) if (!match) { logger?.debug('stack line ignored: no file match') continue } const file = match[1].startsWith('file:///') ? fileURLToPath(match[1]) : path.resolve(match[1]) const slash = file.replaceAll('\\', '/') logger?.debug(`matched file: ${file}`) + + if ( + slash.includes('/packages/core/src/') || + slash.includes('/packages/core/lib/') || + slash.includes('/node_modules/koishi-plugin-chatluna/') + ) { + logger?.debug('core frame skipped before package read') + continue + } let dir = path.dirname(file) while (dir !== path.dirname(dir)) { const pkg = path.join(dir, 'package.json') if (fs.existsSync(pkg)) { logger?.debug(`package.json found: ${pkg}`) const name = JSON.parse(fs.readFileSync(pkg, 'utf8')).name logger?.debug(`package name: ${String(name)}`) if (typeof name === 'string') { const base = name.split('/').pop()?.replaceAll('_', '-') if (!base?.startsWith('koishi-plugin-')) { logger?.debug('package ignored: not a koishi plugin') break } const source = base.slice('koishi-plugin-'.length) logger?.debug(`package source: ${String(source)}`) if ( source && !name.startsWith('`@koishijs/`') && !name.startsWith('`@cordisjs/`') && source !== 'chatluna' ) { const result = source.endsWith('-entry-point') ? source.slice(0, -'-entry-point'.length) : source logger?.debug(`source selected: ${result}`) return result } if (source === 'chatluna') { fallback = source logger?.debug('fallback source set: chatluna') } else { logger?.debug('package ignored') } } break } dir = path.dirname(dir) } - - if ( - slash.includes('/packages/core/src/') || - slash.includes('/packages/core/lib/') || - slash.includes('/node_modules/koishi-plugin-chatluna/') - ) { - logger?.debug('core frame skipped after package read') - continue - } }🤖 Prompt for 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. In `@packages/core/src/utils/usage_source.ts` around lines 31 - 78, Move the early-path filtering so we skip core frames before doing any filesystem traversal or package.json reads: check the existing `slash` string for '/packages/core/src/', '/packages/core/lib/' or '/node_modules/koishi-plugin-chatluna/' at the top of the loop (before computing `dir = path.dirname(file)` and before calling `fs.existsSync`/`fs.readFileSync`/`JSON.parse`), log the same debug message (currently "core frame skipped after package read") and `continue` immediately when matched; this preserves the existing behavior while avoiding unnecessary I/O in the code paths around variables `slash`, `dir`, `pkg`, and the package name parsing logic.
7-82: 💤 Low value调试日志过于详细,可能影响生产环境性能
函数在 Line 8, 15, 21, 29, 35, 37, 41, 46, 56, 61, 63, 76, 81 添加了大量
logger?.debug调用。虽然这些日志对开发调试有帮助,但在生产环境中会在每次堆栈解析时产生多条日志输出,可能影响性能并增加日志噪音。建议将部分非关键日志移除,或者仅在启用详细调试模式时输出。🤖 Prompt for 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. In `@packages/core/src/utils/usage_source.ts` around lines 7 - 82, The function in usage_source.ts emits excessive logger?.debug calls while parsing stack (many calls around variables stack, fallback, match, file, pkg, name, source), which is noisy in production; remove non-essential debug logs (keep only top-level entry/exit and final selected source) and gate detailed per-line logs behind a single verbose check (e.g., const verbose = logger?.isLevelEnabled?.('debug') ?? Boolean(process.env.KOISHI_DEBUG_STACK)) and replace logger?.debug(...) with if (verbose) logger.debug(...). Also collapse repeated trivial logs (like “stack line ignored”, "package ignored", "core frame skipped after package read") so only meaningful events are logged (package.json found, package name, source selected, fallback result).packages/core/src/types.ts (1)
256-270: 💤 Low value命名常量仅使用一次,建议内联
FIXED_FIELD_LABEL常量(Line 256-260)仅在ConstraintFixedError构造函数(Line 267)中使用了一次。根据编码指南"不定义仅使用一次的命名常量,应在调用处直接内联字面值",建议将映射逻辑内联到错误构造函数中。♻️ 建议内联常量
-const FIXED_FIELD_LABEL: Record<ConstraintFixedField, string> = { - model: 'Model', - preset: 'Preset', - chatMode: 'Chat mode' -} - export class ConstraintFixedError extends Error { constructor( public readonly field: ConstraintFixedField, public readonly value: string ) { - super(`${FIXED_FIELD_LABEL[field]} is fixed to ${value}.`) + const label = field === 'model' ? 'Model' : field === 'preset' ? 'Preset' : 'Chat mode' + super(`${label} is fixed to ${value}.`) this.name = 'ConstraintFixedError' } }🤖 Prompt for 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. In `@packages/core/src/types.ts` around lines 256 - 270, FIXED_FIELD_LABEL is defined but only used once in the ConstraintFixedError constructor; inline the mapping there and remove the unused constant: inside the ConstraintFixedError constructor (class ConstraintFixedError) replace the use of FIXED_FIELD_LABEL[field] with an inline Record/lookup literal keyed by ConstraintFixedField (e.g., { model: 'Model', preset: 'Preset', chatMode: 'Chat mode' }[field]) so the error message remains the same, and then delete the now-unused FIXED_FIELD_LABEL declaration.
🤖 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 `@packages/core/src/llm-core/agent/agent.ts`:
- Around line 130-137: built.guildId 不应回退到 session.channelId 因为会把私聊 channel
当作群聚合维度污染统计;修改构建逻辑(在构造 built 对象的位置,相关于 built.guildId / built.session)使
built.guildId 仅等于 input.session?.guildId(不要用 channelId 兜底),并新增或保留一个独立的
built.channelId 字段等于 input.session?.channelId,以保持与 AgentRunContext(ctx.guildId
未兜底)语义一致,让聚合层按需选择 guildId 或 channelId。
In `@packages/core/src/llm-core/chain/agent_chat_chain.ts`:
- Around line 224-232: The code currently assigns the full Koishi Session into
requests['variables']['built'].session and then aliases
requests['variables_hide'] = requests['variables'], which risks leaking
sensitive session data; change this so requests['variables']['built'] only
contains the minimal flattened fields (conversationId, requestId, userId,
guildId, chatPlatform) and remove or avoid adding the full session object there,
and instead place the full session (if absolutely needed) into
requests['configurable'] (e.g., requests['configurable'].session) so that
session is not part of requests['variables']/requests['variables_hide'] used for
prompt rendering and logging; update any code that reads built.session to
reference requests['configurable'].session where appropriate.
In `@packages/core/src/llm-core/platform/model.ts`:
- Around line 236-243: The failure path currently reports promptTokens as 0,
undercounting usage; update the failure reporting so _reportFailedUsage(...)
receives the known promptTokens (from the earlier promptTokens variable computed
via cropMessages in sendRequest/sendStreamingRequest) and mark them as
estimated; for streaming failures also add any accumulated output tokens (e.g.,
the running outputTokens counter) when calling _reportFailedUsage, and ensure
callers of _reportFailedUsage (the branches that increment failedCalls) pass
these token values rather than leaving promptTokens at 0 so failedCalls reflect
estimated input/output token usage.
- Around line 1279-1286: The guildId assignment is incorrectly falling back to
channelId values (built?.channelId, cfg?.agentContext?.channelId,
session?.channelId), mixing channel and guild dimensions; remove those channelId
fallbacks from the guildId expression in the object (the guildId property in
model.ts) so guildId is only derived from built?.guildId, cfg?.guildId,
cfg?.agentContext?.guildId, or session?.guildId; ensure channelId remains mapped
only to the channelId field elsewhere (do not delete channelId usage globally,
just stop using it as a fallback for guildId).
In `@packages/extension-usage/src/index.ts`:
- Around line 231-275: The current search method pulls all rows for the
createdAt range and does in-process filtering/sorting/pagination; change it so
filters/sorting/limit/offset are pushed into the database query instead of
filtering in memory. In practice, update the call to
this.ctx.database.get('chatluna_usage', ...) inside search (and similarly the
list path referenced) to build a DB query/object using query.start/query.end
plus conditional predicates for source, model, platform, chatPlatform, callType,
guildId, userId, success, estimated, and a keyword $or (e.g. regex) across
source/platform/chatPlatform/model/conversationId/requestId/userId/guildId; also
apply requested sort and limit/offset there so the returned rows are already
filtered/paginated and you can remove the large in-memory .filter(...) block.
Ensure types from withDefaults and existing callers remain compatible when
converting client-side checks to DB predicates.
- Around line 278-301: withDefaults() is computing start/end using local Date
arithmetic while dateKey() buckets with toISOString()/UTC, causing near-midnight
records to fall into wrong buckets; fix by computing UTC-aligned start and end
boundaries in withDefaults (use UTC start-of-day / start-of-month /
start-of-year logic or Date.UTC / setUTCHours(0,0,0,0) and analogous
UTC-month/year adjustments) so the query range uses the same timezone as
dateKey(), and ensure the same UTC-based logic is applied to any related range
code noted at the later block (lines 358-361) so all range computation and
bucketing (functions withDefaults and dateKey) are consistent.
---
Duplicate comments:
In `@packages/core/src/llm-core/platform/rerank.ts`:
- Around line 183-200: The estimated flag logic in rerank.ts is wrong: compute
estimated based on whether we had to call estimateTextTokens (i.e., if
usage?.input_tokens == null && usage?.total_tokens == null) rather than
requiring all three fields to be null; update the calculation of estimated
accordingly so that when inputTokens is derived via await
estimateTextTokens(input) you set estimated = true, and keep the rest of the
report payload construction (inputTokens, output: usage?.output_tokens ?? 0,
total: usage?.total_tokens ?? inputTokens) and the this._report call unchanged;
adjust the code around the estimated and inputTokens assignments (references:
estimated, inputTokens, estimateTextTokens, this._report) to reflect this new
condition.
---
Nitpick comments:
In `@packages/core/src/types.ts`:
- Around line 256-270: FIXED_FIELD_LABEL is defined but only used once in the
ConstraintFixedError constructor; inline the mapping there and remove the unused
constant: inside the ConstraintFixedError constructor (class
ConstraintFixedError) replace the use of FIXED_FIELD_LABEL[field] with an inline
Record/lookup literal keyed by ConstraintFixedField (e.g., { model: 'Model',
preset: 'Preset', chatMode: 'Chat mode' }[field]) so the error message remains
the same, and then delete the now-unused FIXED_FIELD_LABEL declaration.
In `@packages/core/src/utils/usage_source.ts`:
- Around line 31-78: Move the early-path filtering so we skip core frames before
doing any filesystem traversal or package.json reads: check the existing `slash`
string for '/packages/core/src/', '/packages/core/lib/' or
'/node_modules/koishi-plugin-chatluna/' at the top of the loop (before computing
`dir = path.dirname(file)` and before calling
`fs.existsSync`/`fs.readFileSync`/`JSON.parse`), log the same debug message
(currently "core frame skipped after package read") and `continue` immediately
when matched; this preserves the existing behavior while avoiding unnecessary
I/O in the code paths around variables `slash`, `dir`, `pkg`, and the package
name parsing logic.
- Around line 7-82: The function in usage_source.ts emits excessive
logger?.debug calls while parsing stack (many calls around variables stack,
fallback, match, file, pkg, name, source), which is noisy in production; remove
non-essential debug logs (keep only top-level entry/exit and final selected
source) and gate detailed per-line logs behind a single verbose check (e.g.,
const verbose = logger?.isLevelEnabled?.('debug') ??
Boolean(process.env.KOISHI_DEBUG_STACK)) and replace logger?.debug(...) with if
(verbose) logger.debug(...). Also collapse repeated trivial logs (like “stack
line ignored”, "package ignored", "core frame skipped after package read") so
only meaningful events are logged (package.json found, package name, source
selected, fallback result).
In `@packages/extension-usage/client/charts/model-line.ts`:
- Line 11: The parameter name chatluna_usage in the options({ chatluna_usage })
destructuring violates camelCase lint rules; update the destructuring to rename
the key to a camelCase local variable (e.g., options({ chatluna_usage:
chatlunaUsage })) and then use that new local symbol (chatlunaUsage) inside the
options function; apply the same pattern to the other files mentioned
(token-line.ts, source-bar.ts, model-pie.ts) to keep naming consistent.
- Around line 13-15: Replace the hardcoded slice(0, 5) with a named constant
(e.g., TOP_MODEL_COUNT) and use that constant when building the models array in
model-line.ts (the expression starting with const models =
chatluna_usage.models.slice...). Declare TOP_MODEL_COUNT near the top of the
module (or in a shared config if other charts like model-pie need the same
value) so the max shown models is configurable and consistent across charts;
update any other occurrences (e.g., model-pie) to reference the same constant.
In `@packages/extension-usage/client/charts/token-line.ts`:
- Line 10: The parameter name chatluna_usage in the options destructuring
violates camelCase; change the destructured name to usage (e.g., options({
chatluna_usage: usage }) or directly options({ usage }) if the caller provides
that) and update all references of chatluna_usage.timeline.map(...) to
usage.timeline.map(...) (there are three occurrences) so the function (options)
and its internal usage variable follow camelCase and lint rules.
- Around line 22-54: chatluna_usage.timeline is being mapped four separate times
(xAxis.data and three series data); refactor to a single loop that builds arrays
for dates, inputTokens, outputTokens and totalTokens and then assign those
arrays to xAxis.data and each series.data to avoid repeated traversal; update
the code around xAxis and series (the mapping logic referencing
chatluna_usage.timeline, xAxis, and series entries named '输入','输出','总量') to use
the prebuilt arrays.
In `@packages/extension-usage/client/charts/utils.ts`:
- Around line 52-75: The Tooltip.item and Tooltip.axis helpers are using a
double cast to echarts.TooltipComponentOption which hides real type mismatches;
update the return types and formatter signatures to match echarts' definitions
instead of using "as unknown as". Specifically, import or reference
echarts.TooltipComponentOption and change item<T> to return an object conforming
to that interface (e.g., set formatter: (params: any |
echarts.TooltipFormatterCallbackParams) => string or the exact echarts formatter
type) and similarly update axis<T> formatter to the echarts axis formatter
signature, removing the "as unknown as" casts so the returned object shape
(trigger, axisPointer, formatter) satisfies echarts.TooltipComponentOption at
compile time (adjust generic T and parameter shapes in Tooltip.item and
Tooltip.axis to match echarts types).
- Around line 25-45: The render logic redundantly checks store.chatluna_usage
twice which can lead to inconsistent messages when options(store) returns a
falsy value; instead base the empty-state text on the computed option variable.
Update the conditional inside the default slot of the k-card render so that it
uses option (set by options(store)) to decide whether to render VChart or the
chart-empty div and change the text to reflect option being absent (e.g.,
"暂无用量数据" vs "正在加载用量数据") accordingly; touch the code around the option assignment
and the default slot where VChart is instantiated (references: option,
options(store), store.chatluna_usage, VChart) to remove the duplicate
store.chatluna_usage check.
In `@packages/extension-usage/client/state.ts`:
- Around line 21-30: The catch in refresh() swallows the error; update it to
capture the error object (e.g., catch (err)) and log the full error for
observability and debugging, then show a user-friendly message; specifically, in
refresh() after await send('chatluna-usage/query', query) catch the error, call
console.error or your logger with the error and context (including query and any
relevant state like store.chatluna_usage), and keep the ElMessage.error call
(optionally append a short err.message) before finally clearing loading.value.
- Around line 56-81: resetFilters 当前把 query 的默认值硬编码一遍,和最初创建 query
的默认值重复,易导致维护不一致;将初始默认值提取为一个共享常量(例如 DEFAULT_QUERY)并在初始化 query 时和 resetFilters
中复用(在 resetFilters 使用 Object.assign(query, DEFAULT_QUERY) 以保持响应式对象而不是替换引用),同时保留对
range.value 和 refresh() 的现有处理;更新代码中与 query
初始化相关的函数/变量(query、resetFilters、range、refresh)以引用该常量即可消除重复。
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6c8c3ab1-9720-42c5-89ed-1792d8d44822
⛔ Files ignored due to path filters (2)
packages/extension-usage/client/tsconfig.jsonis excluded by!**/*.jsonpackages/extension-usage/package.jsonis excluded by!**/*.json
📒 Files selected for processing (66)
packages/adapter-azure-openai/src/client.tspackages/adapter-claude/src/client.tspackages/adapter-deepseek/src/client.tspackages/adapter-dify/src/client.tspackages/adapter-doubao/src/client.tspackages/adapter-gemini/src/client.tspackages/adapter-hunyuan/src/client.tspackages/adapter-ollama/src/client.tspackages/adapter-openai-like/src/client.tspackages/adapter-openai/src/client.tspackages/adapter-qwen/src/client.tspackages/adapter-rwkv/src/client.tspackages/adapter-spark/src/client.tspackages/adapter-wenxin/src/client.tspackages/adapter-zhipu/src/client.tspackages/core/src/chains/chain.tspackages/core/src/index.tspackages/core/src/llm-core/agent/agent.tspackages/core/src/llm-core/chain/agent_chat_chain.tspackages/core/src/llm-core/chain/chat_chain.tspackages/core/src/llm-core/chat/app.tspackages/core/src/llm-core/chat/infinite_context.tspackages/core/src/llm-core/memory/message/database_history.tspackages/core/src/llm-core/platform/client.tspackages/core/src/llm-core/platform/model.tspackages/core/src/llm-core/platform/rerank.tspackages/core/src/llm-core/platform/service.tspackages/core/src/llm-core/platform/usage.tspackages/core/src/middlewares/chat/message_delay.tspackages/core/src/middlewares/chat/rollback_chat.tspackages/core/src/middlewares/conversation/request_conversation.tspackages/core/src/middlewares/conversation/resolve_conversation.tspackages/core/src/middlewares/preset/delete_preset.tspackages/core/src/middlewares/system/conversation_manage.tspackages/core/src/migration/room_to_conversation.tspackages/core/src/migration/validators.tspackages/core/src/services/chat.tspackages/core/src/services/conversation.tspackages/core/src/services/conversation_runtime.tspackages/core/src/services/types.tspackages/core/src/types.tspackages/core/src/utils/archive.tspackages/core/src/utils/conversation.tspackages/core/src/utils/string.tspackages/core/src/utils/usage_source.tspackages/core/tests/conversation-archive.spec.tspackages/core/tests/conversation-migration.spec.tspackages/core/tests/conversation-resolve.spec.tspackages/core/tests/conversation-service.spec.tspackages/core/tests/conversation-utils.spec.tspackages/core/tests/helpers.tspackages/extension-usage/client/charts/echarts.tspackages/extension-usage/client/charts/index.scsspackages/extension-usage/client/charts/index.tspackages/extension-usage/client/charts/model-line.tspackages/extension-usage/client/charts/model-pie.tspackages/extension-usage/client/charts/source-bar.tspackages/extension-usage/client/charts/token-line.tspackages/extension-usage/client/charts/utils.tspackages/extension-usage/client/home.vuepackages/extension-usage/client/index.tspackages/extension-usage/client/numbers/index.vuepackages/extension-usage/client/numbers/numeric.vuepackages/extension-usage/client/state.tspackages/extension-usage/src/index.tspackages/service-embeddings/src/embeddings/huggingface.ts
✅ Files skipped from review due to trivial changes (16)
- packages/extension-usage/client/charts/index.scss
- packages/core/src/utils/conversation.ts
- packages/core/src/middlewares/chat/rollback_chat.ts
- packages/core/src/middlewares/conversation/request_conversation.ts
- packages/core/src/utils/archive.ts
- packages/core/src/llm-core/chat/app.ts
- packages/core/tests/conversation-service.spec.ts
- packages/core/src/migration/room_to_conversation.ts
- packages/core/tests/helpers.ts
- packages/core/src/index.ts
- packages/core/src/utils/string.ts
- packages/core/src/middlewares/system/conversation_manage.ts
- packages/core/src/chains/chain.ts
- packages/core/src/migration/validators.ts
- packages/core/src/services/conversation_runtime.ts
- packages/core/src/middlewares/preset/delete_preset.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/extension-usage/client/state.ts (1)
31-45:⚠️ Potential issue | 🟠 Major | ⚡ Quick win为刷新与清理请求增加统一序号,避免并发回写旧结果。
refresh()和clearHistory()都会异步写入store.chatluna_usage/loading,当前缺少先后顺序保护。定时刷新与清理并发时,旧响应可能晚到并覆盖新数据。建议修复
let timer: ReturnType<typeof setTimeout> | undefined +let reqId = 0 export async function refresh() { + const id = ++reqId if (timer) { clearTimeout(timer) timer = undefined } try { loading.value = true - store.chatluna_usage = await send('chatluna-usage/query', query) + const data = await send('chatluna-usage/query', query) + if (id === reqId) store.chatluna_usage = data } catch { ElMessage.error('查询 ChatLuna 用量失败') } finally { - loading.value = false + if (id === reqId) loading.value = false } } export async function clearHistory() { + const id = ++reqId + if (timer) { + clearTimeout(timer) + timer = undefined + } try { await ElMessageBox.confirm( '这会删除所有 ChatLuna 用量历史数据,无法撤销。', @@ try { loading.value = true await send('chatluna-usage/cleanup') - store.chatluna_usage = await send('chatluna-usage/query', query) + const data = await send('chatluna-usage/query', query) + if (id === reqId) store.chatluna_usage = data ElMessage.success('已清除 ChatLuna 用量历史数据') } catch { ElMessage.error('清除 ChatLuna 用量历史数据失败') } finally { - loading.value = false + if (id === reqId) loading.value = false } }Also applies to: 80-106
🤖 Prompt for 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. In `@packages/extension-usage/client/state.ts` around lines 31 - 45, refresh() and clearHistory() can have race conditions where an older async response overwrites newer state; introduce a shared incrementing sequence ID (e.g., requestSeq) and capture its value into a local currentSeq at the start of each async operation, then only assign to store.chatluna_usage and loading if currentSeq === requestSeq to ensure only the latest request writes back; update both refresh() and clearHistory() (and any other async methods that set store.chatluna_usage/loading) to increment requestSeq before awaiting send('chatluna-usage/query', ...) and check the captured seq before mutating state.packages/core/src/llm-core/platform/usage.ts (1)
84-90:⚠️ Potential issue | 🟠 Major | ⚡ Quick win不要把新增缓存指标做成公共事件的必填字段。
ModelUsageTokens是对外导出的事件契约。这里把cacheRead/cacheCreation设成必填后,直接发chatluna/model-usage的外部插件或脚本会在升级后立刻不兼容;JS 调用方即使能跑,也会把缺失字段一路传到存储/聚合层。建议把这两个字段在公共类型里保持可选,并在 reporter 或落库层统一归一化成0。🤖 Prompt for 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. In `@packages/core/src/llm-core/platform/usage.ts` around lines 84 - 90, The exported interface ModelUsageTokens currently makes cacheRead and cacheCreation required, which will break external callers; change these two properties to optional (cacheRead?: number, cacheCreation?: number) in ModelUsageTokens and handle normalization to 0 inside the reporting/ingestion layer (e.g., in the reporter or storage functions that consume ModelUsageTokens) so callers can omit them while the internal pipeline fills missing values with 0 before aggregation or persistence.
♻️ Duplicate comments (1)
packages/extension-usage/src/index.ts (1)
26-37:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift当前列表查询仍然会随数据量线性变慢。
现在
search()还是只按createdAt回表,再在进程内基于tokens.*做过滤、排序和分页;而这次又把 token 指标收进了json列,后续更难把estimated、cachedTokens和 token 排序安全地下推到数据库。chatluna_usage是持续追加表,数据一多后面板接口会越来越慢,分页也失去意义。建议至少把list路径的过滤、排序、limit/offset下推到 DB;如果这些 token 维度要长期参与查询,最好拆成独立列。Also applies to: 248-252, 323-342
🤖 Prompt for 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. In `@packages/extension-usage/src/index.ts` around lines 26 - 37, The current design stores token metrics as a JSON column (tokens) and performs filtering/sorting/pagination in-process inside the search() / list path for chatluna_usage, causing linear slowdowns as the table grows; refactor by extracting token fields (e.g., input, output, total, estimated, cacheRead, cacheCreation, cachedTokens) into first-class DB columns, add appropriate indexes, and update the search()/list query to push WHERE filters, ORDER BY and LIMIT/OFFSET into the SQL/ORM query so filtering/sorting/paging happens in the database; also include a migration to populate the new columns from existing tokens JSON and update any code that reads tokens to use the new columns or a lightweight projection.
🧹 Nitpick comments (1)
packages/extension-usage/client/charts/token-line.ts (1)
463-478: ⚡ Quick win分页拉取目前是串行的,数据量大时加载会明显变慢
Line 463-478 每一页都
await上一页完成后再请求下一页。可先拿第一页确定总页数,再并行请求剩余页。⚡ 建议重构
- while (true) { - const list = await send('chatluna-usage/list', { - ...query, - page, - pageSize: 1000 - }) - - result.push(...list.rows) - if ( - result.length >= list.total || - !list.rows.length - ) { - break - } - page += 1 - } + const first = await send('chatluna-usage/list', { + ...query, + page: 1, + pageSize: 1000 + }) + result.push(...first.rows) + const totalPages = Math.ceil(first.total / 1000) + if (totalPages > 1) { + const rest = await Promise.all( + Array.from( + { length: totalPages - 1 }, + (_, i) => + send('chatluna-usage/list', { + ...query, + page: i + 2, + pageSize: 1000 + }) + ) + ) + rest.forEach((list) => result.push(...list.rows)) + }As per coding guidelines "ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE".
🤖 Prompt for 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. In `@packages/extension-usage/client/charts/token-line.ts` around lines 463 - 478, The current loop fetches pages serially causing slow loads; first call send('chatluna-usage/list') for page=1 to get list.total and result from list.rows, compute totalPages = Math.ceil(list.total / pageSize), then create an array of send('chatluna-usage/list', { ...query, page: p, pageSize: 1000 }) promises for pages 2..totalPages, await Promise.all to fetch them in parallel, push all returned rows into result and handle empty rows/edge cases; update references to page, pageSize, result and list.total/list.rows accordingly so behavior matches the original.
🤖 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 `@packages/extension-usage/client/charts/token-line.ts`:
- Around line 64-67: The tooltip HTML currently concatenates unescaped text
(item.seriesName and item.marker) into a string in token-line.ts which allows
XSS; update the tooltip generation to escape HTML special characters or build
the tooltip via DOM APIs and set text via textContent (or run the value through
a sanitizer) instead of injecting item.seriesName/item.marker directly into the
template string used for the tooltip; ensure Number(item.value).toLocaleString()
remains safely rendered as text too when switching to DOM/textContent.
---
Outside diff comments:
In `@packages/core/src/llm-core/platform/usage.ts`:
- Around line 84-90: The exported interface ModelUsageTokens currently makes
cacheRead and cacheCreation required, which will break external callers; change
these two properties to optional (cacheRead?: number, cacheCreation?: number) in
ModelUsageTokens and handle normalization to 0 inside the reporting/ingestion
layer (e.g., in the reporter or storage functions that consume ModelUsageTokens)
so callers can omit them while the internal pipeline fills missing values with 0
before aggregation or persistence.
In `@packages/extension-usage/client/state.ts`:
- Around line 31-45: refresh() and clearHistory() can have race conditions where
an older async response overwrites newer state; introduce a shared incrementing
sequence ID (e.g., requestSeq) and capture its value into a local currentSeq at
the start of each async operation, then only assign to store.chatluna_usage and
loading if currentSeq === requestSeq to ensure only the latest request writes
back; update both refresh() and clearHistory() (and any other async methods that
set store.chatluna_usage/loading) to increment requestSeq before awaiting
send('chatluna-usage/query', ...) and check the captured seq before mutating
state.
---
Duplicate comments:
In `@packages/extension-usage/src/index.ts`:
- Around line 26-37: The current design stores token metrics as a JSON column
(tokens) and performs filtering/sorting/pagination in-process inside the
search() / list path for chatluna_usage, causing linear slowdowns as the table
grows; refactor by extracting token fields (e.g., input, output, total,
estimated, cacheRead, cacheCreation, cachedTokens) into first-class DB columns,
add appropriate indexes, and update the search()/list query to push WHERE
filters, ORDER BY and LIMIT/OFFSET into the SQL/ORM query so
filtering/sorting/paging happens in the database; also include a migration to
populate the new columns from existing tokens JSON and update any code that
reads tokens to use the new columns or a lightweight projection.
---
Nitpick comments:
In `@packages/extension-usage/client/charts/token-line.ts`:
- Around line 463-478: The current loop fetches pages serially causing slow
loads; first call send('chatluna-usage/list') for page=1 to get list.total and
result from list.rows, compute totalPages = Math.ceil(list.total / pageSize),
then create an array of send('chatluna-usage/list', { ...query, page: p,
pageSize: 1000 }) promises for pages 2..totalPages, await Promise.all to fetch
them in parallel, push all returned rows into result and handle empty rows/edge
cases; update references to page, pageSize, result and list.total/list.rows
accordingly so behavior matches the original.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 96acec62-6900-406c-9ffe-e9b2d425de11
📒 Files selected for processing (14)
packages/core/src/llm-core/platform/model.tspackages/core/src/llm-core/platform/usage.tspackages/extension-usage/client/charts/echarts.tspackages/extension-usage/client/charts/index.scsspackages/extension-usage/client/charts/index.tspackages/extension-usage/client/charts/token-line.tspackages/extension-usage/client/charts/utils.tspackages/extension-usage/client/home.vuepackages/extension-usage/client/index.tspackages/extension-usage/client/numbers/index.vuepackages/extension-usage/client/sources/index.vuepackages/extension-usage/client/state.tspackages/extension-usage/client/theme.tspackages/extension-usage/src/index.ts
✅ Files skipped from review due to trivial changes (1)
- packages/extension-usage/client/theme.ts
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
packages/core/src/llm-core/platform/model.ts (1)
595-610:⚠️ Potential issue | 🟠 Major | ⚡ Quick win流式失败路径仍会把已生成输出 token 记为 0
_reportFailedUsage只接收promptTokens,并把usageMetadata.output_tokens固定为 0。流式分支在 Line 315 可能已经产出 chunk 后失败,此时latestTokenUsage.output_tokens被丢弃,失败请求会系统性低估用量。💡 建议修复
- if (reportUsage) { - await this._reportFailedUsage(options, promptTokens) - } + if (reportUsage) { + await this._reportFailedUsage( + options, + promptTokens, + latestTokenUsage.output_tokens + ) + } - private async _reportFailedUsage( - options: this['ParsedCallOptions'], - promptTokens = 0 - ) { + private async _reportFailedUsage( + options: this['ParsedCallOptions'], + promptTokens = 0, + outputTokens = 0 + ) { if (this._report == null) return try { await this._report({ callType: 'llm', usageMetadata: { input_tokens: promptTokens, - output_tokens: 0, - total_tokens: promptTokens + output_tokens: outputTokens, + total_tokens: promptTokens + outputTokens }, - estimated: promptTokens > 0, + estimated: promptTokens > 0 || outputTokens > 0, success: false, context: usageContextFromOptions(options) })🤖 Prompt for 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. In `@packages/core/src/llm-core/platform/model.ts` around lines 595 - 610, _reportFailedUsage currently only takes promptTokens and sets usageMetadata.output_tokens to 0, which undercounts usage for streaming calls that already produced chunks; update _reportFailedUsage to accept or obtain the latest token usage (use the existing latestTokenUsage or a new parameter) and set usageMetadata.output_tokens to latestTokenUsage.output_tokens (and usageMetadata.total_tokens to promptTokens + output_tokens); ensure estimated is computed from the true token sum and call sites that invoke _reportFailedUsage from streaming flows (e.g., where latestTokenUsage is available) pass the token info so failures report correct output_tokens and total_tokens.
🤖 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 `@packages/extension-usage/client/charts/model-pie.vue`:
- Around line 106-117: The tooltip formatter in model-pie.vue currently injects
row.name directly into the returned HTML string (formatter -> row.name),
creating an XSS risk; fix by HTML-escaping row.name before concatenation (escape
&, <, >, ", ' at minimum) and use the escaped value in the array with
fmt(row.value), fmt(row.calls) and pct(row.value / tokens.value); add a small
utility (e.g., escapeHtml) or reuse an existing sanitizer and call it inside the
formatter so the returned string is safe for innerHTML rendering.
In `@packages/extension-usage/client/charts/token-line.ts`:
- Around line 485-489: 在分页循环中每次直接展开响应式变量 query
会导致当用户在拉取过程中修改筛选条件时不同页使用不一致的查询快照,导致最终 rows 混入不同条件的数据;在执行分页请求之前先对查询条件做一次不可变快照(例如
const fixedQuery = { ...query } 在循环外),然后在循环体中调用 send('chatluna-usage/list', {
...fixedQuery, page, pageSize: 1000 }) 并将返回合并到 rows,这样保证所有页使用同一查询条件快照并避免响应式
query 被中途修改影响结果。
---
Duplicate comments:
In `@packages/core/src/llm-core/platform/model.ts`:
- Around line 595-610: _reportFailedUsage currently only takes promptTokens and
sets usageMetadata.output_tokens to 0, which undercounts usage for streaming
calls that already produced chunks; update _reportFailedUsage to accept or
obtain the latest token usage (use the existing latestTokenUsage or a new
parameter) and set usageMetadata.output_tokens to latestTokenUsage.output_tokens
(and usageMetadata.total_tokens to promptTokens + output_tokens); ensure
estimated is computed from the true token sum and call sites that invoke
_reportFailedUsage from streaming flows (e.g., where latestTokenUsage is
available) pass the token info so failures report correct output_tokens and
total_tokens.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 1b0da9e1-ca99-4b02-b567-fff5ba398413
⛔ Files ignored due to path filters (1)
packages/extension-usage/package.jsonis excluded by!**/*.json
📒 Files selected for processing (25)
packages/core/src/llm-core/agent/agent.tspackages/core/src/llm-core/chain/agent_chat_chain.tspackages/core/src/llm-core/chain/chat_chain.tspackages/core/src/llm-core/platform/client.tspackages/core/src/llm-core/platform/model.tspackages/core/src/llm-core/platform/rerank.tspackages/core/src/llm-core/platform/usage.tspackages/core/src/utils/usage_source.tspackages/core/tests/conversation-utils.spec.tspackages/extension-usage/client/charts/echarts.tspackages/extension-usage/client/charts/index.scsspackages/extension-usage/client/charts/index.tspackages/extension-usage/client/charts/model-pie.vuepackages/extension-usage/client/charts/model-success.vuepackages/extension-usage/client/charts/token-line.tspackages/extension-usage/client/charts/utils.tspackages/extension-usage/client/home.vuepackages/extension-usage/client/index.tspackages/extension-usage/client/numbers/index.vuepackages/extension-usage/client/sources/index.vuepackages/extension-usage/client/state.tspackages/extension-usage/client/theme.tspackages/extension-usage/src/index.tspackages/shared-adapter/src/types.tspackages/shared-adapter/src/utils.ts
💤 Files with no reviewable changes (1)
- packages/shared-adapter/src/types.ts
✅ Files skipped from review due to trivial changes (1)
- packages/extension-usage/client/charts/index.ts
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
packages/extension-usage/client/charts/token-line.ts (1)
484-489:⚠️ Potential issue | 🟠 Major | ⚡ Quick win分页拉取时应固定查询快照,避免条件不一致。
循环中每次请求都展开响应式
query,如果用户在请求过程中修改了筛选条件,会导致不同页使用不同的查询条件,最终rows可能混合不同条件的数据。建议修复
const idx = ++id const result: ChatLunaUsage.Record[] = [] let page = 1 + const fixedQuery = { ...query } loading.value = true rows.value = [] try { while (true) { const list = await send('chatluna-usage/list', { - ...query, + ...fixedQuery, page, pageSize: 1000 })🤖 Prompt for 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. In `@packages/extension-usage/client/charts/token-line.ts` around lines 484 - 489, The loop uses the reactive `query` directly so if the user mutates filters mid-pagination pages will be fetched with inconsistent criteria; before entering the while loop capture a snapshot of the query (e.g., const snapshotQuery = { ...query }) and use that snapshot in the send call (replace usage of `query` in the `send('chatluna-usage/list', {...})` request) so all pages use the same fixed criteria and `rows` are not mixed across different filters.
🧹 Nitpick comments (6)
packages/extension-usage/client/home.vue (1)
1095-1097: 💤 Low value重复的 CSS 选择器
.chatluna-usage-table。
.chatluna-usage-table在 Line 1095 和 Line 1132 处重复定义。虽然不会导致运行时错误,但会增加维护难度。建议合并这两个选择器块。Also applies to: 1132-1134
🤖 Prompt for 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. In `@packages/extension-usage/client/home.vue` around lines 1095 - 1097, Duplicate CSS selector .chatluna-usage-table is defined twice in the file; locate both occurrences of the .chatluna-usage-table rule in home.vue (the selector name ".chatluna-usage-table") and merge their properties into a single rule, remove the redundant block so only one consolidated .chatluna-usage-table declaration remains, preserving all style properties from both definitions.packages/core/src/llm-core/platform/usage.ts (2)
73-77: 💤 Low value空 catch 块会静默吞掉编码器异常。
虽然回退到启发式计算是合理的,但完全静默可能导致难以诊断的问题。建议至少在 debug 级别记录一下。
try { return encoder.encode(text).length - } catch {} + } catch (e) { + logger.debug('tiktoken encode failed, using heuristic', e) + }🤖 Prompt for 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. In `@packages/core/src/llm-core/platform/usage.ts` around lines 73 - 77, The empty catch in the block that calls encoder.encode(text) silently swallows encoder exceptions; update the catch to log the caught error at debug level (including the error object and context like the text length or that encoder failed) before falling back to the heuristic path. Locate the try/catch around encoder.encode in this module (referencing encoder and encode) and replace the empty catch with a debug logging statement using the module's logger (or console.debug if no logger is available) so the error is visible for diagnostics while preserving the existing fallback behavior.
19-27: 💤 Low value
source回退逻辑可能导致'chatluna'被意外保留。当
usageSourceFromStack(currentStack)返回'chatluna'时,会尝试从备用stack解析fallback。但如果fallback也是'unknown',最终source会被设为原始的'chatluna'(来自 line 27),这可能不是预期行为。建议明确处理
'chatluna'的情况:const source = usageSourceFromStack(currentStack) const fallback = source === 'chatluna' || source === 'unknown' ? usageSourceFromStack(stack) : source const payload: ModelUsagePayload = { ...usage, - source: fallback === 'unknown' ? source : fallback, + source: fallback !== 'unknown' && fallback !== 'chatluna' ? fallback : source,🤖 Prompt for 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. In `@packages/core/src/llm-core/platform/usage.ts` around lines 19 - 27, The current logic using usageSourceFromStack(currentStack), fallback and the ModelUsagePayload 'source' field can accidentally keep 'chatluna' when source==='chatluna' and fallback==='unknown'; update the payload assignment so 'chatluna' is not preserved in that case. Specifically, compute fallback as now from usageSourceFromStack(stack) and set payload.source to: if fallback !== 'unknown' use fallback, else if source === 'chatluna' use 'unknown' (or another explicit non-chatluna sentinel), otherwise use source; reference usageSourceFromStack, currentStack, stack, fallback and payload/ModelUsagePayload to locate and change the assignment.packages/extension-usage/client/state.ts (2)
36-45: 💤 Low value
scopeRange中'day'分支的行为与其他分支不一致。
'day'分支将start设置为今天 0 点,只查询"今天"的数据;而'year'/'month'/'week'分支是往前推一段时间,查询"最近 N 天/月/年"的滚动窗口。如果'day'的意图是"最近 24 小时",应改为减去一天。- if (value === 'day') start.setHours(0, 0, 0, 0) + if (value === 'day') start.setDate(start.getDate() - 1)如果意图确实是"今天",建议在 UI 上将
'日'改为'今日'以区分。🤖 Prompt for 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. In `@packages/extension-usage/client/state.ts` around lines 36 - 45, The 'day' branch in scopeRange behaves differently from the other rolling-window branches: instead of subtracting one day it zeroes the time and returns "today"; change the 'day' branch in function scopeRange to subtract one day (start.setDate(start.getDate() - 1)) so it returns the last 24 hours consistent with 'week'/'month'/'year' behavior; keep the rest of the branches ('week','month','year') unchanged and, if the intended UX was "today" instead of "last 24 hours", update the UI label instead of changing scopeRange.
198-200: 💤 Low value百分比计算可简化。
(((value ?? 0) * 1000) / 10)等价于(value ?? 0) * 100,当前写法增加了不必要的理解成本。export function pct(value?: number) { - return `${(((value ?? 0) * 1000) / 10).toFixed(1)}%` + return `${((value ?? 0) * 100).toFixed(1)}%` }🤖 Prompt for 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. In `@packages/extension-usage/client/state.ts` around lines 198 - 200, The pct function's percentage calculation is needlessly complicated; simplify the expression in pct to use (value ?? 0) * 100 instead of (((value ?? 0) * 1000) / 10) while keeping the existing toFixed(1) formatting and trailing '%' so behavior for undefined/null remains the same and display precision is unchanged.packages/extension-usage/client/charts/token-line.ts (1)
55-62: 💤 Low value手动 HTML 转义函数可能存在边界情况。
静态分析工具标记了手动 HTML 转义的潜在风险。当前实现覆盖了主要的特殊字符,但在复杂场景下可能不够完善。在 tooltip 这种内部数据展示场景中,当前实现是可接受的,但如果未来需要处理更复杂的用户输入,建议考虑使用专门的库如
DOMPurify。🤖 Prompt for 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. In `@packages/extension-usage/client/charts/token-line.ts` around lines 55 - 62, The manual HTML-escaping in escapeHtml is brittle and flagged by static analysis; replace this ad-hoc implementation with a robust sanitizer or safe-encoding strategy: either use a vetted library like DOMPurify (call DOMPurify.sanitize on tooltip content) or avoid manual escaping by assigning content via textContent/createTextNode when rendering tooltips; update references to escapeHtml in token-line.ts to use the chosen sanitizer (or direct text assignment) so all tooltip strings are safely handled.
🤖 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 `@packages/extension-usage/client/home.vue`:
- Around line 175-184: Several table columns (props createdAt, model, platform,
source, callType) have sortable set but the changeSort function only handles
sorting for totalTokens, so client-side sorting will not trigger server queries
for those columns; either remove the sortable attribute from those column
definitions (the el-table-column elements that call time(scope.row.createdAt)
and the columns with prop="model", "platform", "source", "callType") or extend
the changeSort function to recognize these prop names and translate them into
the appropriate server-side sort parameters (update changeSort to switch on
sorter.prop or sorter.column.prop and map
createdAt/model/platform/source/callType to backend keys, then call the existing
fetch/query path used for totalTokens). Ensure the UI and server parameter names
align and update any tests or docs accordingly.
In `@packages/extension-usage/client/sources/index.vue`:
- Around line 133-145: The tooltip formatter in the formatter function of
sources/index.vue directly injects row.name into an HTML string causing XSS; add
and import a shared escapeHtml utility (e.g. export function escapeHtml in
charts/utils.ts) and replace row.name with escapeHtml(row.name) before building
the returned string so the name is properly escaped; keep the rest of the
formatter unchanged and ensure escapeHtml handles &, <, >, " and ' characters.
---
Duplicate comments:
In `@packages/extension-usage/client/charts/token-line.ts`:
- Around line 484-489: The loop uses the reactive `query` directly so if the
user mutates filters mid-pagination pages will be fetched with inconsistent
criteria; before entering the while loop capture a snapshot of the query (e.g.,
const snapshotQuery = { ...query }) and use that snapshot in the send call
(replace usage of `query` in the `send('chatluna-usage/list', {...})` request)
so all pages use the same fixed criteria and `rows` are not mixed across
different filters.
---
Nitpick comments:
In `@packages/core/src/llm-core/platform/usage.ts`:
- Around line 73-77: The empty catch in the block that calls
encoder.encode(text) silently swallows encoder exceptions; update the catch to
log the caught error at debug level (including the error object and context like
the text length or that encoder failed) before falling back to the heuristic
path. Locate the try/catch around encoder.encode in this module (referencing
encoder and encode) and replace the empty catch with a debug logging statement
using the module's logger (or console.debug if no logger is available) so the
error is visible for diagnostics while preserving the existing fallback
behavior.
- Around line 19-27: The current logic using usageSourceFromStack(currentStack),
fallback and the ModelUsagePayload 'source' field can accidentally keep
'chatluna' when source==='chatluna' and fallback==='unknown'; update the payload
assignment so 'chatluna' is not preserved in that case. Specifically, compute
fallback as now from usageSourceFromStack(stack) and set payload.source to: if
fallback !== 'unknown' use fallback, else if source === 'chatluna' use 'unknown'
(or another explicit non-chatluna sentinel), otherwise use source; reference
usageSourceFromStack, currentStack, stack, fallback and
payload/ModelUsagePayload to locate and change the assignment.
In `@packages/extension-usage/client/charts/token-line.ts`:
- Around line 55-62: The manual HTML-escaping in escapeHtml is brittle and
flagged by static analysis; replace this ad-hoc implementation with a robust
sanitizer or safe-encoding strategy: either use a vetted library like DOMPurify
(call DOMPurify.sanitize on tooltip content) or avoid manual escaping by
assigning content via textContent/createTextNode when rendering tooltips; update
references to escapeHtml in token-line.ts to use the chosen sanitizer (or direct
text assignment) so all tooltip strings are safely handled.
In `@packages/extension-usage/client/home.vue`:
- Around line 1095-1097: Duplicate CSS selector .chatluna-usage-table is defined
twice in the file; locate both occurrences of the .chatluna-usage-table rule in
home.vue (the selector name ".chatluna-usage-table") and merge their properties
into a single rule, remove the redundant block so only one consolidated
.chatluna-usage-table declaration remains, preserving all style properties from
both definitions.
In `@packages/extension-usage/client/state.ts`:
- Around line 36-45: The 'day' branch in scopeRange behaves differently from the
other rolling-window branches: instead of subtracting one day it zeroes the time
and returns "today"; change the 'day' branch in function scopeRange to subtract
one day (start.setDate(start.getDate() - 1)) so it returns the last 24 hours
consistent with 'week'/'month'/'year' behavior; keep the rest of the branches
('week','month','year') unchanged and, if the intended UX was "today" instead of
"last 24 hours", update the UI label instead of changing scopeRange.
- Around line 198-200: The pct function's percentage calculation is needlessly
complicated; simplify the expression in pct to use (value ?? 0) * 100 instead of
(((value ?? 0) * 1000) / 10) while keeping the existing toFixed(1) formatting
and trailing '%' so behavior for undefined/null remains the same and display
precision is unchanged.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: cea66fc6-16e9-4256-8c76-222511b1c500
⛔ Files ignored due to path filters (1)
packages/extension-usage/package.jsonis excluded by!**/*.json
📒 Files selected for processing (25)
packages/core/src/llm-core/agent/agent.tspackages/core/src/llm-core/chain/agent_chat_chain.tspackages/core/src/llm-core/chain/chat_chain.tspackages/core/src/llm-core/platform/client.tspackages/core/src/llm-core/platform/model.tspackages/core/src/llm-core/platform/rerank.tspackages/core/src/llm-core/platform/usage.tspackages/core/src/utils/usage_source.tspackages/core/tests/conversation-utils.spec.tspackages/extension-usage/client/charts/echarts.tspackages/extension-usage/client/charts/index.scsspackages/extension-usage/client/charts/index.tspackages/extension-usage/client/charts/model-pie.vuepackages/extension-usage/client/charts/model-success.vuepackages/extension-usage/client/charts/token-line.tspackages/extension-usage/client/charts/utils.tspackages/extension-usage/client/home.vuepackages/extension-usage/client/index.tspackages/extension-usage/client/numbers/index.vuepackages/extension-usage/client/sources/index.vuepackages/extension-usage/client/state.tspackages/extension-usage/client/theme.tspackages/extension-usage/src/index.tspackages/shared-adapter/src/types.tspackages/shared-adapter/src/utils.ts
💤 Files with no reviewable changes (1)
- packages/shared-adapter/src/types.ts
✅ Files skipped from review due to trivial changes (1)
- packages/extension-usage/client/charts/echarts.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/extension-usage/client/home.vue (1)
529-535:⚠️ Potential issue | 🟠 Major | ⚡ Quick win后端排序器还不支持这些字符串列。
Line 533 现在会把
model、platform、source、callType直接下发给服务端,但packages/extension-usage/src/index.ts的pageRows()仍然对非Date字段走Number(left) - Number(right)。这些列会得到NaN,排序结果会失效或不稳定。这里要么只放行当前后端已支持的字段,要么把服务端比较器补成字符串比较。🤖 Prompt for 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. In `@packages/extension-usage/client/home.vue` around lines 529 - 535, changeSort currently sends arbitrary prop values (e.g., "model","platform","source","callType") to the backend while pageRows() in packages/extension-usage/src/index.ts still assumes non-Date fields are numeric (Number(left)-Number(right)), causing NaN; either restrict changeSort to only send server-supported sort keys (whitelist the allowed fields in changeSort before assigning listQuery.listSortBy) or update the server-side comparator in pageRows() to detect string fields and perform a proper string comparison (localeCompare or simple <,> checks) and keep Date handling intact; reference changeSort and pageRows() when making the change.
🤖 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.
Outside diff comments:
In `@packages/extension-usage/client/home.vue`:
- Around line 529-535: changeSort currently sends arbitrary prop values (e.g.,
"model","platform","source","callType") to the backend while pageRows() in
packages/extension-usage/src/index.ts still assumes non-Date fields are numeric
(Number(left)-Number(right)), causing NaN; either restrict changeSort to only
send server-supported sort keys (whitelist the allowed fields in changeSort
before assigning listQuery.listSortBy) or update the server-side comparator in
pageRows() to detect string fields and perform a proper string comparison
(localeCompare or simple <,> checks) and keep Date handling intact; reference
changeSort and pageRows() when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f9ba18c7-7b48-4df3-85fd-c85c4d7d17f6
📒 Files selected for processing (6)
packages/core/src/llm-core/platform/model.tspackages/extension-usage/client/charts/model-pie.vuepackages/extension-usage/client/charts/token-line.tspackages/extension-usage/client/charts/utils.tspackages/extension-usage/client/home.vuepackages/extension-usage/client/sources/index.vue
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/extension-usage/client/home.vue (1)
362-395:⚠️ Potential issue | 🟠 Major | ⚡ Quick win网格布局在显示思考 Token 时会错位。
.token-cell使用grid-template-columns: repeat(3, ...)定义了 3 列网格。当reasoningTokens > 0时,会渲染 4 个元素(input、output、reasoning、cache),导致 cache 元素换行显示。建议将网格列数改为 4 列,或在显示 reasoning 时动态调整:
🐛 修复网格布局
.token-cell { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); + grid-template-columns: repeat(4, minmax(0, 1fr)); align-items: center; gap: 0.9rem; line-height: 1; font-variant-numeric: tabular-nums; - min-width: 16rem; + min-width: 20rem; white-space: nowrap; }同时需要调整 Tokens 列宽(Line 233)以适应 4 列布局:
<el-table-column prop="totalTokens" label="Tokens" - width="290" + width="360" align="right"🤖 Prompt for 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. In `@packages/extension-usage/client/home.vue` around lines 362 - 395, The grid layout for token cells is defined with three columns but rendering the reasoning token adds a fourth element, causing wrapping; update the CSS for .token-cell (and related token-item layout) to use four columns (e.g., grid-template-columns: repeat(4, ...)) or implement a conditional class when scope.row.reasoningTokens > 0 to switch to a 4-column layout, and also increase the Tokens column width used in the table header/cell style so the four-column layout fits (adjust the Tokens column width CSS rule referenced near the existing token styles). Ensure changes target .token-cell, .token-item, and the Tokens column width so input/output/reasoning/cache render on one row.
🤖 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.
Outside diff comments:
In `@packages/extension-usage/client/home.vue`:
- Around line 362-395: The grid layout for token cells is defined with three
columns but rendering the reasoning token adds a fourth element, causing
wrapping; update the CSS for .token-cell (and related token-item layout) to use
four columns (e.g., grid-template-columns: repeat(4, ...)) or implement a
conditional class when scope.row.reasoningTokens > 0 to switch to a 4-column
layout, and also increase the Tokens column width used in the table header/cell
style so the four-column layout fits (adjust the Tokens column width CSS rule
referenced near the existing token styles). Ensure changes target .token-cell,
.token-item, and the Tokens column width so input/output/reasoning/cache render
on one row.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 743636d5-a817-4bdc-822a-23eafcd62988
📒 Files selected for processing (1)
packages/extension-usage/client/home.vue
d68c986 to
390b786
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@packages/extension-usage/src/index.ts`:
- Around line 149-151: 当前实现将 label 从 row.model 改为
`${row.platform}/${row.model}`(在 Index 构造处使用的 row.platform 和 row.model),但 models
的 key 仍然是 row.model;这会让前端把带平台的 label 当作 query.model 回传导致筛选不一致并合并跨平台同名模型。请恢复
label 使用与 key 一致的值(即保持 label 为 row.model),或如果必须显示平台信息,则把显示文本与实际 query 值分离:保留
models map 的 key 为 row.model,用单独字段(例如 displayLabel 或 uiLabel)保存
`${row.platform}/${row.model}` 供 UI 展示,确保 query.model 仍取 row.model。
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 277a0933-2d0b-4d7b-909d-8ebe77ba79e2
📒 Files selected for processing (7)
packages/extension-usage/client/charts/model-pie.vuepackages/extension-usage/client/charts/model-success.vuepackages/extension-usage/client/charts/token-line.tspackages/extension-usage/client/home.vuepackages/extension-usage/client/numbers/index.vuepackages/extension-usage/client/sources/index.vuepackages/extension-usage/src/index.ts
💤 Files with no reviewable changes (1)
- packages/extension-usage/client/numbers/index.vue
- 在 core 模型层增加 chatluna/model-usage 事件和 usage reporter,覆盖 LLM、stream、embeddings、reranker 调用。 - 增加 usage source 推断与上下文传递,支持 ChatLuna 会话和外部插件自动归属调用来源。 - 新增 chatluna-usage 插件,负责持久化用量记录,并导出按来源/模型聚合与清理 helper。 - 补充 usage 回归测试,覆盖 provider usage、fallback 估算、stream、source 推断、聚合与清理场景。
- 为 usage source 的 package.json 解析增加失败兜底,避免异常中断来源推断。 - 为 token encoder 预热补充错误处理,避免未处理 Promise 拒绝。 - 拆分 test_model 命令逻辑,降低 CodeFactor 标出的复杂度。 - 调整 usage source 默认值写法,规避 CodeFactor 的格式检查失败。
1. 扩展模型用量上报与 usage 存储,记录缓存 Token 并在列表中返回派生统计字段。 2. 重构 extension-usage 首页 WebUI,优化总请求数、成功率、今日请求与 Token 统计卡片。 3. 将模型数据分析调整为小时级多模型图表,统一颜色映射、图例与 tooltip 展示。 4. 新增插件消耗分布饼图和模型明细列表,清理旧图表入口与未使用前端组件。
- 按调用栈推断出的来源隔离模型实例和 usage reporter 缓存 - reporter 内部保留 createModel 时的调用栈作为兜底来源 - 移除 ModelUsageInput.source 和可变 reporter.stack,避免调用方传 source 或共享 reporter 覆盖来源
- Refine usage summary cards and token statistics layout. - Polish model and plugin consumption charts with consistent titles and chart styles. - Decouple detail filters from dashboard statistics and simplify detail columns.
…48193412 - Snapshot reactive query before pagination loop to prevent mid-loop mutation - Pass output tokens to _reportFailedUsage for streaming failures - Extend changeSort to support all sortable columns - Fix XSS in tooltip formatters (sources/index.vue, model-pie.vue) - Extract shared escapeHtml to charts/utils.ts
- Use lightbulb icon for reasoning tokens (conditionally shown) - Use stacked-layers icon for cached tokens - Show reasoning tokens in token-cell when value > 0
- Use lightbulb icon for reasoning tokens (conditionally shown) - Use stacked-layers icon for cached tokens - Show reasoning tokens in token-cell when value > 0 - Remove dashboard-shell wrapper card, keep title and segment inline
- Add dark mode overrides for el-tag (transparent bg, colored border/text) - Fix pageRows sort comparator to handle string fields with localeCompare - Restructure token-cell into two rows: input+output / reasoning+cache - Always show reasoning tokens (0 when absent) for consistent layout
- Remove SVG icons from metric cards (total requests, success rate, token stats, today requests) - Remove chart title icon from model data analysis section - Remove search icon from usage detail table header
- Revert server-side label to row.model (keeps key/label consistent for query) - Display platform/model in chart series names, tooltips, and legends - Ensures query.model filter still matches raw model name
b5cdc96 to
a3ea74f
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/extension-usage/client/charts/token-line.ts (1)
253-253: 💤 Low value修复格式化问题。
静态分析工具检测到多处代码行超出 prettier 配置的最大行宽。请运行
yarn lint-fix或手动换行。♻️ 建议修复示例(以 Line 253 为例)
return { - name: model.platform ? `${model.platform}/${model.label}` : model.label, + name: model.platform + ? `${model.platform}/${model.label}` + : model.label, type: 'bar',Also applies to: 302-302, 313-313, 328-328, 390-390, 440-440
🤖 Prompt for 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. In `@packages/extension-usage/client/charts/token-line.ts` at line 253, Multiple lines (e.g., the object property assignment "name: model.platform ? `${model.platform}/${model.label}` : model.label," and similar expressions at lines 302, 313, 328, 390, 440) exceed the configured Prettier max line width; run "yarn lint-fix" to auto-format or manually break these long expressions by extracting the computed string into a short variable (e.g., const displayName = model.platform ? `${model.platform}/${model.label}` : model.label) and then use "name: displayName" (or split the ternary/template across lines) so each line conforms to the Prettier width and resolves the formatting warnings.packages/extension-usage/client/home.vue (1)
1095-1098: 💤 Low value合并重复的 CSS 选择器。
静态分析工具检测到
.chatluna-usage-table选择器在第 1095 行和第 1132 行重复定义。建议将这两处合并为一个规则块以提高可维护性。♻️ 建议合并
.chatluna-usage-table { margin-top: 1rem; + overflow: visible; } -.chatluna-usage-table { - overflow: visible; -}Also applies to: 1132-1134
🤖 Prompt for 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. In `@packages/extension-usage/client/home.vue` around lines 1095 - 1098, The .chatluna-usage-table CSS selector is defined twice (around the blocks shown) causing duplication; remove the duplicate by consolidating both rule sets into a single declaration for .chatluna-usage-table (keep any unique properties from both occurrences), delete the redundant block, and ensure the final rule contains the combined margin/top/other properties so styles remain identical; update the single .chatluna-usage-table rule wherever used.
🤖 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 `@packages/core/src/llm-core/platform/rerank.ts`:
- Around line 183-189: The _report call currently uses "usageMetadata: usage ??
{ input_tokens: inputTokens, output_tokens: 0, total_tokens: inputTokens }"
which lets a provided usage object that lacks token fields pass through
incomplete; change it to merge computed token estimates into the final payload
so missing fields are filled. Specifically, when calling this._report, build
usageMetadata from usage but set input_tokens: usage?.input_tokens ??
inputTokens, output_tokens: usage?.output_tokens ?? 0, and total_tokens:
usage?.total_tokens ?? ((usage?.input_tokens ?? inputTokens) +
(usage?.output_tokens ?? 0)); keep other usage fields intact — update the
usageMetadata construction around the existing this._report invocation
(referencing usage and inputTokens) to perform this merge.
---
Nitpick comments:
In `@packages/extension-usage/client/charts/token-line.ts`:
- Line 253: Multiple lines (e.g., the object property assignment "name:
model.platform ? `${model.platform}/${model.label}` : model.label," and similar
expressions at lines 302, 313, 328, 390, 440) exceed the configured Prettier max
line width; run "yarn lint-fix" to auto-format or manually break these long
expressions by extracting the computed string into a short variable (e.g., const
displayName = model.platform ? `${model.platform}/${model.label}` : model.label)
and then use "name: displayName" (or split the ternary/template across lines) so
each line conforms to the Prettier width and resolves the formatting warnings.
In `@packages/extension-usage/client/home.vue`:
- Around line 1095-1098: The .chatluna-usage-table CSS selector is defined twice
(around the blocks shown) causing duplication; remove the duplicate by
consolidating both rule sets into a single declaration for .chatluna-usage-table
(keep any unique properties from both occurrences), delete the redundant block,
and ensure the final rule contains the combined margin/top/other properties so
styles remain identical; update the single .chatluna-usage-table rule wherever
used.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 022efd36-d7d8-493a-a111-27b614506243
⛔ Files ignored due to path filters (4)
packages/core/package.jsonis excluded by!**/*.jsonpackages/extension-usage/client/tsconfig.jsonis excluded by!**/*.jsonpackages/extension-usage/package.jsonis excluded by!**/*.jsonpackages/extension-usage/tsconfig.jsonis excluded by!**/*.json
📒 Files selected for processing (83)
packages/adapter-azure-openai/src/client.tspackages/adapter-azure-openai/src/requester.tspackages/adapter-claude/src/client.tspackages/adapter-deepseek/src/client.tspackages/adapter-deepseek/src/requester.tspackages/adapter-dify/src/client.tspackages/adapter-doubao/src/client.tspackages/adapter-doubao/src/requester.tspackages/adapter-gemini/src/client.tspackages/adapter-gemini/src/requester.tspackages/adapter-hunyuan/src/client.tspackages/adapter-hunyuan/src/requester.tspackages/adapter-ollama/src/client.tspackages/adapter-ollama/src/requester.tspackages/adapter-openai-like/src/client.tspackages/adapter-openai-like/src/requester.tspackages/adapter-openai/src/client.tspackages/adapter-openai/src/requester.tspackages/adapter-qwen/src/client.tspackages/adapter-qwen/src/requester.tspackages/adapter-rwkv/src/client.tspackages/adapter-rwkv/src/requester.tspackages/adapter-spark/src/client.tspackages/adapter-wenxin/src/client.tspackages/adapter-wenxin/src/requester.tspackages/adapter-zhipu/src/client.tspackages/adapter-zhipu/src/requester.tspackages/core/src/chains/chain.tspackages/core/src/index.tspackages/core/src/llm-core/agent/agent.tspackages/core/src/llm-core/chain/agent_chat_chain.tspackages/core/src/llm-core/chain/chat_chain.tspackages/core/src/llm-core/chat/app.tspackages/core/src/llm-core/chat/infinite_context.tspackages/core/src/llm-core/memory/message/database_history.tspackages/core/src/llm-core/platform/api.tspackages/core/src/llm-core/platform/client.tspackages/core/src/llm-core/platform/model.tspackages/core/src/llm-core/platform/rerank.tspackages/core/src/llm-core/platform/service.tspackages/core/src/llm-core/platform/usage.tspackages/core/src/middlewares/chat/message_delay.tspackages/core/src/middlewares/chat/rollback_chat.tspackages/core/src/middlewares/conversation/request_conversation.tspackages/core/src/middlewares/conversation/resolve_conversation.tspackages/core/src/middlewares/preset/delete_preset.tspackages/core/src/middlewares/system/conversation_manage.tspackages/core/src/migration/room_to_conversation.tspackages/core/src/migration/validators.tspackages/core/src/services/chat.tspackages/core/src/services/conversation.tspackages/core/src/services/conversation_runtime.tspackages/core/src/services/conversation_types.tspackages/core/src/services/types.tspackages/core/src/types.tspackages/core/src/utils/archive.tspackages/core/src/utils/conversation.tspackages/core/src/utils/string.tspackages/core/src/utils/usage_source.tspackages/core/tests/conversation-archive.spec.tspackages/core/tests/conversation-migration.spec.tspackages/core/tests/conversation-resolve.spec.tspackages/core/tests/conversation-service.spec.tspackages/core/tests/conversation-utils.spec.tspackages/core/tests/helpers.tspackages/extension-usage/client/charts/echarts.tspackages/extension-usage/client/charts/index.scsspackages/extension-usage/client/charts/index.tspackages/extension-usage/client/charts/model-pie.vuepackages/extension-usage/client/charts/model-success.vuepackages/extension-usage/client/charts/token-line.tspackages/extension-usage/client/charts/utils.tspackages/extension-usage/client/home.vuepackages/extension-usage/client/index.tspackages/extension-usage/client/numbers/index.vuepackages/extension-usage/client/sources/index.vuepackages/extension-usage/client/state.tspackages/extension-usage/client/theme.tspackages/extension-usage/src/index.tspackages/service-embeddings/src/embeddings/huggingface.tspackages/shared-adapter/src/requester.tspackages/shared-adapter/src/types.tspackages/shared-adapter/src/utils.ts
💤 Files with no reviewable changes (2)
- packages/core/src/services/conversation_types.ts
- packages/shared-adapter/src/types.ts
✅ Files skipped from review due to trivial changes (20)
- packages/core/src/llm-core/chat/app.ts
- packages/core/src/middlewares/chat/rollback_chat.ts
- packages/core/src/llm-core/platform/service.ts
- packages/core/src/chains/chain.ts
- packages/core/src/llm-core/chat/infinite_context.ts
- packages/core/src/middlewares/preset/delete_preset.ts
- packages/core/src/index.ts
- packages/core/tests/helpers.ts
- packages/core/src/middlewares/chat/message_delay.ts
- packages/core/src/migration/validators.ts
- packages/core/src/migration/room_to_conversation.ts
- packages/core/src/middlewares/system/conversation_manage.ts
- packages/core/src/middlewares/conversation/resolve_conversation.ts
- packages/core/src/utils/conversation.ts
- packages/core/src/llm-core/memory/message/database_history.ts
- packages/core/src/utils/archive.ts
- packages/core/tests/conversation-migration.spec.ts
- packages/core/src/services/conversation_runtime.ts
- packages/core/tests/conversation-archive.spec.ts
- packages/core/src/middlewares/conversation/request_conversation.ts
This pr adds ChatLuna model usage tracking, persistence, and a console dashboard for browsing usage metrics.
New Features
chatluna/model-usageevent with shared usage payload types in core platform usage utilities.UsageMetadata, including cache-read/cache-creation and reasoning token details when adapters provide them.chatluna-usageextension in thechatluna_usagetable.Bug fixes
Other Changes
chatluna-usageaggregation with grouping, model/source summaries, timelines, list sorting, and filtering.yarn lint-fix.