feat(i18n): AIOps 智能运维模块全量国际化迁移 — 12 文件 + 84 翻译键
迁移文件:
- AgentCLI.tsx: 主机格式/安全模式示例/确认弹框/历史记录/状态徽章等 12 处
- LLMConfig.tsx: 云服务/自定义分组/推理模式/测试连接/配置帮助等 15 处
- AIOpsAssistant.tsx: 能力展示/自动报告生成
- AIAlertAnalyzer.tsx: AI引擎状态/活跃计数/严重等级标签
- AIAnalysisResult.tsx: 分析结果标题/根因/影响/建议/相似案例/修复脚本
- AutoFixDialog.tsx: 脚本确认弹框/警告/取消/执行按钮
- PredictiveCapacityTab.tsx: 容量预警/AI解读/7天预测/当前/预测/阈值标签
- AIPredictiveAnalytics.tsx: 关键指标/线性回归/趋势/预测值/概率
- AgentCLIMessages.tsx: 轮次/工具数/思考/耗时标签
- AgentCLIHistory.tsx: 删除确认/新建/轮次摘要
- analyzerHelpers.ts: 持续时间格式化 (小时/分钟)
- predictUtils.ts: 时间范围/异常预测/容量摘要
翻译键总数: 6088 → 6172 (zh-CN/en-US 完全对齐)
CJK 残留: 1672 → 1114 字符 (33% 降幅)
tsc -b=0, npm build=✅, 17/17 集成测试通过
This commit is contained in:
1 parent
eb98a95842
commit
76393c663c
15 files changed
+254
-91
No files matched your search
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
# AIOps 智能运维平台 — 优化路线图
|
# AIOps 智能运维平台 — 优化路线图
|
||||||
|
|
||||||
> 基于全量审计(96,102 行代码 / 374 文件),从「K8s 功能开发」全面转向「工程质量 + 功能补全 + 架构治理」。
|
> 基于全量审计(96,102 行代码 / 374 文件),从「K8s 功能开发」全面转向「工程质量 + 功能补全 + 架构治理」。
|
||||||
> 最后更新: 2026-06-25
|
> 最后更新: 2026-07-03
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -273,6 +273,7 @@ services/api.ts (1324L)
|
|||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
| Dashboard/ServerManager/AlertCenter/Approval/安全审计/Deploy/Kubernetes/Cloud/Task/Monitor | 3800+ | 已迁移 |
|
| Dashboard/ServerManager/AlertCenter/Approval/安全审计/Deploy/Kubernetes/Cloud/Task/Monitor | 3800+ | 已迁移 |
|
||||||
| **监控大屏 (GrafanaDashboard)** | 108 (monitor.dashboard.*) | GrafanaDashboard.tsx (AgentTab/ZabbixTab/PrometheusTab/DashboardTab) |
|
| **监控大屏 (GrafanaDashboard)** | 108 (monitor.dashboard.*) | GrafanaDashboard.tsx (AgentTab/ZabbixTab/PrometheusTab/DashboardTab) |
|
||||||
|
| **AIOps 智能运维模块** | 83 (aiops.*) | AgentCLI/LLMConfig/AIOpsAssistant/AIAlertAnalyzer/AIAnalysisResult/AutoFixDialog/PredictiveCapacityTab/AIPredictiveAnalytics/AgentCLIMessages/AgentCLIHistory/predictUtils/analyzerHelpers (12 文件) |
|
||||||
|
|
||||||
**已完成 (2026-06-27)**:
|
**已完成 (2026-06-27)**:
|
||||||
- ✅ **i18n 基础设施搭建** — 安装 `i18next` + `react-i18next`,配置翻译实例 (`src/i18n/index.ts`),支持 `zh-CN` / `en-US` 双语
|
- ✅ **i18n 基础设施搭建** — 安装 `i18next` + `react-i18next`,配置翻译实例 (`src/i18n/index.ts`),支持 `zh-CN` / `en-US` 双语
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export function AIAlertAnalyzer() {
|
|||||||
)}
|
)}
|
||||||
actions={(
|
actions={(
|
||||||
<Badge variant="outline" className="border-emerald-500/30 dg-text-green bg-emerald-500/10">
|
<Badge variant="outline" className="border-emerald-500/30 dg-text-green bg-emerald-500/10">
|
||||||
<Sparkles className="h-3 w-3 mr-1" />AI 引擎运行中
|
<Sparkles className="h-3 w-3 mr-1" />{t('aiops.aiEngineRunning')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
contentClassName="grid grid-cols-2 gap-6 h-[calc(100vh-200px)]"
|
contentClassName="grid grid-cols-2 gap-6 h-[calc(100vh-200px)]"
|
||||||
@@ -170,7 +170,7 @@ export function AIAlertAnalyzer() {
|
|||||||
{t('dashboard.ops.firingAlerts')}
|
{t('dashboard.ops.firingAlerts')}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Badge className="bg-red-500/10 dg-text-red border-red-500/30">
|
<Badge className="bg-red-500/10 dg-text-red border-red-500/30">
|
||||||
{alerts.filter(a => a.status === 'active').length} 个活跃
|
{t('aiops.activeCount', { count: alerts.filter(a => a.status === 'active').length })}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@@ -203,7 +203,7 @@ export function AIAlertAnalyzer() {
|
|||||||
<span className="font-medium text-[var(--dg-text-primary)]">{alert.name}</span>
|
<span className="font-medium text-[var(--dg-text-primary)]">{alert.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="outline" className={getSeverityColor(alert.severity)}>
|
<Badge variant="outline" className={getSeverityColor(alert.severity)}>
|
||||||
{alert.severity === 'critical' ? '严重' : alert.severity === 'warning' ? '警告' : '提示'}
|
{alert.severity === 'critical' ? t('aiops.severityCritical') : alert.severity === 'warning' ? t('aiops.severityWarning') : t('aiops.severityInfo')}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm dg-text-secondary mb-2">{alert.description}</div>
|
<div className="text-sm dg-text-secondary mb-2">{alert.description}</div>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
<CardHeader className="border-b border-[var(--dg-border)] pb-3">
|
<CardHeader className="border-b border-[var(--dg-border)] pb-3">
|
||||||
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||||
<Sparkles className="h-5 w-5 dg-text-purple" />
|
<Sparkles className="h-5 w-5 dg-text-purple" />
|
||||||
AI 分析结果
|
{i18n.t('aiops.aiAnalysisResult')}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<ScrollArea className="flex-1">
|
<ScrollArea className="flex-1">
|
||||||
@@ -33,7 +33,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
) : isAnalyzing ? (
|
) : isAnalyzing ? (
|
||||||
<div className="flex flex-col items-center justify-center h-64">
|
<div className="flex flex-col items-center justify-center h-64">
|
||||||
<Loader2 className="h-8 w-8 animate-spin dg-text-purple mb-4" />
|
<Loader2 className="h-8 w-8 animate-spin dg-text-purple mb-4" />
|
||||||
<p className="dg-text-secondary">AI正在分析告警根因...</p>
|
<p className="dg-text-secondary">{i18n.t('aiops.analyzingRootCause')}</p>
|
||||||
<p className="text-xs dg-text-muted mt-2"> {i18n.t('aiops.aianalysisresul.sf7d04a0f')} </p>
|
<p className="text-xs dg-text-muted mt-2"> {i18n.t('aiops.aianalysisresul.sf7d04a0f')} </p>
|
||||||
</div>
|
</div>
|
||||||
) : analysis ? (
|
) : analysis ? (
|
||||||
@@ -59,7 +59,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
||||||
<AlertCircle className="h-4 w-4 dg-text-red" />
|
<AlertCircle className="h-4 w-4 dg-text-red" />
|
||||||
根因分析
|
{i18n.t('aiops.rootCauseAnalysis')}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="p-3 bg-[var(--dg-bg-secondary)] rounded-lg border border-[var(--dg-border)]">
|
<div className="p-3 bg-[var(--dg-bg-secondary)] rounded-lg border border-[var(--dg-border)]">
|
||||||
<p className="text-sm text-[var(--dg-text-secondary)] leading-relaxed">{analysis.rootCause}</p>
|
<p className="text-sm text-[var(--dg-text-secondary)] leading-relaxed">{analysis.rootCause}</p>
|
||||||
@@ -70,7 +70,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
||||||
<TrendingUp className="h-4 w-4 dg-text-yellow" />
|
<TrendingUp className="h-4 w-4 dg-text-yellow" />
|
||||||
影响范围
|
{i18n.t('aiops.impactScopeLabel')}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="p-3 bg-[var(--dg-bg-secondary)] rounded-lg border border-[var(--dg-border)]">
|
<div className="p-3 bg-[var(--dg-bg-secondary)] rounded-lg border border-[var(--dg-border)]">
|
||||||
<p className="text-sm text-[var(--dg-text-secondary)] leading-relaxed">{analysis.impact}</p>
|
<p className="text-sm text-[var(--dg-text-secondary)] leading-relaxed">{analysis.impact}</p>
|
||||||
@@ -81,7 +81,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
||||||
<CheckCircle className="h-4 w-4 dg-text-green" />
|
<CheckCircle className="h-4 w-4 dg-text-green" />
|
||||||
修复建议
|
{i18n.t('aiops.fixSuggestions')}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{analysis.recommendations.map((rec, idx) => (
|
{analysis.recommendations.map((rec, idx) => (
|
||||||
@@ -102,7 +102,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
<div className="flex items-center gap-2 p-3 bg-[var(--dg-bg-secondary)] rounded-lg border border-[var(--dg-border)]">
|
<div className="flex items-center gap-2 p-3 bg-[var(--dg-bg-secondary)] rounded-lg border border-[var(--dg-border)]">
|
||||||
<FileText className="h-4 w-4 dg-text-blue" />
|
<FileText className="h-4 w-4 dg-text-blue" />
|
||||||
<span className="text-sm dg-text-secondary">
|
<span className="text-sm dg-text-secondary">
|
||||||
发现 <span className="dg-text-blue font-medium">{analysis.similarCases}</span> 个相似历史案例
|
{i18n.t('aiops.similarCasesFound', { count: analysis.similarCases })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
<h4 className="text-sm font-medium text-[var(--dg-text-secondary)] flex items-center gap-2">
|
||||||
<Terminal className="h-4 w-4 dg-text-purple" />
|
<Terminal className="h-4 w-4 dg-text-purple" />
|
||||||
自动修复脚本
|
{i18n.t('aiops.autoFixScriptTitle')}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="p-3 bg-[var(--dg-bg-primary)] rounded-lg border border-[var(--dg-border)]">
|
<div className="p-3 bg-[var(--dg-bg-primary)] rounded-lg border border-[var(--dg-border)]">
|
||||||
<pre className="text-xs dg-text-secondary overflow-x-auto">{analysis.autoFixScript}</pre>
|
<pre className="text-xs dg-text-secondary overflow-x-auto">{analysis.autoFixScript}</pre>
|
||||||
@@ -121,7 +121,7 @@ export function AIAnalysisResult({ selectedAlert, isAnalyzing, analysis, onAutoF
|
|||||||
onClick={onAutoFix}
|
onClick={onAutoFix}
|
||||||
>
|
>
|
||||||
<Play className="h-4 w-4 mr-2" />
|
<Play className="h-4 w-4 mr-2" />
|
||||||
执行自动修复
|
{i18n.t('aiops.executeAutoFix')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ export function AIOpsAssistant() {
|
|||||||
|
|
||||||
<Card className="dg-card">
|
<Card className="dg-card">
|
||||||
<CardHeader className="pb-3">
|
<CardHeader className="pb-3">
|
||||||
<CardTitle className="text-sm text-[var(--dg-text-secondary)]">能力展示</CardTitle>
|
<CardTitle className="text-sm text-[var(--dg-text-secondary)]">{t('aiops.capabilities')}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
<div className="flex items-center gap-2 text-sm dg-text-secondary">
|
<div className="flex items-center gap-2 text-sm dg-text-secondary">
|
||||||
@@ -355,7 +355,7 @@ export function AIOpsAssistant() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm dg-text-secondary">
|
<div className="flex items-center gap-2 text-sm dg-text-secondary">
|
||||||
<FileText className="h-4 w-4 dg-text-green" />
|
<FileText className="h-4 w-4 dg-text-green" />
|
||||||
<span>自动报告生成</span>
|
<span>{t('aiops.autoReportGen')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm dg-text-secondary">
|
<div className="flex items-center gap-2 text-sm dg-text-secondary">
|
||||||
<Terminal className="h-4 w-4 dg-text-purple" />
|
<Terminal className="h-4 w-4 dg-text-purple" />
|
||||||
|
|||||||
@@ -159,18 +159,18 @@ export function AIPredictiveAnalytics() {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||||
<TrendingUp className="h-5 w-5 dg-text-blue" />
|
<TrendingUp className="h-5 w-5 dg-text-blue" />
|
||||||
关键指标预测
|
{t('aiops.keyMetricForecast')}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Badge variant="outline" className="border-purple-500/30 dg-text-purple bg-purple-500/10">
|
<Badge variant="outline" className="border-purple-500/30 dg-text-purple bg-purple-500/10">
|
||||||
<Sparkles className="h-3 w-3 mr-1" />
|
<Sparkles className="h-3 w-3 mr-1" />
|
||||||
线性回归预测
|
{t('aiops.linearRegressionForecast')}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12 dg-text-muted">
|
<div className="flex items-center justify-center py-12 dg-text-muted">
|
||||||
<Loader2 className="h-5 w-5 animate-spin mr-2" /> 加载指标数据...
|
<Loader2 className="h-5 w-5 animate-spin mr-2" /> {t('aiops.loadingMetricData')}
|
||||||
</div>
|
</div>
|
||||||
) : predictions.length === 0 ? (
|
) : predictions.length === 0 ? (
|
||||||
<div className="text-center py-12 dg-text-muted">{t('aiops.noMetricData')}</div>
|
<div className="text-center py-12 dg-text-muted">{t('aiops.noMetricData')}</div>
|
||||||
@@ -181,15 +181,15 @@ export function AIPredictiveAnalytics() {
|
|||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{prediction.name.includes('CPU') && <Cpu className="h-4 w-4 dg-text-blue" />}
|
{prediction.name.includes('CPU') && <Cpu className="h-4 w-4 dg-text-blue" />}
|
||||||
{prediction.name.includes('内存') && <MemoryStick className="h-4 w-4 dg-text-green" />}
|
{prediction.name.includes(t('aiops.memoryKeyword')) && <MemoryStick className="h-4 w-4 dg-text-green" />}
|
||||||
{prediction.name.includes('磁盘') && <HardDrive className="h-4 w-4 dg-text-yellow" />}
|
{prediction.name.includes(t('aiops.diskKeyword')) && <HardDrive className="h-4 w-4 dg-text-yellow" />}
|
||||||
{prediction.name.includes('网络') && <Network className="h-4 w-4 dg-text-purple" />}
|
{prediction.name.includes(t('aiops.networkKeyword')) && <Network className="h-4 w-4 dg-text-purple" />}
|
||||||
<span className="font-medium text-[var(--dg-text-primary)]">{prediction.name}</span>
|
<span className="font-medium text-[var(--dg-text-primary)]">{prediction.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{getTrendIcon(prediction.trend)}
|
{getTrendIcon(prediction.trend)}
|
||||||
<span className={`text-sm ${trendColorClass(prediction.trend)}`}>
|
<span className={`text-sm ${trendColorClass(prediction.trend)}`}>
|
||||||
{prediction.trend === 'up' ? '上升' : prediction.trend === 'down' ? '下降' : '平稳'}
|
{prediction.trend === 'up' ? t('aiops.trendUp') : prediction.trend === 'down' ? t('aiops.trendDown') : t('aiops.trendStable')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -203,7 +203,7 @@ export function AIPredictiveAnalytics() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs dg-text-muted mb-1">预测值 ({prediction.timeRange})</p>
|
<p className="text-xs dg-text-muted mb-1">{t('aiops.predictedValueLabel', { range: prediction.timeRange })}</p>
|
||||||
<p className={`text-lg font-bold ${trendColorClass(prediction.trend)}`}>
|
<p className={`text-lg font-bold ${trendColorClass(prediction.trend)}`}>
|
||||||
{prediction.predicted}
|
{prediction.predicted}
|
||||||
{prediction.unit}
|
{prediction.unit}
|
||||||
@@ -284,7 +284,7 @@ export function AIPredictiveAnalytics() {
|
|||||||
<div className="flex items-center gap-4 mt-2">
|
<div className="flex items-center gap-4 mt-2">
|
||||||
<span className="text-xs dg-text-muted">{anomaly.timeframe}</span>
|
<span className="text-xs dg-text-muted">{anomaly.timeframe}</span>
|
||||||
<span className="text-xs dg-text-muted">
|
<span className="text-xs dg-text-muted">
|
||||||
概率:{' '}
|
{t('aiops.probabilityColon')}
|
||||||
<span className={anomaly.probability >= 70 ? 'dg-text-red' : 'dg-text-yellow'}>
|
<span className={anomaly.probability >= 70 ? 'dg-text-red' : 'dg-text-yellow'}>
|
||||||
{anomaly.probability}%
|
{anomaly.probability}%
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const SAFETY_MODES = [
|
|||||||
label: i18n.t('aiops.safetyBalanced'),
|
label: i18n.t('aiops.safetyBalanced'),
|
||||||
short: i18n.t('aiops.safetyHighRiskConfirm'),
|
short: i18n.t('aiops.safetyHighRiskConfirm'),
|
||||||
desc: i18n.t('aiops.safetyBalancedDesc'),
|
desc: i18n.t('aiops.safetyBalancedDesc'),
|
||||||
examples: 'cat/grep/df 自动 · rm/reboot 确认',
|
examples: i18n.t('aiops.safetyBalancedExamples'),
|
||||||
recommended: true,
|
recommended: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -82,7 +82,7 @@ const SAFETY_MODES = [
|
|||||||
|
|
||||||
// formatServerLabel 主机下拉展示文案 // Logan.Li
|
// formatServerLabel 主机下拉展示文案 // Logan.Li
|
||||||
function formatServerLabel(s: { hostname: string; ip: string }) {
|
function formatServerLabel(s: { hostname: string; ip: string }) {
|
||||||
return `${s.hostname}(${s.ip})`;
|
return i18n.t('aiops.hostIpFormat', { hostname: s.hostname, ip: s.ip });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AgentCLI() {
|
export default function AgentCLI() {
|
||||||
@@ -291,7 +291,7 @@ export default function AgentCLI() {
|
|||||||
}
|
}
|
||||||
case 'error':
|
case 'error':
|
||||||
case 'aborted':
|
case 'aborted':
|
||||||
push({ kind: type === 'aborted' ? 'info' : 'error', content: ev.message || (type === 'aborted' ? '任务已中断' : '发生错误') });
|
push({ kind: type === 'aborted' ? 'info' : 'error', content: ev.message || (type === 'aborted' ? t('aiops.taskAborted') : t('aiops.errorOccurred')) });
|
||||||
setRunning(false);
|
setRunning(false);
|
||||||
setCanContinue(type !== 'aborted');
|
setCanContinue(type !== 'aborted');
|
||||||
setHistoryRefresh((k) => k + 1);
|
setHistoryRefresh((k) => k + 1);
|
||||||
@@ -303,13 +303,13 @@ export default function AgentCLI() {
|
|||||||
const handleConfirm = async (ev: WsEvent) => {
|
const handleConfirm = async (ev: WsEvent) => {
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: t('aiops.confirmHighRisk'),
|
title: t('aiops.confirmHighRisk'),
|
||||||
description: `工具:${ev.tool_name}\n风险:${ev.risk}\n命令:${ev.command || t('system.none')}`,
|
description: t('aiops.confirmToolExec', { tool: ev.tool_name, risk: ev.risk, cmd: ev.command || t('system.none') }),
|
||||||
confirmText: t('aiops.assistantConfirmExec'),
|
confirmText: t('aiops.assistantConfirmExec'),
|
||||||
cancelText: t('common.deny'),
|
cancelText: t('common.deny'),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
wsRef.current?.send(JSON.stringify({ type: ok ? 'confirm' : 'deny', confirm_id: ev.confirm_id }));
|
wsRef.current?.send(JSON.stringify({ type: ok ? 'confirm' : 'deny', confirm_id: ev.confirm_id }));
|
||||||
if (!ok) push({ kind: 'info', content: `已拒绝执行:${ev.command || ev.tool_name}` });
|
if (!ok) push({ kind: 'info', content: t('aiops.rejectedExec', { cmd: ev.command || ev.tool_name }) });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送任务 // Logan.Li
|
// 发送任务 // Logan.Li
|
||||||
@@ -342,7 +342,7 @@ export default function AgentCLI() {
|
|||||||
setItems([
|
setItems([
|
||||||
{
|
{
|
||||||
id: nextId(), kind: 'info',
|
id: nextId(), kind: 'info',
|
||||||
content: `查看历史记录:${sess.title}(${sess.status === 'done' ? '已完成' : sess.status})— 仅浏览,点击「继续追问」可在此基础上接着执行`,
|
content: t('aiops.viewHistoryDetail', { title: sess.title, status: sess.status === 'done' ? t('aiops.taskDone') : sess.status }),
|
||||||
},
|
},
|
||||||
...replayItems,
|
...replayItems,
|
||||||
]);
|
]);
|
||||||
@@ -357,7 +357,7 @@ export default function AgentCLI() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wsRef.current.send(JSON.stringify({ type: 'resume', session_id: viewSession.id }));
|
wsRef.current.send(JSON.stringify({ type: 'resume', session_id: viewSession.id }));
|
||||||
push({ kind: 'info', content: `正在恢复会话「${viewSession.title}」…` });
|
push({ kind: 'info', content: t('aiops.resumingSession', { title: viewSession.title }) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNewTask = () => {
|
const handleNewTask = () => {
|
||||||
@@ -439,7 +439,7 @@ export default function AgentCLI() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="dg-text-secondary leading-relaxed mt-1">{m.desc}</p>
|
<p className="dg-text-secondary leading-relaxed mt-1">{m.desc}</p>
|
||||||
<p className="text-[10px] dg-text-muted mt-1">例:{m.examples}</p>
|
<p className="text-[10px] dg-text-muted mt-1">{t('aiops.examplePrefix')}{m.examples}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
@@ -454,7 +454,7 @@ export default function AgentCLI() {
|
|||||||
title={historyOpen ? t('aiops.collapseHistory') : t('aiops.expandHistory')}
|
title={historyOpen ? t('aiops.collapseHistory') : t('aiops.expandHistory')}
|
||||||
>
|
>
|
||||||
{historyOpen ? <PanelLeftClose className="w-3.5 h-3.5 mr-1" /> : <History className="w-3.5 h-3.5 mr-1" />}
|
{historyOpen ? <PanelLeftClose className="w-3.5 h-3.5 mr-1" /> : <History className="w-3.5 h-3.5 mr-1" />}
|
||||||
历史
|
{t('aiops.history')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="flex-1 min-w-[1rem]" />
|
<div className="flex-1 min-w-[1rem]" />
|
||||||
@@ -472,7 +472,7 @@ export default function AgentCLI() {
|
|||||||
)}
|
)}
|
||||||
{running && (
|
{running && (
|
||||||
<Badge className="h-6 text-[10px] bg-blue-500/15 dg-text-blue border-blue-500/30">
|
<Badge className="h-6 text-[10px] bg-blue-500/15 dg-text-blue border-blue-500/30">
|
||||||
<Loader2 className="w-3 h-3 mr-1 animate-spin" />第 {round} 轮 · {tokens}t
|
<Loader2 className="w-3 h-3 mr-1 animate-spin" />{t('aiops.roundTokens', { round, tokens })}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -513,7 +513,7 @@ export default function AgentCLI() {
|
|||||||
{viewMode && viewSession && (
|
{viewMode && viewSession && (
|
||||||
<div className="flex items-center gap-2 px-3 py-2 border-b border-[var(--dg-border)] bg-amber-500/5 shrink-0 flex-wrap">
|
<div className="flex items-center gap-2 px-3 py-2 border-b border-[var(--dg-border)] bg-amber-500/5 shrink-0 flex-wrap">
|
||||||
<span className="text-xs dg-text-secondary flex-1 min-w-[12rem]">
|
<span className="text-xs dg-text-secondary flex-1 min-w-[12rem]">
|
||||||
历史记录(只读)· {viewSession.title}
|
{t('aiops.historyReadonly', { title: viewSession.title })}
|
||||||
</span>
|
</span>
|
||||||
{canResumeView && (
|
{canResumeView && (
|
||||||
<Button size="sm" variant="outline" className="h-7 text-xs" onClick={resumeHistorySession}>
|
<Button size="sm" variant="outline" className="h-7 text-xs" onClick={resumeHistorySession}>
|
||||||
@@ -548,9 +548,9 @@ export default function AgentCLI() {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm text-center max-w-md">
|
<div className="text-sm text-center max-w-md">
|
||||||
描述你要执行的任务,例如「检查磁盘使用率并清理大文件」
|
{t('aiops.describeTaskHint')}
|
||||||
<div className="text-xs dg-text-muted mt-2">
|
<div className="text-xs dg-text-muted mt-2">
|
||||||
当前安全模式:{currentSafety.label}({currentSafety.short})
|
{t('aiops.currentSafetyMode', { label: currentSafety.label, short: currentSafety.short })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default function AgentCLIHistory({ serverId, onView, onNewTask, refreshKe
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const ok = await confirm({
|
const ok = await confirm({
|
||||||
title: i18n.t('audit.terminal.deleteTitle'),
|
title: i18n.t('audit.terminal.deleteTitle'),
|
||||||
description: `确定删除「${sess.title}」?此操作不可恢复。`,
|
description: i18n.t('aiops.confirmDeleteSession', { title: sess.title }),
|
||||||
confirmText: i18n.t('kubernetes.storage.delete2'),
|
confirmText: i18n.t('kubernetes.storage.delete2'),
|
||||||
cancelText: i18n.t('rbac.jobRole.cancel'),
|
cancelText: i18n.t('rbac.jobRole.cancel'),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
@@ -96,7 +96,7 @@ export default function AgentCLIHistory({ serverId, onView, onNewTask, refreshKe
|
|||||||
<History className="w-3.5 h-3.5 dg-text-secondary shrink-0" />
|
<History className="w-3.5 h-3.5 dg-text-secondary shrink-0" />
|
||||||
<span className="text-xs font-medium text-[var(--dg-text-primary)] flex-1 truncate"> {i18n.t('k8s.history.historical')} </span>
|
<span className="text-xs font-medium text-[var(--dg-text-primary)] flex-1 truncate"> {i18n.t('k8s.history.historical')} </span>
|
||||||
<Button variant="ghost" size="sm" className="h-6 px-1.5 text-[10px]" onClick={() => { setActiveId(null); onNewTask(); }}>
|
<Button variant="ghost" size="sm" className="h-6 px-1.5 text-[10px]" onClick={() => { setActiveId(null); onNewTask(); }}>
|
||||||
<Plus className="w-3 h-3 mr-0.5" />新建
|
<Plus className="w-3 h-3 mr-0.5" />{i18n.t('aiops.newSessionBtn')}
|
||||||
</Button>
|
</Button>
|
||||||
{onCollapse && (
|
{onCollapse && (
|
||||||
<Button variant="ghost" size="icon" className="h-6 w-6 dg-text-muted hover:dg-text-secondary" onClick={onCollapse} title={i18n.t('aiops.agentclihistory.s590d5c76')}>
|
<Button variant="ghost" size="icon" className="h-6 w-6 dg-text-muted hover:dg-text-secondary" onClick={onCollapse} title={i18n.t('aiops.agentclihistory.s590d5c76')}>
|
||||||
@@ -127,7 +127,7 @@ export default function AgentCLIHistory({ serverId, onView, onNewTask, refreshKe
|
|||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="text-[11px] text-[var(--dg-text-primary)] truncate leading-tight">{s.title}</div>
|
<div className="text-[11px] text-[var(--dg-text-primary)] truncate leading-tight">{s.title}</div>
|
||||||
<div className="text-[10px] dg-text-muted mt-0.5">
|
<div className="text-[10px] dg-text-muted mt-0.5">
|
||||||
{s.rounds}轮 · {s.total_tokens || 0}t
|
{i18n.t('aiops.roundsTokensSummary', { rounds: s.rounds, tokens: s.total_tokens || 0 })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Badge className={`text-[9px] px-1 py-0 border shrink-0 ${statusBadge(s.status)}`}>
|
<Badge className={`text-[9px] px-1 py-0 border shrink-0 ${statusBadge(s.status)}`}>
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ function RoundGroup({ round, items, isLatest }: { round: number; items: ChatItem
|
|||||||
>
|
>
|
||||||
{open ? <ChevronDown className="w-3.5 h-3.5 dg-text-secondary shrink-0" /> : <ChevronRight className="w-3.5 h-3.5 dg-text-secondary shrink-0" />}
|
{open ? <ChevronDown className="w-3.5 h-3.5 dg-text-secondary shrink-0" /> : <ChevronRight className="w-3.5 h-3.5 dg-text-secondary shrink-0" />}
|
||||||
<Layers className="w-3.5 h-3.5 dg-text-purple shrink-0" />
|
<Layers className="w-3.5 h-3.5 dg-text-purple shrink-0" />
|
||||||
<span className="font-medium text-[var(--dg-text-primary)]">第 {round} 轮</span>
|
<span className="font-medium text-[var(--dg-text-primary)]">{i18n.t('aiops.roundN', { round })}</span>
|
||||||
<span className="dg-text-muted">{toolCount > 0 ? `· ${toolCount} 个工具` : ''}</span>
|
<span className="dg-text-muted">{toolCount > 0 ? i18n.t('aiops.toolCountLabel', { count: toolCount }) : ''}</span>
|
||||||
{!open && preview && (
|
{!open && preview && (
|
||||||
<span className="ml-1 dg-text-muted truncate flex-1">{preview}{preview.length >= 60 ? '…' : ''}</span>
|
<span className="ml-1 dg-text-muted truncate flex-1">{preview}{preview.length >= 60 ? '…' : ''}</span>
|
||||||
)}
|
)}
|
||||||
@@ -268,7 +268,7 @@ function ThoughtBlock({
|
|||||||
<button type="button" className="flex items-center gap-1.5 text-xs dg-text-secondary w-full text-left" onClick={() => setOpen((v) => !v)}>
|
<button type="button" className="flex items-center gap-1.5 text-xs dg-text-secondary w-full text-left" onClick={() => setOpen((v) => !v)}>
|
||||||
{open ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronRight className="w-3.5 h-3.5" />}
|
{open ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronRight className="w-3.5 h-3.5" />}
|
||||||
<Brain className="w-3.5 h-3.5 dg-text-purple" />
|
<Brain className="w-3.5 h-3.5 dg-text-purple" />
|
||||||
{!hideRoundLabel && round != null && <span className="dg-text-muted">第 {round} 轮思考</span>}
|
{!hideRoundLabel && round != null && <span className="dg-text-muted">{i18n.t('aiops.roundThinking', { round })}</span>}
|
||||||
{hideRoundLabel && <span className="dg-text-muted"> {i18n.t('aiops.agentclimessage.s21d68b2d')} </span>}
|
{hideRoundLabel && <span className="dg-text-muted"> {i18n.t('aiops.agentclimessage.s21d68b2d')} </span>}
|
||||||
{streaming && <Loader2 className="w-3 h-3 dg-text-purple animate-spin" />}
|
{streaming && <Loader2 className="w-3 h-3 dg-text-purple animate-spin" />}
|
||||||
<span className="ml-1 dg-text-muted truncate">{!open && preview}</span>
|
<span className="ml-1 dg-text-muted truncate">{!open && preview}</span>
|
||||||
@@ -305,7 +305,7 @@ function ToolBlock({ item, inRoundGroup }: { item: ChatItem; inRoundGroup?: bool
|
|||||||
)}
|
)}
|
||||||
{!item.pending && item.output != null && (
|
{!item.pending && item.output != null && (
|
||||||
<>
|
<>
|
||||||
{item.duration && <div className="text-[10px] dg-text-muted">耗时 {item.duration}</div>}
|
{item.duration && <div className="text-[10px] dg-text-muted">{i18n.t('aiops.durationLabel', { duration: item.duration })}</div>}
|
||||||
{(item.toolName === 'analyze_output' || looksLikeMarkdown(item.output)) ? (
|
{(item.toolName === 'analyze_output' || looksLikeMarkdown(item.output)) ? (
|
||||||
<div className="bg-[var(--dg-bg-primary)]/40 rounded px-2 py-1.5 max-h-80 overflow-y-auto">
|
<div className="bg-[var(--dg-bg-primary)]/40 rounded px-2 py-1.5 max-h-80 overflow-y-auto">
|
||||||
<AgentMarkdown content={item.output || i18n.t('aiops.agentclimessage.s216caee5')} />
|
<AgentMarkdown content={item.output || i18n.t('aiops.agentclimessage.s216caee5')} />
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ export function AutoFixDialog({ open, onOpenChange, script }: AutoFixDialogProps
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<Terminal className="h-5 w-5 dg-text-purple" />
|
<Terminal className="h-5 w-5 dg-text-purple" />
|
||||||
自动修复脚本
|
{i18n.t('aiops.autoFixScriptTitle')}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="dg-text-muted">
|
<DialogDescription className="dg-text-muted">
|
||||||
AI生成的自动修复脚本,请确认后执行
|
{i18n.t('aiops.autoFixDialogDesc')}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4 py-4">
|
<div className="space-y-4 py-4">
|
||||||
@@ -55,17 +55,17 @@ export function AutoFixDialog({ open, onOpenChange, script }: AutoFixDialogProps
|
|||||||
<div className="flex items-center gap-2 p-3 bg-amber-500/10 rounded-lg border border-amber-500/30">
|
<div className="flex items-center gap-2 p-3 bg-amber-500/10 rounded-lg border border-amber-500/30">
|
||||||
<AlertTriangle className="h-4 w-4 dg-text-yellow" />
|
<AlertTriangle className="h-4 w-4 dg-text-yellow" />
|
||||||
<span className="text-sm dg-text-yellow">
|
<span className="text-sm dg-text-yellow">
|
||||||
警告:执行脚本前请确保已备份重要数据
|
{i18n.t('aiops.autoFixWarning')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button variant="outline" className="flex-1 dg-btn-secondary" onClick={() => onOpenChange(false)}>
|
<Button variant="outline" className="flex-1 dg-btn-secondary" onClick={() => onOpenChange(false)}>
|
||||||
取消
|
{i18n.t('common.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="flex-1 bg-purple-600 hover:bg-purple-700" onClick={() => onOpenChange(false)}>
|
<Button className="flex-1 bg-purple-600 hover:bg-purple-700" onClick={() => onOpenChange(false)}>
|
||||||
<Play className="h-4 w-4 mr-2" />
|
<Play className="h-4 w-4 mr-2" />
|
||||||
确认执行
|
{i18n.t('aiops.confirmExecute')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ export default function LLMConfigPage() {
|
|||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
)}
|
)}
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>云服务</SelectLabel>
|
<SelectLabel>{t('aiops.cloudService')}</SelectLabel>
|
||||||
{CLOUD_GENERAL_PROVIDERS.map((p) => (
|
{CLOUD_GENERAL_PROVIDERS.map((p) => (
|
||||||
<SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>
|
<SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -319,7 +319,7 @@ export default function LLMConfigPage() {
|
|||||||
))}
|
))}
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>自定义接入</SelectLabel>
|
<SelectLabel>{t('aiops.customAccess')}</SelectLabel>
|
||||||
{CUSTOM_PROVIDERS.map((p) => (
|
{CUSTOM_PROVIDERS.map((p) => (
|
||||||
<SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>
|
<SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -356,7 +356,7 @@ export default function LLMConfigPage() {
|
|||||||
{unknownProvider && (
|
{unknownProvider && (
|
||||||
<div className="flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs text-[var(--dg-text-secondary)]">
|
<div className="flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs text-[var(--dg-text-secondary)]">
|
||||||
<AlertCircle className="w-4 h-4 shrink-0 text-amber-500" />
|
<AlertCircle className="w-4 h-4 shrink-0 text-amber-500" />
|
||||||
<span>当前提供商「{cfg.provider}」不在预设列表中,可继续使用或切换到其他提供商。</span>
|
<span>{t('aiops.unknownProviderHint', { provider: cfg.provider })}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ export default function LLMConfigPage() {
|
|||||||
<ShoppingBag className="w-4 h-4 shrink-0 mt-0.5 text-[var(--dg-orange)]" />
|
<ShoppingBag className="w-4 h-4 shrink-0 mt-0.5 text-[var(--dg-orange)]" />
|
||||||
<div className="flex-1 min-w-0 space-y-1">
|
<div className="flex-1 min-w-0 space-y-1">
|
||||||
<div className="text-sm text-[var(--dg-text-secondary)]">
|
<div className="text-sm text-[var(--dg-text-secondary)]">
|
||||||
{prov.planHint || `使用 ${prov.name} 需先开通服务并获取 API Key`}
|
{prov.planHint || t('aiops.providerNeedsKey', { name: prov.name })}
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
href={prov.planUrl}
|
href={prov.planUrl}
|
||||||
@@ -412,7 +412,7 @@ export default function LLMConfigPage() {
|
|||||||
<Card className="dg-card">
|
<Card className="dg-card">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-[var(--dg-text-primary)] text-base flex items-center gap-2">
|
<CardTitle className="text-[var(--dg-text-primary)] text-base flex items-center gap-2">
|
||||||
<Sparkles className="w-4 h-4 text-[var(--dg-orange)]" />推理 / Plan 模式
|
<Sparkles className="w-4 h-4 text-[var(--dg-orange)]" />{t('aiops.reasoningPlanMode')}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
@@ -420,7 +420,7 @@ export default function LLMConfigPage() {
|
|||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-sm text-[var(--dg-text-secondary)]">{t('aiops.enableReasoning')}</div>
|
<div className="text-sm text-[var(--dg-text-secondary)]">{t('aiops.enableReasoning')}</div>
|
||||||
<div className="text-xs dg-text-muted">
|
<div className="text-xs dg-text-muted">
|
||||||
适用于 OpenAI o-series / DeepSeek R1 / Claude 3.7 Sonnet 等推理模型;普通模型开启后可能报错。
|
{t('aiops.reasoningModelHint')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch checked={cfg.reasoning_enabled ?? false} onCheckedChange={(v) => set('reasoning_enabled', v)} />
|
<Switch checked={cfg.reasoning_enabled ?? false} onCheckedChange={(v) => set('reasoning_enabled', v)} />
|
||||||
@@ -437,17 +437,17 @@ export default function LLMConfigPage() {
|
|||||||
{testing
|
{testing
|
||||||
? <Loader2 className="w-4 h-4 mr-1 animate-spin" />
|
? <Loader2 className="w-4 h-4 mr-1 animate-spin" />
|
||||||
: <Wifi className="w-4 h-4 mr-1" />}
|
: <Wifi className="w-4 h-4 mr-1" />}
|
||||||
测试连接
|
{t('aiops.testConnectionBtn')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={save} disabled={saving || !isVerified} className="dg-btn-primary">
|
<Button onClick={save} disabled={saving || !isVerified} className="dg-btn-primary">
|
||||||
{saving
|
{saving
|
||||||
? <Loader2 className="w-4 h-4 mr-1 animate-spin" />
|
? <Loader2 className="w-4 h-4 mr-1 animate-spin" />
|
||||||
: <Save className="w-4 h-4 mr-1" />}
|
: <Save className="w-4 h-4 mr-1" />}
|
||||||
保存配置
|
{t('aiops.saveConfigBtn')}
|
||||||
</Button>
|
</Button>
|
||||||
{isVerified ? (
|
{isVerified ? (
|
||||||
<span className="inline-flex items-center gap-1 text-xs text-[var(--dg-green)]">
|
<span className="inline-flex items-center gap-1 text-xs text-[var(--dg-green)]">
|
||||||
<CheckCircle2 className="w-3.5 h-3.5" />已验证,可保存
|
<CheckCircle2 className="w-3.5 h-3.5" />{t('aiops.verifiedCanSave')}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-xs dg-text-muted">{t('system.switchProviderTest')}</span>
|
<span className="text-xs dg-text-muted">{t('system.switchProviderTest')}</span>
|
||||||
@@ -459,7 +459,7 @@ export default function LLMConfigPage() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-[var(--dg-text-primary)] text-base flex items-center gap-2">
|
<CardTitle className="text-[var(--dg-text-primary)] text-base flex items-center gap-2">
|
||||||
<MessageSquare className="w-4 h-4 text-[var(--dg-green)]" />
|
<MessageSquare className="w-4 h-4 text-[var(--dg-green)]" />
|
||||||
测试结果
|
{t('aiops.testResultTitle')}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
@@ -467,9 +467,9 @@ export default function LLMConfigPage() {
|
|||||||
{testResult.answer || t('aiops.noModelContent')}
|
{testResult.answer || t('aiops.noModelContent')}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-3 text-xs dg-text-muted">
|
<div className="flex flex-wrap gap-3 text-xs dg-text-muted">
|
||||||
<span className="bg-[var(--dg-bg-tertiary)] px-2 py-1 rounded">模型: {testResult.model || t('common.unknown')}</span>
|
<span className="bg-[var(--dg-bg-tertiary)] px-2 py-1 rounded">{t('aiops.modelColon')}{testResult.model || t('common.unknown')}</span>
|
||||||
<span className="bg-[var(--dg-bg-tertiary)] px-2 py-1 rounded">提供商: {testResult.provider}</span>
|
<span className="bg-[var(--dg-bg-tertiary)] px-2 py-1 rounded">{t('aiops.providerColon')}{testResult.provider}</span>
|
||||||
<span className="bg-[var(--dg-bg-tertiary)] px-2 py-1 rounded">消耗 Token: {testResult.tokens_used ?? '-'}</span>
|
<span className="bg-[var(--dg-bg-tertiary)] px-2 py-1 rounded">{t('aiops.tokensConsumed')}{testResult.tokens_used ?? '-'}</span>
|
||||||
{testResult.reasoning_enabled && (
|
{testResult.reasoning_enabled && (
|
||||||
<span className="bg-amber-900/40 dg-text-yellow px-2 py-1 rounded">{t('aiops.reasoningOn')}</span>
|
<span className="bg-amber-900/40 dg-text-yellow px-2 py-1 rounded">{t('aiops.reasoningOn')}</span>
|
||||||
)}
|
)}
|
||||||
@@ -481,11 +481,7 @@ export default function LLMConfigPage() {
|
|||||||
<div className="flex items-start gap-2 text-xs dg-text-muted">
|
<div className="flex items-start gap-2 text-xs dg-text-muted">
|
||||||
<AlertCircle className="w-4 h-4 shrink-0 mt-0.5" />
|
<AlertCircle className="w-4 h-4 shrink-0 mt-0.5" />
|
||||||
<span>
|
<span>
|
||||||
云服务:选择提供商 → 开通并填写 API Key → 测试连接 → 保存。
|
{t('aiops.configHelpText')}
|
||||||
编码套餐:GLM / Kimi / MiniMax 固定月费套餐,适合 Agent CLI 高频调用(注意 Key 与按量版不互通)。
|
|
||||||
大模型自定义:填请求地址 + API Key + 模型名,兼容 OpenRouter 等 OpenAI 格式服务。
|
|
||||||
本地/自建(Ollama、vLLM):填写 Endpoint 与模型名即可,API Key 可留空。
|
|
||||||
测试时会发送「你是什么模型?请简短回答。」以确认接入目标模型。
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</PageShell>
|
</PageShell>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export function PredictiveCapacityTab({
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||||
<HardDrive className="h-5 w-5 dg-text-yellow" />
|
<HardDrive className="h-5 w-5 dg-text-yellow" />
|
||||||
容量规划预警
|
{i18n.t('aiops.capacityWarning')}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
{!loading && forecasts.length > 0 && (
|
{!loading && forecasts.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
@@ -55,7 +55,7 @@ export function PredictiveCapacityTab({
|
|||||||
) : (
|
) : (
|
||||||
<Sparkles className="h-3.5 w-3.5 mr-1" />
|
<Sparkles className="h-3.5 w-3.5 mr-1" />
|
||||||
)}
|
)}
|
||||||
AI 深度解读
|
{i18n.t('aiops.aiDeepAnalysis')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -63,7 +63,7 @@ export function PredictiveCapacityTab({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12 dg-text-muted">
|
<div className="flex items-center justify-center py-12 dg-text-muted">
|
||||||
<Loader2 className="h-5 w-5 animate-spin mr-2" /> 加载容量数据...
|
<Loader2 className="h-5 w-5 animate-spin mr-2" /> {i18n.t('aiops.loadingCapacityData')}
|
||||||
</div>
|
</div>
|
||||||
) : forecasts.length === 0 ? (
|
) : forecasts.length === 0 ? (
|
||||||
<div className="text-center py-12 dg-text-muted"> {i18n.t('aiops.predictivecapac.sf803eb75')} </div>
|
<div className="text-center py-12 dg-text-muted"> {i18n.t('aiops.predictivecapac.sf803eb75')} </div>
|
||||||
@@ -74,7 +74,7 @@ export function PredictiveCapacityTab({
|
|||||||
<div className="p-4 rounded-lg border border-purple-500/30 bg-purple-500/5">
|
<div className="p-4 rounded-lg border border-purple-500/30 bg-purple-500/5">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Sparkles className="h-4 w-4 dg-text-purple" />
|
<Sparkles className="h-4 w-4 dg-text-purple" />
|
||||||
<span className="text-sm font-medium text-purple-300">AI 深度解读</span>
|
<span className="text-sm font-medium text-purple-300">{i18n.t('aiops.aiDeepAnalysis')}</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-[var(--dg-text-secondary)] whitespace-pre-wrap break-words">{insight}</p>
|
<p className="text-sm text-[var(--dg-text-secondary)] whitespace-pre-wrap break-words">{insight}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,15 +106,13 @@ export function PredictiveCapacityTab({
|
|||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<div className="flex items-center justify-between text-sm mb-1">
|
<div className="flex items-center justify-between text-sm mb-1">
|
||||||
<span className="dg-text-secondary">7天使用率预测</span>
|
<span className="dg-text-secondary">{i18n.t('aiops.sevenDayForecast')}</span>
|
||||||
<span className="dg-text-secondary">
|
<span className="dg-text-secondary">
|
||||||
{forecast.daysUntilThreshold >= 999 ? (
|
{forecast.daysUntilThreshold >= 999 ? (
|
||||||
i18n.t('aiops.predictutilsts.s179fd5a9')
|
i18n.t('aiops.predictutilsts.s179fd5a9')
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
预计{' '}
|
{i18n.t('aiops.expectedReachThreshold', { days: forecast.daysUntilThreshold })}
|
||||||
<span className="dg-text-yellow font-medium">{forecast.daysUntilThreshold}天</span>{' '}
|
|
||||||
后达到阈值
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -131,9 +129,9 @@ export function PredictiveCapacityTab({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between text-xs mt-1">
|
<div className="flex items-center justify-between text-xs mt-1">
|
||||||
<span className="dg-text-muted">当前 {forecast.currentUsage}%</span>
|
<span className="dg-text-muted">{i18n.t('aiops.currentUsageLabel', { n: forecast.currentUsage })}</span>
|
||||||
<span className="dg-text-yellow">预测 {forecast.predictedUsage}%</span>
|
<span className="dg-text-yellow">{i18n.t('aiops.predictedUsageLabel', { n: forecast.predictedUsage })}</span>
|
||||||
<span className="dg-text-red">阈值 {forecast.threshold}%</span>
|
<span className="dg-text-red">{i18n.t('aiops.thresholdLabel', { n: forecast.threshold })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -141,7 +139,7 @@ export function PredictiveCapacityTab({
|
|||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<Zap className="h-4 w-4 dg-text-green mt-0.5" />
|
<Zap className="h-4 w-4 dg-text-green mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm dg-text-secondary">AI建议:</span>
|
<span className="text-sm dg-text-secondary">{i18n.t('aiops.aiSuggestionColon')}</span>
|
||||||
<p className="text-sm text-[var(--dg-text-secondary)] mt-1">{forecast.recommendation}</p>
|
<p className="text-sm text-[var(--dg-text-secondary)] mt-1">{forecast.recommendation}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export function mapAlertEvent(event: JsonData): AlertItem {
|
|||||||
const endTime = event.resolved_at ? new Date(event.resolved_at) : new Date();
|
const endTime = event.resolved_at ? new Date(event.resolved_at) : new Date();
|
||||||
const startTime = new Date(event.started_at);
|
const startTime = new Date(event.started_at);
|
||||||
const diffMin = Math.max(1, Math.round((endTime.getTime() - startTime.getTime()) / 60000));
|
const diffMin = Math.max(1, Math.round((endTime.getTime() - startTime.getTime()) / 60000));
|
||||||
const duration = diffMin >= 60 ? `${Math.floor(diffMin / 60)}小时${diffMin % 60}分钟` : `${diffMin}分钟`;
|
const duration = diffMin >= 60 ? i18n.t('aiops.durationHM', { hours: Math.floor(diffMin / 60), mins: diffMin % 60 }) : i18n.t('aiops.durationM', { mins: diffMin });
|
||||||
|
|
||||||
const startedAt = startTime.toLocaleString('zh-CN', { hour12: false });
|
const startedAt = startTime.toLocaleString('zh-CN', { hour12: false });
|
||||||
|
|
||||||
|
|||||||
@@ -251,18 +251,18 @@ export function computePredictions(input: ComputePredictionsInput): ComputedPred
|
|||||||
probability = Math.max(5, Math.min(90, probability));
|
probability = Math.max(5, Math.min(90, probability));
|
||||||
|
|
||||||
// 时间范围:基于斜率估算何时触及阈值
|
// 时间范围:基于斜率估算何时触及阈值
|
||||||
let timeframe = '未来24小时';
|
let timeframe = i18n.t('aiops.next24Hours');
|
||||||
if (slope > 0.01 && threshold > current) {
|
if (slope > 0.01 && threshold > current) {
|
||||||
const hours = (threshold - current) / slope;
|
const hours = (threshold - current) / slope;
|
||||||
if (hours < 4) timeframe = '未来4小时';
|
if (hours < 4) timeframe = i18n.t('aiops.next4Hours');
|
||||||
else if (hours < 12) timeframe = '未来12小时';
|
else if (hours < 12) timeframe = i18n.t('aiops.next12Hours');
|
||||||
else if (hours < 24) timeframe = '未来24小时';
|
else if (hours < 24) timeframe = i18n.t('aiops.next24Hours');
|
||||||
else timeframe = `未来${Math.ceil(hours / 24)}天`;
|
else timeframe = i18n.t('aiops.nextNDays', { n: Math.ceil(hours / 24) });
|
||||||
}
|
}
|
||||||
|
|
||||||
anomalyPredictions.push({
|
anomalyPredictions.push({
|
||||||
metric: rule.name,
|
metric: rule.name,
|
||||||
predictedAnomaly: rule.description || `${rule.name}可能触发`,
|
predictedAnomaly: rule.description || i18n.t('aiops.mayTrigger', { name: rule.name }),
|
||||||
probability,
|
probability,
|
||||||
timeframe,
|
timeframe,
|
||||||
suggestedAction: anomalyAction(matchedDef.key),
|
suggestedAction: anomalyAction(matchedDef.key),
|
||||||
@@ -276,7 +276,7 @@ export function computePredictions(input: ComputePredictionsInput): ComputedPred
|
|||||||
const exprKey = exprLower.includes('service') ? 'service' : exprLower.includes('pod') ? 'pod' : 'service';
|
const exprKey = exprLower.includes('service') ? 'service' : exprLower.includes('pod') ? 'pod' : 'service';
|
||||||
anomalyPredictions.push({
|
anomalyPredictions.push({
|
||||||
metric: rule.name,
|
metric: rule.name,
|
||||||
predictedAnomaly: rule.description || `${rule.name}可能再次发生`,
|
predictedAnomaly: rule.description || i18n.t('aiops.mayRecur', { name: rule.name }),
|
||||||
probability: Math.min(60, 20 + eventCount * 5),
|
probability: Math.min(60, 20 + eventCount * 5),
|
||||||
timeframe: i18n.t('aiops.predictutilsts.s658d6e80'),
|
timeframe: i18n.t('aiops.predictutilsts.s658d6e80'),
|
||||||
suggestedAction: anomalyAction(exprKey),
|
suggestedAction: anomalyAction(exprKey),
|
||||||
@@ -328,8 +328,8 @@ export function trendColorClass(trend: string): string {
|
|||||||
export function buildCapacityInsightPrompt(forecasts: CapacityForecast[]): string {
|
export function buildCapacityInsightPrompt(forecasts: CapacityForecast[]): string {
|
||||||
if (forecasts.length === 0) return '';
|
if (forecasts.length === 0) return '';
|
||||||
const lines = forecasts.map((f) => {
|
const lines = forecasts.map((f) => {
|
||||||
const risk = f.daysUntilThreshold >= 999 ? i18n.t('aiops.predictutilsts.s179fd5a9') : `${f.daysUntilThreshold} 天后触及阈值`;
|
const risk = f.daysUntilThreshold >= 999 ? i18n.t('aiops.predictutilsts.s179fd5a9') : i18n.t('aiops.daysToThreshold', { days: f.daysUntilThreshold });
|
||||||
return `- ${f.resource}:当前 ${f.currentUsage}% → 7天后预测 ${f.predictedUsage}%(阈值 ${f.threshold}%),${risk}`;
|
return i18n.t('aiops.forecastSummary', { resource: f.resource, current: f.currentUsage, predicted: f.predictedUsage, threshold: f.threshold, risk });
|
||||||
});
|
});
|
||||||
return [
|
return [
|
||||||
i18n.t('aiops.predictutilsts.s3b46e5a0'),
|
i18n.t('aiops.predictutilsts.s3b46e5a0'),
|
||||||
|
|||||||
@@ -6537,6 +6537,90 @@
|
|||||||
"predictivecapac.sf803eb75": "暂无容量数据",
|
"predictivecapac.sf803eb75": "暂无容量数据",
|
||||||
"predictivecapac.scb0ed5ef": "紧急",
|
"predictivecapac.scb0ed5ef": "紧急",
|
||||||
"assistantts.s16f94bee": "流式响应不可用",
|
"assistantts.s16f94bee": "流式响应不可用",
|
||||||
"assistantts.sfeeaa489": "LLM 流式响应错误"
|
"assistantts.sfeeaa489": "LLM 流式响应错误",
|
||||||
|
"history": "History",
|
||||||
|
"examplePrefix": "Ex: ",
|
||||||
|
"roundTokens": "Round {{round}} · {{tokens}}t",
|
||||||
|
"historyReadonly": "History (read-only) · {{title}}",
|
||||||
|
"describeTaskHint": "Describe the task you want to execute, e.g. \"Check disk usage and clean up large files\"",
|
||||||
|
"currentSafetyMode": "Current safety mode: {{label}} ({{short}})",
|
||||||
|
"taskAborted": "Task aborted",
|
||||||
|
"errorOccurred": "An error occurred",
|
||||||
|
"confirmToolExec": "Tool: {{tool}}\nRisk: {{risk}}\nCommand: {{cmd}}",
|
||||||
|
"rejectedExec": "Execution rejected: {{cmd}}",
|
||||||
|
"viewHistoryDetail": "Viewing history: {{title}} ({{status}}) — Read-only. Click \"Continue\" to resume execution",
|
||||||
|
"resumingSession": "Resuming session \"{{title}}\"...",
|
||||||
|
"hostIpFormat": "{{hostname}} ({{ip}})",
|
||||||
|
"cloudService": "Cloud Services",
|
||||||
|
"customAccess": "Custom Access",
|
||||||
|
"unknownProviderHint": "Current provider \"{{provider}}\" is not in the preset list. You can continue using it or switch to another provider.",
|
||||||
|
"providerNeedsKey": "To use {{name}}, you need to activate the service and obtain an API Key first",
|
||||||
|
"reasoningPlanMode": "Reasoning / Plan Mode",
|
||||||
|
"reasoningModelHint": "Suitable for reasoning models like OpenAI o-series / DeepSeek R1 / Claude 3.7 Sonnet. May cause errors with standard models.",
|
||||||
|
"testConnectionBtn": "Test Connection",
|
||||||
|
"saveConfigBtn": "Save Config",
|
||||||
|
"verifiedCanSave": "Verified, ready to save",
|
||||||
|
"testResultTitle": "Test Result",
|
||||||
|
"modelColon": "Model: ",
|
||||||
|
"providerColon": "Provider: ",
|
||||||
|
"tokensConsumed": "Tokens Used: ",
|
||||||
|
"configHelpText": "Cloud Services: Select a provider → Activate and enter API Key → Test Connection → Save.\n Coding Plans: GLM / Kimi / MiniMax fixed monthly plans, ideal for frequent Agent CLI usage (note: keys are not shared with pay-as-you-go versions).\n Custom LLM: Enter endpoint + API Key + model name, compatible with OpenRouter and other OpenAI-format services.\n Local/Self-hosted (Ollama, vLLM): Enter endpoint and model name; API Key can be left blank.\n Testing sends \"What model are you? Please answer briefly.\" to confirm the target model.",
|
||||||
|
"capabilities": "Capabilities",
|
||||||
|
"autoReportGen": "Automated Reporting",
|
||||||
|
"aiEngineRunning": "AI Engine Active",
|
||||||
|
"activeCount": "{{count}} active",
|
||||||
|
"severityCritical": "Critical",
|
||||||
|
"severityWarning": "Warning",
|
||||||
|
"severityInfo": "Info",
|
||||||
|
"durationHM": "{{hours}}h {{mins}}m",
|
||||||
|
"durationM": "{{mins}}m",
|
||||||
|
"confidencePercent": "Confidence {{n}}%",
|
||||||
|
"noAnalysisYet": "Select an alert to view AI analysis",
|
||||||
|
"aiAnalysisResult": "AI Analysis Result",
|
||||||
|
"analyzingRootCause": "AI is analyzing alert root cause...",
|
||||||
|
"rootCauseAnalysis": "Root Cause Analysis",
|
||||||
|
"impactScopeLabel": "Impact Scope",
|
||||||
|
"fixSuggestions": "Fix Suggestions",
|
||||||
|
"similarCasesFound": "Found {{count}} similar historical cases",
|
||||||
|
"autoFixScriptTitle": "Auto-Fix Script",
|
||||||
|
"executeAutoFix": "Execute Auto-Fix",
|
||||||
|
"autoFixDialogDesc": "AI-generated auto-fix script. Please confirm before execution.",
|
||||||
|
"autoFixWarning": "Warning: Ensure important data is backed up before executing the script",
|
||||||
|
"confirmExecute": "Confirm Execute",
|
||||||
|
"aiDeepAnalysis": "AI Deep Analysis",
|
||||||
|
"loadingCapacityData": "Loading capacity data...",
|
||||||
|
"sevenDayForecast": "7-Day Usage Forecast",
|
||||||
|
"expectedReachThreshold": "Expected to reach threshold in {{days}} days",
|
||||||
|
"currentUsageLabel": "Current {{n}}%",
|
||||||
|
"predictedUsageLabel": "Predicted {{n}}%",
|
||||||
|
"thresholdLabel": "Threshold {{n}}%",
|
||||||
|
"aiSuggestionColon": "AI Suggestion: ",
|
||||||
|
"keyMetricForecast": "Key Metric Forecast",
|
||||||
|
"linearRegressionForecast": "Linear Regression Forecast",
|
||||||
|
"loadingMetricData": "Loading metric data...",
|
||||||
|
"trendUp": "Up",
|
||||||
|
"trendDown": "Down",
|
||||||
|
"trendStable": "Stable",
|
||||||
|
"predictedValueLabel": "Predicted ({{range}})",
|
||||||
|
"probabilityColon": "Probability: ",
|
||||||
|
"memoryKeyword": "Memory",
|
||||||
|
"diskKeyword": "Disk",
|
||||||
|
"networkKeyword": "Network",
|
||||||
|
"roundN": "Round {{round}}",
|
||||||
|
"toolCountLabel": "{{count}} tools",
|
||||||
|
"roundThinking": "Round {{round}} Thinking",
|
||||||
|
"durationLabel": "Duration {{duration}}",
|
||||||
|
"confirmDeleteSession": "Delete \"{{title}}\"? This action cannot be undone.",
|
||||||
|
"newSessionBtn": "New",
|
||||||
|
"roundsTokensSummary": "{{rounds}} rounds · {{tokens}}t",
|
||||||
|
"next4Hours": "Next 4 hours",
|
||||||
|
"next12Hours": "Next 12 hours",
|
||||||
|
"next24Hours": "Next 24 hours",
|
||||||
|
"nextNDays": "Next {{n}} days",
|
||||||
|
"mayTrigger": "{{name}} may trigger",
|
||||||
|
"mayRecur": "{{name}} may recur",
|
||||||
|
"daysToThreshold": "reaches threshold in {{days}} days",
|
||||||
|
"forecastSummary": "- {{resource}}: Current {{current}}% → 7-day forecast {{predicted}}% (threshold {{threshold}}%), {{risk}}",
|
||||||
|
"safetyBalancedExamples": "cat/grep/df auto · rm/reboot confirm"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6537,6 +6537,90 @@
|
|||||||
"predictivecapac.sf803eb75": "暂无容量数据",
|
"predictivecapac.sf803eb75": "暂无容量数据",
|
||||||
"predictivecapac.scb0ed5ef": "紧急",
|
"predictivecapac.scb0ed5ef": "紧急",
|
||||||
"assistantts.s16f94bee": "流式响应不可用",
|
"assistantts.s16f94bee": "流式响应不可用",
|
||||||
"assistantts.sfeeaa489": "LLM 流式响应错误"
|
"assistantts.sfeeaa489": "LLM 流式响应错误",
|
||||||
|
"history": "历史",
|
||||||
|
"examplePrefix": "例:",
|
||||||
|
"roundTokens": "第 {{round}} 轮 · {{tokens}}t",
|
||||||
|
"historyReadonly": "历史记录(只读)· {{title}}",
|
||||||
|
"describeTaskHint": "描述你要执行的任务,例如「检查磁盘使用率并清理大文件」",
|
||||||
|
"currentSafetyMode": "当前安全模式:{{label}}({{short}})",
|
||||||
|
"taskAborted": "任务已中断",
|
||||||
|
"errorOccurred": "发生错误",
|
||||||
|
"confirmToolExec": "工具:{{tool}}\n风险:{{risk}}\n命令:{{cmd}}",
|
||||||
|
"rejectedExec": "已拒绝执行:{{cmd}}",
|
||||||
|
"viewHistoryDetail": "查看历史记录:{{title}}({{status}})— 仅浏览,点击「继续追问」可在此基础上接着执行",
|
||||||
|
"resumingSession": "正在恢复会话「{{title}}」…",
|
||||||
|
"hostIpFormat": "{{hostname}}({{ip}})",
|
||||||
|
"cloudService": "云服务",
|
||||||
|
"customAccess": "自定义接入",
|
||||||
|
"unknownProviderHint": "当前提供商「{{provider}}」不在预设列表中,可继续使用或切换到其他提供商。",
|
||||||
|
"providerNeedsKey": "使用 {{name}} 需先开通服务并获取 API Key",
|
||||||
|
"reasoningPlanMode": "推理 / Plan 模式",
|
||||||
|
"reasoningModelHint": "适用于 OpenAI o-series / DeepSeek R1 / Claude 3.7 Sonnet 等推理模型;普通模型开启后可能报错。",
|
||||||
|
"testConnectionBtn": "测试连接",
|
||||||
|
"saveConfigBtn": "保存配置",
|
||||||
|
"verifiedCanSave": "已验证,可保存",
|
||||||
|
"testResultTitle": "测试结果",
|
||||||
|
"modelColon": "模型:",
|
||||||
|
"providerColon": "提供商:",
|
||||||
|
"tokensConsumed": "消耗 Token:",
|
||||||
|
"configHelpText": "云服务:选择提供商 → 开通并填写 API Key → 测试连接 → 保存。\n 编码套餐:GLM / Kimi / MiniMax 固定月费套餐,适合 Agent CLI 高频调用(注意 Key 与按量版不互通)。\n 大模型自定义:填请求地址 + API Key + 模型名,兼容 OpenRouter 等 OpenAI 格式服务。\n 本地/自建(Ollama、vLLM):填写 Endpoint 与模型名即可,API Key 可留空。\n 测试时会发送「你是什么模型?请简短回答。」以确认接入目标模型。",
|
||||||
|
"capabilities": "能力展示",
|
||||||
|
"autoReportGen": "自动报告生成",
|
||||||
|
"aiEngineRunning": "AI 引擎运行中",
|
||||||
|
"activeCount": "{{count}} 个活跃",
|
||||||
|
"severityCritical": "严重",
|
||||||
|
"severityWarning": "警告",
|
||||||
|
"severityInfo": "提示",
|
||||||
|
"durationHM": "{{hours}}小时{{mins}}分钟",
|
||||||
|
"durationM": "{{mins}}分钟",
|
||||||
|
"confidencePercent": "置信度 {{n}}%",
|
||||||
|
"noAnalysisYet": "选择左侧告警查看 AI 分析",
|
||||||
|
"aiAnalysisResult": "AI 分析结果",
|
||||||
|
"analyzingRootCause": "AI正在分析告警根因...",
|
||||||
|
"rootCauseAnalysis": "根因分析",
|
||||||
|
"impactScopeLabel": "影响范围",
|
||||||
|
"fixSuggestions": "修复建议",
|
||||||
|
"similarCasesFound": "发现 {{count}} 个相似历史案例",
|
||||||
|
"autoFixScriptTitle": "自动修复脚本",
|
||||||
|
"executeAutoFix": "执行自动修复",
|
||||||
|
"autoFixDialogDesc": "AI生成的自动修复脚本,请确认后执行",
|
||||||
|
"autoFixWarning": "警告:执行脚本前请确保已备份重要数据",
|
||||||
|
"confirmExecute": "确认执行",
|
||||||
|
"aiDeepAnalysis": "AI 深度解读",
|
||||||
|
"loadingCapacityData": "加载容量数据...",
|
||||||
|
"sevenDayForecast": "7天使用率预测",
|
||||||
|
"expectedReachThreshold": "预计 {{days}} 天后达到阈值",
|
||||||
|
"currentUsageLabel": "当前 {{n}}%",
|
||||||
|
"predictedUsageLabel": "预测 {{n}}%",
|
||||||
|
"thresholdLabel": "阈值 {{n}}%",
|
||||||
|
"aiSuggestionColon": "AI建议:",
|
||||||
|
"keyMetricForecast": "关键指标预测",
|
||||||
|
"linearRegressionForecast": "线性回归预测",
|
||||||
|
"loadingMetricData": "加载指标数据...",
|
||||||
|
"trendUp": "上升",
|
||||||
|
"trendDown": "下降",
|
||||||
|
"trendStable": "平稳",
|
||||||
|
"predictedValueLabel": "预测值 ({{range}})",
|
||||||
|
"probabilityColon": "概率:",
|
||||||
|
"memoryKeyword": "内存",
|
||||||
|
"diskKeyword": "磁盘",
|
||||||
|
"networkKeyword": "网络",
|
||||||
|
"roundN": "第 {{round}} 轮",
|
||||||
|
"toolCountLabel": "{{count}} 个工具",
|
||||||
|
"roundThinking": "第 {{round}} 轮思考",
|
||||||
|
"durationLabel": "耗时 {{duration}}",
|
||||||
|
"confirmDeleteSession": "确定删除「{{title}}」?此操作不可恢复。",
|
||||||
|
"newSessionBtn": "新建",
|
||||||
|
"roundsTokensSummary": "{{rounds}}轮 · {{tokens}}t",
|
||||||
|
"next4Hours": "未来4小时",
|
||||||
|
"next12Hours": "未来12小时",
|
||||||
|
"next24Hours": "未来24小时",
|
||||||
|
"nextNDays": "未来{{n}}天",
|
||||||
|
"mayTrigger": "{{name}}可能触发",
|
||||||
|
"mayRecur": "{{name}}可能再次发生",
|
||||||
|
"daysToThreshold": "{{days}} 天后触及阈值",
|
||||||
|
"forecastSummary": "- {{resource}}:当前 {{current}}% → 7天后预测 {{predicted}}%(阈值 {{threshold}}%),{{risk}}",
|
||||||
|
"safetyBalancedExamples": "cat/grep/df 自动 · rm/reboot 确认"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user