mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat: add i18n for stats command
This commit is contained in:
@@ -879,6 +879,36 @@ export default {
|
|||||||
'of input tokens were served from the cache, reducing costs.',
|
'of input tokens were served from the cache, reducing costs.',
|
||||||
'Tip: For a full token breakdown, run `/stats model`.':
|
'Tip: For a full token breakdown, run `/stats model`.':
|
||||||
'Tip: For a full token breakdown, run `/stats model`.',
|
'Tip: For a full token breakdown, run `/stats model`.',
|
||||||
|
'Model Stats For Nerds': 'Model Stats For Nerds',
|
||||||
|
'Tool Stats For Nerds': 'Tool Stats For Nerds',
|
||||||
|
Metric: 'Metric',
|
||||||
|
API: 'API',
|
||||||
|
Requests: 'Requests',
|
||||||
|
Errors: 'Errors',
|
||||||
|
'Avg Latency': 'Avg Latency',
|
||||||
|
Tokens: 'Tokens',
|
||||||
|
Total: 'Total',
|
||||||
|
Prompt: 'Prompt',
|
||||||
|
Cached: 'Cached',
|
||||||
|
Thoughts: 'Thoughts',
|
||||||
|
Tool: 'Tool',
|
||||||
|
Output: 'Output',
|
||||||
|
'No API calls have been made in this session.':
|
||||||
|
'No API calls have been made in this session.',
|
||||||
|
'Tool Name': 'Tool Name',
|
||||||
|
Calls: 'Calls',
|
||||||
|
'Success Rate': 'Success Rate',
|
||||||
|
'Avg Duration': 'Avg Duration',
|
||||||
|
'User Decision Summary': 'User Decision Summary',
|
||||||
|
'Total Reviewed Suggestions:': 'Total Reviewed Suggestions:',
|
||||||
|
' » Accepted:': ' » Accepted:',
|
||||||
|
' » Rejected:': ' » Rejected:',
|
||||||
|
' » Modified:': ' » Modified:',
|
||||||
|
' Overall Agreement Rate:': ' Overall Agreement Rate:',
|
||||||
|
'No tool calls have been made in this session.':
|
||||||
|
'No tool calls have been made in this session.',
|
||||||
|
'Session start time is unavailable, cannot calculate stats.':
|
||||||
|
'Session start time is unavailable, cannot calculate stats.',
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Loading Phrases
|
// Loading Phrases
|
||||||
|
|||||||
@@ -832,6 +832,36 @@ export default {
|
|||||||
'的输入令牌来自缓存,降低了成本',
|
'的输入令牌来自缓存,降低了成本',
|
||||||
'Tip: For a full token breakdown, run `/stats model`.':
|
'Tip: For a full token breakdown, run `/stats model`.':
|
||||||
'提示:要查看完整的令牌明细,请运行 `/stats model`',
|
'提示:要查看完整的令牌明细,请运行 `/stats model`',
|
||||||
|
'Model Stats For Nerds': '模型统计(技术细节)',
|
||||||
|
'Tool Stats For Nerds': '工具统计(技术细节)',
|
||||||
|
Metric: '指标',
|
||||||
|
API: 'API',
|
||||||
|
Requests: '请求数',
|
||||||
|
Errors: '错误数',
|
||||||
|
'Avg Latency': '平均延迟',
|
||||||
|
Tokens: '令牌',
|
||||||
|
Total: '总计',
|
||||||
|
Prompt: '提示',
|
||||||
|
Cached: '缓存',
|
||||||
|
Thoughts: '思考',
|
||||||
|
Tool: '工具',
|
||||||
|
Output: '输出',
|
||||||
|
'No API calls have been made in this session.':
|
||||||
|
'本次会话中未进行任何 API 调用',
|
||||||
|
'Tool Name': '工具名称',
|
||||||
|
Calls: '调用次数',
|
||||||
|
'Success Rate': '成功率',
|
||||||
|
'Avg Duration': '平均耗时',
|
||||||
|
'User Decision Summary': '用户决策摘要',
|
||||||
|
'Total Reviewed Suggestions:': '已审核建议总数:',
|
||||||
|
' » Accepted:': ' » 已接受:',
|
||||||
|
' » Rejected:': ' » 已拒绝:',
|
||||||
|
' » Modified:': ' » 已修改:',
|
||||||
|
' Overall Agreement Rate:': ' 总体同意率:',
|
||||||
|
'No tool calls have been made in this session.':
|
||||||
|
'本次会话中未进行任何工具调用',
|
||||||
|
'Session start time is unavailable, cannot calculate stats.':
|
||||||
|
'会话开始时间不可用,无法计算统计信息',
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Loading Phrases
|
// Loading Phrases
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const statsCommand: SlashCommand = {
|
|||||||
context.ui.addItem(
|
context.ui.addItem(
|
||||||
{
|
{
|
||||||
type: MessageType.ERROR,
|
type: MessageType.ERROR,
|
||||||
text: 'Session start time is unavailable, cannot calculate stats.',
|
text: t('Session start time is unavailable, cannot calculate stats.'),
|
||||||
},
|
},
|
||||||
Date.now(),
|
Date.now(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
} from '../utils/computeStats.js';
|
} from '../utils/computeStats.js';
|
||||||
import type { ModelMetrics } from '../contexts/SessionContext.js';
|
import type { ModelMetrics } from '../contexts/SessionContext.js';
|
||||||
import { useSessionStats } from '../contexts/SessionContext.js';
|
import { useSessionStats } from '../contexts/SessionContext.js';
|
||||||
|
import { t } from '../../i18n/index.js';
|
||||||
|
|
||||||
const METRIC_COL_WIDTH = 28;
|
const METRIC_COL_WIDTH = 28;
|
||||||
const MODEL_COL_WIDTH = 22;
|
const MODEL_COL_WIDTH = 22;
|
||||||
@@ -65,7 +66,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
paddingX={2}
|
paddingX={2}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={theme.text.primary}>
|
||||||
No API calls have been made in this session.
|
{t('No API calls have been made in this session.')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@@ -94,7 +95,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
paddingX={2}
|
paddingX={2}
|
||||||
>
|
>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={theme.text.accent}>
|
||||||
Model Stats For Nerds
|
{t('Model Stats For Nerds')}
|
||||||
</Text>
|
</Text>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
<Box>
|
<Box>
|
||||||
<Box width={METRIC_COL_WIDTH}>
|
<Box width={METRIC_COL_WIDTH}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
Metric
|
{t('Metric')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{modelNames.map((name) => (
|
{modelNames.map((name) => (
|
||||||
@@ -125,13 +126,13 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* API Section */}
|
{/* API Section */}
|
||||||
<StatRow title="API" values={[]} isSection />
|
<StatRow title={t('API')} values={[]} isSection />
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Requests"
|
title={t('Requests')}
|
||||||
values={getModelValues((m) => m.api.totalRequests.toLocaleString())}
|
values={getModelValues((m) => m.api.totalRequests.toLocaleString())}
|
||||||
/>
|
/>
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Errors"
|
title={t('Errors')}
|
||||||
values={getModelValues((m) => {
|
values={getModelValues((m) => {
|
||||||
const errorRate = calculateErrorRate(m);
|
const errorRate = calculateErrorRate(m);
|
||||||
return (
|
return (
|
||||||
@@ -146,7 +147,7 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Avg Latency"
|
title={t('Avg Latency')}
|
||||||
values={getModelValues((m) => {
|
values={getModelValues((m) => {
|
||||||
const avgLatency = calculateAverageLatency(m);
|
const avgLatency = calculateAverageLatency(m);
|
||||||
return formatDuration(avgLatency);
|
return formatDuration(avgLatency);
|
||||||
@@ -156,9 +157,9 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
{/* Tokens Section */}
|
{/* Tokens Section */}
|
||||||
<StatRow title="Tokens" values={[]} isSection />
|
<StatRow title={t('Tokens')} values={[]} isSection />
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Total"
|
title={t('Total')}
|
||||||
values={getModelValues((m) => (
|
values={getModelValues((m) => (
|
||||||
<Text color={theme.status.warning}>
|
<Text color={theme.status.warning}>
|
||||||
{m.tokens.total.toLocaleString()}
|
{m.tokens.total.toLocaleString()}
|
||||||
@@ -166,13 +167,13 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Prompt"
|
title={t('Prompt')}
|
||||||
isSubtle
|
isSubtle
|
||||||
values={getModelValues((m) => m.tokens.prompt.toLocaleString())}
|
values={getModelValues((m) => m.tokens.prompt.toLocaleString())}
|
||||||
/>
|
/>
|
||||||
{hasCached && (
|
{hasCached && (
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Cached"
|
title={t('Cached')}
|
||||||
isSubtle
|
isSubtle
|
||||||
values={getModelValues((m) => {
|
values={getModelValues((m) => {
|
||||||
const cacheHitRate = calculateCacheHitRate(m);
|
const cacheHitRate = calculateCacheHitRate(m);
|
||||||
@@ -186,20 +187,20 @@ export const ModelStatsDisplay: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{hasThoughts && (
|
{hasThoughts && (
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Thoughts"
|
title={t('Thoughts')}
|
||||||
isSubtle
|
isSubtle
|
||||||
values={getModelValues((m) => m.tokens.thoughts.toLocaleString())}
|
values={getModelValues((m) => m.tokens.thoughts.toLocaleString())}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{hasTool && (
|
{hasTool && (
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Tool"
|
title={t('Tool')}
|
||||||
isSubtle
|
isSubtle
|
||||||
values={getModelValues((m) => m.tokens.tool.toLocaleString())}
|
values={getModelValues((m) => m.tokens.tool.toLocaleString())}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<StatRow
|
<StatRow
|
||||||
title="Output"
|
title={t('Output')}
|
||||||
isSubtle
|
isSubtle
|
||||||
values={getModelValues((m) => m.tokens.candidates.toLocaleString())}
|
values={getModelValues((m) => m.tokens.candidates.toLocaleString())}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from '../utils/displayUtils.js';
|
} from '../utils/displayUtils.js';
|
||||||
import { useSessionStats } from '../contexts/SessionContext.js';
|
import { useSessionStats } from '../contexts/SessionContext.js';
|
||||||
import type { ToolCallStats } from '@qwen-code/qwen-code-core';
|
import type { ToolCallStats } from '@qwen-code/qwen-code-core';
|
||||||
|
import { t } from '../../i18n/index.js';
|
||||||
|
|
||||||
const TOOL_NAME_COL_WIDTH = 25;
|
const TOOL_NAME_COL_WIDTH = 25;
|
||||||
const CALLS_COL_WIDTH = 8;
|
const CALLS_COL_WIDTH = 8;
|
||||||
@@ -68,7 +69,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
paddingX={2}
|
paddingX={2}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}>
|
<Text color={theme.text.primary}>
|
||||||
No tool calls have been made in this session.
|
{t('No tool calls have been made in this session.')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@@ -103,7 +104,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
width={70}
|
width={70}
|
||||||
>
|
>
|
||||||
<Text bold color={theme.text.accent}>
|
<Text bold color={theme.text.accent}>
|
||||||
Tool Stats For Nerds
|
{t('Tool Stats For Nerds')}
|
||||||
</Text>
|
</Text>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
@@ -111,22 +112,22 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
<Box>
|
<Box>
|
||||||
<Box width={TOOL_NAME_COL_WIDTH}>
|
<Box width={TOOL_NAME_COL_WIDTH}>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
Tool Name
|
{t('Tool Name')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
<Box width={CALLS_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
Calls
|
{t('Calls')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
<Box width={SUCCESS_RATE_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
Success Rate
|
{t('Success Rate')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
Avg Duration
|
{t('Avg Duration')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -151,13 +152,15 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
|
|
||||||
{/* User Decision Summary */}
|
{/* User Decision Summary */}
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
User Decision Summary
|
{t('User Decision Summary')}
|
||||||
</Text>
|
</Text>
|
||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.link}>Total Reviewed Suggestions:</Text>
|
<Text color={theme.text.link}>
|
||||||
|
{t('Total Reviewed Suggestions:')}
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.text.primary}>{totalReviewed}</Text>
|
<Text color={theme.text.primary}>{totalReviewed}</Text>
|
||||||
@@ -167,7 +170,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> » Accepted:</Text>
|
<Text color={theme.text.primary}>{t(' » Accepted:')}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.status.success}>{totalDecisions.accept}</Text>
|
<Text color={theme.status.success}>{totalDecisions.accept}</Text>
|
||||||
@@ -177,7 +180,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> » Rejected:</Text>
|
<Text color={theme.text.primary}>{t(' » Rejected:')}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.status.error}>{totalDecisions.reject}</Text>
|
<Text color={theme.status.error}>{totalDecisions.reject}</Text>
|
||||||
@@ -187,7 +190,7 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> » Modified:</Text>
|
<Text color={theme.text.primary}>{t(' » Modified:')}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text color={theme.status.warning}>{totalDecisions.modify}</Text>
|
<Text color={theme.status.warning}>{totalDecisions.modify}</Text>
|
||||||
@@ -209,7 +212,9 @@ export const ToolStatsDisplay: React.FC = () => {
|
|||||||
<Box
|
<Box
|
||||||
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
width={TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH}
|
||||||
>
|
>
|
||||||
<Text color={theme.text.primary}> Overall Agreement Rate:</Text>
|
<Text color={theme.text.primary}>
|
||||||
|
{t(' Overall Agreement Rate:')}
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
<Box width={AVG_DURATION_COL_WIDTH} justifyContent="flex-end">
|
||||||
<Text bold color={totalReviewed > 0 ? agreementColor : undefined}>
|
<Text bold color={totalReviewed > 0 ? agreementColor : undefined}>
|
||||||
|
|||||||
Reference in New Issue
Block a user