-
Notifications
You must be signed in to change notification settings - Fork 26
增加渠道用量统计 #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
增加渠道用量统计 #30
Conversation
|
@Zic-Wang is attempting to deploy a commit to the sxjeru's projects Team on Vercel. A member of the Team first needs to authorize it. |
Reviewer's Guide通过使用基于 auth/API-key 的 channel 名称丰富 usage 记录,并添加数据库列和迁移,实现按 channel 的用量追踪与可视化;同时暴露新的 将 usage 记录与 channel 解析同步的时序图sequenceDiagram
actor Admin
participant Browser
participant NextServer
participant SyncRoute as performSync
participant CLIProxy as CLIProxyAPI
participant DB
Admin->>Browser: Trigger sync request
Browser->>NextServer: POST /api/sync
NextServer->>SyncRoute: performSync(request)
SyncRoute->>SyncRoute: isAuthorized
SyncRoute-->>NextServer: unauthorized (if invalid)
NextServer-->>Browser: 401 (if invalid)
rect rgb(40,40,60)
SyncRoute->>CLIProxy: fetchAuthIndexMapping
CLIProxy-->>SyncRoute: auth_index to channel_name mapping
SyncRoute->>CLIProxy: fetchApiKeyChannelMapping
CLIProxy-->>SyncRoute: api_key to channel_name mapping
end
SyncRoute->>SyncRoute: toUsageRecords(payload, pulledAt, authMap, apiKeyMap)
SyncRoute->>DB: INSERT usage_records (including authIndex, channel)
DB-->>SyncRoute: insert result
SyncRoute-->>NextServer: JSON { status: ok, inserted: n }
NextServer-->>Browser: 200 OK
在 Channels 页面加载 channel 统计数据的时序图sequenceDiagram
actor User
participant Browser as ChannelsPage
participant NextServer
participant ChannelsAPI as GET_api_channels
participant DB
User->>Browser: Navigate to /channels
Browser->>ChannelsAPI: GET /api/channels?days=n
ChannelsAPI->>DB: SELECT aggregated usage_records by channel
DB-->>ChannelsAPI: ChannelAggRow list
ChannelsAPI->>DB: SELECT aggregated usage_records by channel, model
DB-->>ChannelsAPI: ChannelModelAggRow list
ChannelsAPI->>DB: SELECT * FROM model_prices
DB-->>ChannelsAPI: PriceRow list
ChannelsAPI->>ChannelsAPI: estimateCost per channel
ChannelsAPI-->>Browser: JSON { channels, days }
Browser->>Browser: groupChannels, aggregateStats
Browser-->>User: Render grouped channel statistics and costs
usageRecords 与 channel 追踪的实体关系图erDiagram
usageRecords {
uuid id PK
timestamptz occurredAt
timestamptz syncedAt
text route
text model
text authIndex
text channel
integer totalTokens
integer inputTokens
integer outputTokens
integer reasoningTokens
integer cachedTokens
boolean isError
}
modelPrices {
serial id PK
text model
numeric inputPricePer1M
numeric cachedInputPricePer1M
numeric outputPricePer1M
}
usageRecords }o--o{ modelPrices : model_price_for_model
usage 解析与 channel 聚合的类图classDiagram
class UsageTokens {
+number inputTokens
+number cachedTokens
+number outputTokens
+number reasoningTokens
+number totalTokens
}
class UsageDetail {
+string timestamp
+string source
+string auth_index
+UsageTokens tokens
+boolean failed
+number cached
}
class ApiParsed {
+Record~string,UsageDetail[]~ details
+string api
}
class UsageResponse {
+Record~string,ApiParsed~ apis
}
class UsageRecordInsert {
+Date occurredAt
+Date syncedAt
+string route
+string model
+string authIndex
+string channel
+number totalTokens
+number inputTokens
+number outputTokens
+number reasoningTokens
+number cachedTokens
+boolean isError
}
class toUsageRecords {
+UsageRecordInsert[] toUsageRecords(UsageResponse payload, Date pulledAt, Map~string,string~ authMap, Map~string,string~ apiKeyMap)
}
class ChannelStat {
+string channel
+number requests
+number totalTokens
+number inputTokens
+number outputTokens
+number reasoningTokens
+number cachedTokens
+number errorCount
+number cost
}
class ChannelGroup {
+string name
+string type
+ChannelStat[] channels
+number requests
+number totalTokens
+number inputTokens
+number outputTokens
+number reasoningTokens
+number cachedTokens
+number errorCount
+number cost
}
class ChannelsPage {
+ChannelsPage()
}
class groupChannelsFn {
+ChannelGroup[] groupChannels(ChannelStat[] channels)
}
class aggregateStatsFn {
+ChannelStat aggregateStats(ChannelStat[] channels)
}
class fmtRateFn {
+string fmtRate(number requests, number errorCount)
}
class rateColorFn {
+string rateColor(number requests, number errorCount)
}
UsageResponse --> ApiParsed : contains
ApiParsed --> UsageDetail : contains
UsageDetail --> UsageTokens : contains
toUsageRecords --> UsageResponse : parses
toUsageRecords --> UsageRecordInsert : produces
ChannelGroup --> ChannelStat : aggregates
ChannelsPage --> ChannelStat : displays
ChannelsPage --> ChannelGroup : displays
groupChannelsFn --> ChannelStat : input
groupChannelsFn --> ChannelGroup : output
aggregateStatsFn --> ChannelStat : input_output
fmtRateFn --> ChannelStat : uses
rateColorFn --> ChannelStat : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your Experience访问你的 dashboard 可以:
Getting HelpOriginal review guide in EnglishReviewer's GuideImplements per-channel usage tracking and visualization by enriching usage records with auth/api-key based channel names, adding database columns and migrations, exposing a new /api/channels aggregation endpoint, and a new Channels UI page plus sidebar entry for inspecting channel statistics and costs. Sequence diagram for syncing usage records with channel resolutionsequenceDiagram
actor Admin
participant Browser
participant NextServer
participant SyncRoute as performSync
participant CLIProxy as CLIProxyAPI
participant DB
Admin->>Browser: Trigger sync request
Browser->>NextServer: POST /api/sync
NextServer->>SyncRoute: performSync(request)
SyncRoute->>SyncRoute: isAuthorized
SyncRoute-->>NextServer: unauthorized (if invalid)
NextServer-->>Browser: 401 (if invalid)
rect rgb(40,40,60)
SyncRoute->>CLIProxy: fetchAuthIndexMapping
CLIProxy-->>SyncRoute: auth_index to channel_name mapping
SyncRoute->>CLIProxy: fetchApiKeyChannelMapping
CLIProxy-->>SyncRoute: api_key to channel_name mapping
end
SyncRoute->>SyncRoute: toUsageRecords(payload, pulledAt, authMap, apiKeyMap)
SyncRoute->>DB: INSERT usage_records (including authIndex, channel)
DB-->>SyncRoute: insert result
SyncRoute-->>NextServer: JSON { status: ok, inserted: n }
NextServer-->>Browser: 200 OK
Sequence diagram for loading channel statistics in Channels pagesequenceDiagram
actor User
participant Browser as ChannelsPage
participant NextServer
participant ChannelsAPI as GET_api_channels
participant DB
User->>Browser: Navigate to /channels
Browser->>ChannelsAPI: GET /api/channels?days=n
ChannelsAPI->>DB: SELECT aggregated usage_records by channel
DB-->>ChannelsAPI: ChannelAggRow list
ChannelsAPI->>DB: SELECT aggregated usage_records by channel, model
DB-->>ChannelsAPI: ChannelModelAggRow list
ChannelsAPI->>DB: SELECT * FROM model_prices
DB-->>ChannelsAPI: PriceRow list
ChannelsAPI->>ChannelsAPI: estimateCost per channel
ChannelsAPI-->>Browser: JSON { channels, days }
Browser->>Browser: groupChannels, aggregateStats
Browser-->>User: Render grouped channel statistics and costs
Entity relationship diagram for usageRecords and channel trackingerDiagram
usageRecords {
uuid id PK
timestamptz occurredAt
timestamptz syncedAt
text route
text model
text authIndex
text channel
integer totalTokens
integer inputTokens
integer outputTokens
integer reasoningTokens
integer cachedTokens
boolean isError
}
modelPrices {
serial id PK
text model
numeric inputPricePer1M
numeric cachedInputPricePer1M
numeric outputPricePer1M
}
usageRecords }o--o{ modelPrices : model_price_for_model
Class diagram for usage parsing and channel aggregationclassDiagram
class UsageTokens {
+number inputTokens
+number cachedTokens
+number outputTokens
+number reasoningTokens
+number totalTokens
}
class UsageDetail {
+string timestamp
+string source
+string auth_index
+UsageTokens tokens
+boolean failed
+number cached
}
class ApiParsed {
+Record~string,UsageDetail[]~ details
+string api
}
class UsageResponse {
+Record~string,ApiParsed~ apis
}
class UsageRecordInsert {
+Date occurredAt
+Date syncedAt
+string route
+string model
+string authIndex
+string channel
+number totalTokens
+number inputTokens
+number outputTokens
+number reasoningTokens
+number cachedTokens
+boolean isError
}
class toUsageRecords {
+UsageRecordInsert[] toUsageRecords(UsageResponse payload, Date pulledAt, Map~string,string~ authMap, Map~string,string~ apiKeyMap)
}
class ChannelStat {
+string channel
+number requests
+number totalTokens
+number inputTokens
+number outputTokens
+number reasoningTokens
+number cachedTokens
+number errorCount
+number cost
}
class ChannelGroup {
+string name
+string type
+ChannelStat[] channels
+number requests
+number totalTokens
+number inputTokens
+number outputTokens
+number reasoningTokens
+number cachedTokens
+number errorCount
+number cost
}
class ChannelsPage {
+ChannelsPage()
}
class groupChannelsFn {
+ChannelGroup[] groupChannels(ChannelStat[] channels)
}
class aggregateStatsFn {
+ChannelStat aggregateStats(ChannelStat[] channels)
}
class fmtRateFn {
+string fmtRate(number requests, number errorCount)
}
class rateColorFn {
+string rateColor(number requests, number errorCount)
}
UsageResponse --> ApiParsed : contains
ApiParsed --> UsageDetail : contains
UsageDetail --> UsageTokens : contains
toUsageRecords --> UsageResponse : parses
toUsageRecords --> UsageRecordInsert : produces
ChannelGroup --> ChannelStat : aggregates
ChannelsPage --> ChannelStat : displays
ChannelsPage --> ChannelGroup : displays
groupChannelsFn --> ChannelStat : input
groupChannelsFn --> ChannelGroup : output
aggregateStatsFn --> ChannelStat : input_output
fmtRateFn --> ChannelStat : uses
rateColorFn --> ChannelStat : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - 我在这里给出了一些整体性的反馈:
scripts/migrate.mjs里的自定义 .env 加载器实现得非常朴素(不处理引号、行内注释,或包含=的值),可能会错误解析合法的环境变量文件;建议使用dotenv,或者复用 Next 的环境变量加载逻辑,而不是手写一个解析器。- channels API 在大致相同的时间范围内,对
usage_records运行了两次单独的聚合查询;你可以通过只做一次按(channel, model)分组的聚合来减少负载,然后从该结果中推导每个 channel 的汇总,而不是扫描两次表。
给 AI Agent 的提示
Please address the comments from this code review:
## Overall Comments
- The custom .env loader in `scripts/migrate.mjs` is very naive (no quote handling, inline comments, or values containing `=`) and may mis-parse valid environment files; consider using `dotenv` or reusing Next’s env loading logic instead of hand-rolling a parser.
- The channels API runs two separate aggregation queries over `usage_records` for roughly the same time range; you could reduce load by doing a single aggregation grouped by `(channel, model)` and deriving per-channel totals from that result rather than scanning the table twice.帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English
Hey - I've left some high level feedback:
- The custom .env loader in
scripts/migrate.mjsis very naive (no quote handling, inline comments, or values containing=) and may mis-parse valid environment files; consider usingdotenvor reusing Next’s env loading logic instead of hand-rolling a parser. - The channels API runs two separate aggregation queries over
usage_recordsfor roughly the same time range; you could reduce load by doing a single aggregation grouped by(channel, model)and deriving per-channel totals from that result rather than scanning the table twice.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The custom .env loader in `scripts/migrate.mjs` is very naive (no quote handling, inline comments, or values containing `=`) and may mis-parse valid environment files; consider using `dotenv` or reusing Next’s env loading logic instead of hand-rolling a parser.
- The channels API runs two separate aggregation queries over `usage_records` for roughly the same time range; you could reduce load by doing a single aggregation grouped by `(channel, model)` and deriving per-channel totals from that result rather than scanning the table twice.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
按自己需求写的,纯Vibe,注意屎山代码
通过usage的auth_index去匹配auth_files的auth_index
如果无匹配,则使用usage的source去匹配ai-provider的api-key
若都无法匹配显示未知渠道,旧数据显示未知渠道
auth认证以该渠道名称显示,如antigravity,如果有多账号可展开查看账号用量
ai-provider渠道显示渠道名称,若无渠道名称则显示Base_url
Database增加:channel列
统计内容
请求次数 token 费用 成功率
输入 缓存 输出 思考 token数
Summary by Sourcery
添加按渠道的使用情况追踪,并通过新的渠道统计 API 和 UI 视图对外提供。
新功能:
/api/channels端点,用于按渠道和模型聚合使用记录,并为每个渠道计算 token 数量和预估成本。增强:
.env文件中加载环境变量,并自动标记已经执行过的初始迁移。构建:
usage_records上存储auth_index和channel列。Original summary in English
Summary by Sourcery
Add per-channel usage tracking and expose it via a new channels statistics API and UI view.
New Features:
Enhancements:
Build: