diff --git a/backend/controller/log_proxy.go b/backend/controller/log_proxy.go index 04249c2..2449397 100644 --- a/backend/controller/log_proxy.go +++ b/backend/controller/log_proxy.go @@ -22,7 +22,7 @@ type LogProxyHandler struct { ds *LogDSHandler } -const maxLogQueryLimit = 500 // 单次查询上限,防止平台承载过大数据 // Logan.Li +const maxLogQueryLimit = 500 func NewLogProxyHandler(ds *LogDSHandler) *LogProxyHandler { return &LogProxyHandler{ds: ds} @@ -149,19 +149,36 @@ func (h *LogProxyHandler) QueryES(c *gin.Context) { }, } if req.Start != "" || req.End != "" { - rangeQ := map[string]interface{}{"range": map[string]interface{}{"@timestamp": map[string]string{}}} - r := rangeQ["range"].(map[string]interface{})["@timestamp"].(map[string]string) - if req.Start != "" { - r["gte"] = req.Start + // ES 7.2 date_time 格式要求 +00:00 而非 Z;兼容 @timestamp 与 timestamp // Logan.Li + buildRangeVal := func(v string) string { + return strings.Replace(v, "Z", "+00:00", 1) } - if req.End != "" { - r["lte"] = req.End + rangeCond := map[string]interface{}{ + "bool": map[string]interface{}{ + "should": []interface{}{ + map[string]interface{}{"range": map[string]interface{}{"@timestamp": map[string]string{}}}, + map[string]interface{}{"range": map[string]interface{}{"timestamp": map[string]string{}}}, + }, + "minimum_should_match": 1, + }, + } + for _, clause := range rangeCond["bool"].(map[string]interface{})["should"].([]interface{}) { + rng := clause.(map[string]interface{})["range"].(map[string]interface{}) + for _, v := range rng { + r := v.(map[string]string) + if req.Start != "" { + r["gte"] = buildRangeVal(req.Start) + } + if req.End != "" { + r["lte"] = buildRangeVal(req.End) + } + } } searchBody["query"] = map[string]interface{}{ "bool": map[string]interface{}{ "must": []interface{}{ searchBody["query"], - rangeQ, + rangeCond, }, }, } @@ -200,6 +217,8 @@ func parseLogTime(s string) (time.Time, error) { } return time.Unix(n, 0), nil } + // ES 7.2 date_time 不接受 Z,转 +00:00 + s = strings.Replace(s, "Z", "+00:00", 1) return time.Parse(time.RFC3339, s) } @@ -287,4 +306,4 @@ func pickESMessage(src map[string]interface{}) string { return string(b[:500]) + "..." } return string(b) -} \ No newline at end of file +} diff --git a/frontend/src/components/aiops/AIOpsAssistant.tsx b/frontend/src/components/aiops/AIOpsAssistant.tsx index 891666d..1f44a33 100644 --- a/frontend/src/components/aiops/AIOpsAssistant.tsx +++ b/frontend/src/components/aiops/AIOpsAssistant.tsx @@ -6,23 +6,6 @@ import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { Label } from '@/components/ui/label'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import { Switch } from '@/components/ui/switch'; import { toast } from 'sonner'; import { aiApi, alertApi, agentApi, dashboardStatsApi, type LLMConfigData, type ChatHistoryItem } from '@/api'; import { @@ -40,7 +23,6 @@ import { Brain, Loader2, CheckCircle2, - Wifi, Square, } from 'lucide-react'; import type { JsonData } from '@/types/common'; @@ -154,9 +136,6 @@ export function AIOpsAssistant() { const abortRef = useRef(null); // 组件卸载时中止进行中的流式请求,避免 setState on unmounted // Logan.Li useEffect(() => () => abortRef.current?.abort(), []); - const [isConfigOpen, setIsConfigOpen] = useState(false); - const [saving, setSaving] = useState(false); - const [testing, setTesting] = useState(false); const [config, setConfig] = useState(defaultConfig); const [configReady, setConfigReady] = useState(false); // 已配置且启用 const [contextLoading, setContextLoading] = useState(null); // 快捷操作拉取平台数据中 @@ -209,50 +188,6 @@ export function AIOpsAssistant() { loadHistory(); }, []); - // 切换提供商时自动填充默认 endpoint + 模型 // Logan.Li - const handleProviderChange = (provider: string) => { - const p = LLM_PROVIDERS.find((x) => x.id === provider); - setConfig((prev) => ({ - ...prev, - provider, - endpoint: p?.defaultEndpoint || prev.endpoint, - model: p?.models[0] || prev.model, - })); - }; - - // 保存配置 // Logan.Li - const handleSaveConfig = async () => { - if (config.enabled && !config.api_key && config.provider !== 'ollama') { - toast.error('请填写 API Key'); - return; - } - setSaving(true); - try { - const saved = await aiApi.saveConfig(config); - setConfig(saved); - setConfigReady(!!(saved.enabled && (saved as JsonData).configured)); - toast.success('配置已保存'); - setIsConfigOpen(false); - } catch (e) { - toast.error(e instanceof Error ? e.message : '保存失败'); - } finally { - setSaving(false); - } - }; - - // 测试连接 // Logan.Li - const handleTest = async () => { - setTesting(true); - try { - await aiApi.testConnection(); - toast.success('LLM 服务连接成功'); - } catch (e) { - toast.error(e instanceof Error ? e.message : '连接失败'); - } finally { - setTesting(false); - } - }; - // 发送一条消息的核心逻辑(流式输出,逐步填充助手气泡) // Logan.Li const sendMessage = async (text: string) => { const userMsg: Message = { @@ -367,7 +302,6 @@ export function AIOpsAssistant() { toast.success('已开始新对话'); }; - const currentProvider = LLM_PROVIDERS.find((p) => p.id === config.provider); return (
@@ -383,12 +317,21 @@ export function AIOpsAssistant() {
- {configReady && ( + {configReady ? ( {config.model} - )} + ) : null} +
@@ -564,131 +507,6 @@ export function AIOpsAssistant() { - {/* LLM Config Dialog */} - - - - 大模型配置 - - 配置 AI 助手使用的大语言模型(支持 OpenAI 兼容接口) - - -
- {/* 启用开关 */} -
-
-

启用 AI 助手

-

关闭后将无法进行对话

-
- setConfig({ ...config, enabled: v })} /> -
- -
-
- - -
-
- - {currentProvider && currentProvider.models.length > 0 ? ( - - ) : ( - setConfig({ ...config, model: e.target.value })} - placeholder="输入模型名称" - className="bg-slate-800 border-slate-700 text-slate-100 mt-1" - /> - )} -
-
-
- - setConfig({ ...config, api_key: e.target.value })} - placeholder="sk-..." - className="bg-slate-800 border-slate-700 text-slate-100 mt-1" - /> -
-
- - setConfig({ ...config, endpoint: e.target.value })} - placeholder="https://api.openai.com/v1" - className="bg-slate-800 border-slate-700 text-slate-100 mt-1 font-mono text-sm" - /> -

基础地址(不含 /chat/completions),OpenAI 兼容格式

-
-
- -