diff --git a/BACKEND/app/services/line_connection.ts b/BACKEND/app/services/line_connection.ts index cb91eb1..08ec574 100644 --- a/BACKEND/app/services/line_connection.ts +++ b/BACKEND/app/services/line_connection.ts @@ -494,7 +494,7 @@ export default class LineConnection { const data = textfsmResults(logScenarios, '') let pid = this.config.inventory?.pid || '' try { - data.forEach((item) => { + for (const item of data) { if (item?.textfsm && isValidJson(item?.textfsm)) { if ( ['show inventory', 'sh inventory', 'show inv', 'sh inv'].includes(item.command) @@ -521,9 +521,16 @@ export default class LineConnection { ? { ...this.config.inventory, ...dataVer } : dataVer } + if ( + item.command?.trim()?.includes('show env') || + item.command?.trim()?.includes('sh env') + ) { + const dataEnv = await this.detectShowEnvWithAI(item.output) + item.dataAI = dataEnv + } item.textfsm = JSON.parse(item.textfsm) } - }) + } const scenario = await detectScenarioByModel(pid, this.listScenarios) console.log(pid, scenario?.title, this.listScenarios) if ( @@ -1522,4 +1529,66 @@ export default class LineConnection { await this.runScript(script as any, userName) await this.sendEmailLoadLicense(licenseFileName, startTime) } + + /** + * Detect log by call api gpt, return string[] + */ + async detectShowEnvWithAI(log: string) { + try { + const payload = { + model: 'gpt-4o-mini', + max_tokens: 1000, + messages: [ + { + role: 'user', + content: `You are a network log parser. + +Input is the raw output of Cisco "show environment" or "show environment all". + +Your task: +- Focus ONLY on FAN and POWER related information. +- Ignore TEMPERATURE, VOLTAGE, and other sensors unless they relate to FAN or POWER. +- Extract each FAN or POWER component and its state. +- Normalize each item into the format: + + ": " + +Examples: +- "FAN is OK" -> "FAN: OK" +- "FAN 2 is FAILED" -> "FAN 2: FAILED" +- "POWER SUPPLY A is NOT PRESENT" -> "POWER SUPPLY A: NOT PRESENT" +- "PSU 1 Absent" -> "PSU 1: ABSENT" + +Output requirements: +- Return ONLY a valid JSON array of strings. +- Do NOT include any explanation or extra text. +- Do NOT include code block. +- JSON must be directly parsable. + +Here is the input log: + +${log} +`, + }, + ], + } + const remoteUrl = process.env.ERP_URL || 'https://stage.nswteam.net' + const remoteResp = await axios.post( + remoteUrl + '/api/transferPostData', + { + urlAPI: '/api/open-ai-sfp/model-image-info', + data: payload, + }, + { + headers: { + Authorization: 'Bearer ' + process.env.ERP_TOKEN, + }, + } + ) + return remoteResp.data?.Status === 'OK' ? remoteResp.data?.data : '' + } catch (error: any) { + console.log('[ERROR] Detect log show env from AI', error) + } + return '' + } } diff --git a/BACKEND/app/ultils/templates/index.ts b/BACKEND/app/ultils/templates/index.ts index 6623e84..d44b1ac 100644 --- a/BACKEND/app/ultils/templates/index.ts +++ b/BACKEND/app/ultils/templates/index.ts @@ -56,6 +56,7 @@ export const textfsmResults = (logContent: string, cmd: string) => { command: cmd, output: logContent, textfsm: JSON.stringify(structuredOutput).replace(/[\x00-\x1f\x7f-\x9f]/g, ''), + dataAI: [], }, ] } else { @@ -101,6 +102,7 @@ export const textfsmResults = (logContent: string, cmd: string) => { command, output, textfsm: JSON.stringify(structuredOutput).replace(/[\x00-\x1f\x7f-\x9f]/g, ''), // Clean special characters + dataAI: [], } }) .filter((el) => el.command) diff --git a/BACKEND/app/ultils/templates/show_env.ts b/BACKEND/app/ultils/templates/show_env.ts index b284a91..44d8581 100644 --- a/BACKEND/app/ultils/templates/show_env.ts +++ b/BACKEND/app/ultils/templates/show_env.ts @@ -42,14 +42,15 @@ const parseShowEnvironment = (data: string) => { for (const p of patterns) { const m = XRegExp.exec(line, p) if (m?.groups) { - const record: any = { - TYPE: m.groups.TYPE?.trim() || '', - NAME: m.groups.NAME?.trim() || '', - LOCATION: m.groups.LOCATION?.trim() || '', - STATE: m.groups.STATE?.trim() || '', - VALUE: m.groups.VALUE?.trim() || '', - RAW: line, - } + // const record: any = { + // TYPE: m.groups.TYPE?.trim() || '', + // NAME: m.groups.NAME?.trim() || '', + // LOCATION: m.groups.LOCATION?.trim() || '', + // STATE: m.groups.STATE?.trim() || '', + // VALUE: m.groups.VALUE?.trim() || '', + // RAW: line, + // } + const record: any = line // Lưu nguyên dòng records.push(record) break diff --git a/FRONTEND/src/components/Modal/ModalTerminal.tsx b/FRONTEND/src/components/Modal/ModalTerminal.tsx index c14c49a..d812bfd 100644 --- a/FRONTEND/src/components/Modal/ModalTerminal.tsx +++ b/FRONTEND/src/components/Modal/ModalTerminal.tsx @@ -479,8 +479,11 @@ const ModalTerminal = ({ d.command?.trim()?.includes("show env") || d.command?.trim()?.includes("sh env") ); - console.log("showEnv", showEnv); - return showEnv?.textfsm && Array.isArray(showEnv?.textfsm) + return showEnv?.dataAI && + Array.isArray(showEnv?.dataAI) && + showEnv?.dataAI?.length > 0 + ? showEnv?.dataAI + : showEnv?.textfsm && Array.isArray(showEnv?.textfsm) ? showEnv?.textfsm : null; }; @@ -823,7 +826,7 @@ const ModalTerminal = ({ {findDataShowEnv() ? findDataShowEnv()?.map((v: TextTSMEnvironment, i) => ( - - {v.RAW} + - {v} )) : ""} diff --git a/FRONTEND/src/untils/types.ts b/FRONTEND/src/untils/types.ts index 59e88ed..3b7b412 100644 --- a/FRONTEND/src/untils/types.ts +++ b/FRONTEND/src/untils/types.ts @@ -223,6 +223,7 @@ export type TextFSM = { command: string; output: string; textfsm: any; + dataAI: any; }; export type TDataTicket = { @@ -255,14 +256,16 @@ export type TextTSMLicense = { LICENSE_PRIORITY: string; }; -export type TextTSMEnvironment = { - TYPE: string; - NAME: string; - LOCATION: string; - STATE: string; - VALUE: string; - RAW: string; -}; +// export type TextTSMEnvironment = { +// TYPE: string; +// NAME: string; +// LOCATION: string; +// STATE: string; +// VALUE: string; +// RAW: string; +// }; + +export type TextTSMEnvironment = string; export type TBrands = { id: number;