UNPKG

grafana-mcp-analyzer

Version:

让AI助手直接分析你的Grafana监控数据 - A Model Context Protocol server for Grafana data analysis

20 lines (16 loc) 10.3 kB
import d from"fs";import p from"fs/promises";import f from"path";import z from"os";function y(){const h=process.env.MAX_CHUNK_SIZE;if(h){const n=Number(h);if(!isNaN(n)&&n>0)return n*1024}const t=process.env.DEFAULT_CHUNK_SIZE;if(t){const n=Number(t);if(!isNaN(n)&&n>0)return n*1024}return 50*1024}class x{constructor(t){this.maxChunkSize=t||y()}detectDataType(t){return t?.data?.results&&typeof t.data.results=="object"||t?.results&&typeof t.results=="object"?"grafana-query":t?.responses||t?.hits||t?.aggregations?"elasticsearch":t?.series&&Array.isArray(t.series)?"timeseries":t?.tables&&Array.isArray(t.tables)?"tables":Array.isArray(t)?"array":typeof t=="object"&&t!==null?"object":"unknown"}calculateSize(t){return Buffer.byteLength(JSON.stringify(t),"utf8")}isWithinLimit(t){return this.calculateSize(t)<=this.maxChunkSize}chunkElasticsearchData(t){const n=[];let e=1;const r=this.extractESMetadata(t);this.isWithinLimit(r)&&n.push({id:"metadata",index:e++,totalChunks:0,type:"metadata",contentType:"elasticsearch-metadata",content:r,size:this.calculateSize(r)});const a=t.responses||[t];for(let o=0;o<a.length;o++){const s=a[o];if(s.hits?.hits&&Array.isArray(s.hits.hits)){const i=this.chunkArray(s.hits.hits,`hits-${o}`,e,"elasticsearch-hits");n.push(...i),e+=i.length}if(s.aggregations){const i=this.chunkAggregations(s.aggregations,`aggregations-${o}`,e);n.push(...i),e+=i.length}}return n.forEach(o=>{o.totalChunks=n.length}),n}chunkGrafanaQueryData(t){const n=[];let e=1;const r=t?.data?.results||t?.results||{},a=Object.keys(r),o={queryCount:a.length,queryNames:a,timestamp:new Date().toISOString()};for(const s of a){const c=r[s]?.frames||[];for(let u=0;u<c.length;u++){const l=c[u],k=this.calculateSize(l);if(k<=this.maxChunkSize)n.push({id:`${s}-frame-${u}`,index:e++,totalChunks:0,type:"data",contentType:"frame-data",content:{metadata:o,frame:l},size:k});else{const g=this.chunkFrameOptimized(l,`${s}-frame-${u}`,e,o);n.push(...g),e+=g.length}}}return n.forEach(s=>{s.totalChunks=n.length}),n}chunkFrameOptimized(t,n,e,r){const a=[];let o=e;const s=t?.schema?.fields||t?.fields||[],i=t?.data||{},c={name:t?.schema?.name||t?.name||"unknown",fieldCount:s.length,fieldNames:s.map(k=>k.name),meta:t?.meta,queryMetadata:r},u={metadata:c,data:i},l=this.calculateSize(u);if(l<=this.maxChunkSize)a.push({id:`${n}-full`,index:o++,totalChunks:0,type:"data",contentType:"frame-full",content:u,size:l});else{const k=this.extractRowsFromFrame(t),g=this.chunkArray(k,`${n}-rows`,o,"frame-rows",{metadata:c,fieldNames:s.map(S=>S.name)});a.push(...g),o+=g.length}return a}extractRowsFromFrame(t){const n=t?.schema?.fields||t?.fields||[],r=(t?.data||{}).values||[];if(r.length===0)return[];const a=[],o=r[0].length;for(let s=0;s<o;s++){const i={};n.forEach((c,u)=>{i[c.name]=r[u]?.[s]}),a.push(i)}return a}chunkAggregations(t,n,e){const r=[];let a=e;for(const[o,s]of Object.entries(t))if(s&&typeof s=="object"&&"buckets"in s){const i=s.buckets;if(Array.isArray(i)){const c=this.chunkArray(i,`${n}-${o}`,a,"aggregation-buckets",{aggregationName:o,aggregationType:"bucket"});r.push(...c),a+=c.length}}else{const i={[o]:s};this.isWithinLimit(i)&&r.push({id:`${n}-${o}`,index:a++,totalChunks:0,type:"data",contentType:"aggregation-stats",content:i,size:this.calculateSize(i)})}return r}chunkArray(t,n,e,r,a){const o=[];let s=e,i=[];for(let c=0;c<t.length;c++){const u=t[c];if(this.calculateSize(u)>this.maxChunkSize){const m=this.handleOversizedItem(u,`${n}-item-${c}`,s);o.push(m),s++;continue}const k=[...i,u],g={id:`${n}-chunk-${o.length+1}`,index:s,totalChunks:0,type:"data",contentType:r,content:k,size:0,metadata:{...a,startIndex:c-k.length+1,endIndex:c}};if(this.calculateSize(g)>this.maxChunkSize&&i.length>0){const m={id:`${n}-chunk-${o.length+1}`,index:s++,totalChunks:0,type:"data",contentType:r,content:i,size:this.calculateSize({id:`${n}-chunk-${o.length+1}`,index:s-1,totalChunks:0,type:"data",contentType:r,content:i,size:0,metadata:{...a,startIndex:c-i.length,endIndex:c-1}}),metadata:{...a,startIndex:c-i.length,endIndex:c-1}};o.push(m),i=[u]}else i=k}if(i.length>0){const c={id:`${n}-chunk-${o.length+1}`,index:s++,totalChunks:0,type:"data",contentType:r,content:i,size:this.calculateSize({id:`${n}-chunk-${o.length+1}`,index:s-1,totalChunks:0,type:"data",contentType:r,content:i,size:0,metadata:{...a,startIndex:t.length-i.length,endIndex:t.length-1}}),metadata:{...a,startIndex:t.length-i.length,endIndex:t.length-1}};o.push(c)}return o}handleOversizedItem(t,n,e){const r=this.extractItemSummary(t);return{id:n,index:e,totalChunks:0,type:"oversize",contentType:"oversized-item",content:{originalSize:this.calculateSize(t),summary:r,note:"\u9879\u76EE\u8FC7\u5927\uFF0C\u5DF2\u63D0\u53D6\u5173\u952E\u4FE1\u606F"},size:this.calculateSize(r)}}extractItemSummary(t){if(typeof t=="object"&&t!==null){const n=Object.keys(t),e={},r=["key","key_as_string","doc_count","value","timestamp","name"];for(const a of r)a in t&&(e[a]=t[a]);if(Object.keys(e).length<3)for(const a of n.slice(0,5))a in e||(e[a]=t[a]);return e}return t}extractESMetadata(t){const n=t.responses||[t],e=n[0];return{responseCount:n.length,totalHits:e?.hits?.total?.value||e?.hits?.total||0,maxScore:e?.hits?.max_score,took:e?.took,timedOut:e?.timed_out,aggregationCount:e?.aggregations?Object.keys(e.aggregations).length:0,aggregationNames:e?.aggregations?Object.keys(e.aggregations):[],timestamp:new Date().toISOString()}}chunk(t){const n=this.detectDataType(t);let e=[];switch(n){case"elasticsearch":e=this.chunkElasticsearchData(t);break;case"grafana-query":e=this.chunkGrafanaQueryData(t);break;case"timeseries":e=this.chunkArray(t.series||[],"timeseries",1,"timeseries-data");break;case"tables":e=this.chunkTableData(t);break;case"array":e=this.chunkArray(t,"array",1,"array-data");break;default:e=this.chunkGenericData(t);break}return e.forEach(r=>{r.totalChunks=e.length}),{chunks:e,metadata:{totalSize:this.calculateSize(t),totalChunks:e.length,dataType:n,chunkingStrategy:"strict-chunking"}}}chunkTableData(t){const n=[];let e=1;const r=t.tables||[];for(let a=0;a<r.length;a++){const o=r[a],s=o.rows||[],i=o.columns||[],c={tableIndex:a,rowCount:s.length,columnCount:i.length,columns:i.map(l=>l.text||l.name)};this.isWithinLimit(c)&&n.push({id:`table-${a}-metadata`,index:e++,totalChunks:0,type:"metadata",contentType:"table-metadata",content:c,size:this.calculateSize(c)});const u=this.chunkArray(s,`table-${a}-rows`,e,"table-rows");n.push(...u),e+=u.length}return n}chunkGenericData(t){const n=[];let e=1;if(typeof t=="object"&&t!==null){const r=Object.entries(t);let a={},o=0;for(const[s,i]of r){const c=this.calculateSize({[s]:i});if(c>this.maxChunkSize){n.push({id:`generic-oversized-${s}`,index:e++,totalChunks:0,type:"oversize",contentType:"oversized-field",content:{fieldName:s,fieldType:typeof i,summary:this.extractItemSummary(i)},size:this.calculateSize({fieldName:s,summary:this.extractItemSummary(i)})});continue}o+c>this.maxChunkSize&&Object.keys(a).length>0&&(n.push({id:`generic-chunk-${n.length+1}`,index:e++,totalChunks:0,type:"data",contentType:"generic-object",content:a,size:o}),a={},o=0),a[s]=i,o+=c}Object.keys(a).length>0&&n.push({id:`generic-chunk-${n.length+1}`,index:e++,totalChunks:0,type:"data",contentType:"generic-object",content:a,size:o})}else{const r={value:t};n.push({id:"generic-single",index:1,totalChunks:1,type:"data",contentType:"generic-single",content:r,size:this.calculateSize(r)})}return n}}function C(h){return new x(h)}async function w(h,t,n){const r=C(n).chunk(h),a=process.env.DATA_STORE_ROOT||f.join(z.homedir(),".grafana-mcp-analyzer","data-store"),o=f.join(a,t),s=f.join(o,"data");d.existsSync(s)||d.mkdirSync(s,{recursive:!0});const i=f.join(s,"chunking-metadata.json");await d.promises.writeFile(i,JSON.stringify({totalChunks:r.chunks.length,metadata:r.metadata,guidance:b(r.chunks),chunkFiles:r.chunks.map((c,u)=>`chunk-${u+1}.json`)},null,2));for(let c=0;c<r.chunks.length;c++){const u=r.chunks[c],l=f.join(s,`chunk-${c+1}.json`);await d.promises.writeFile(l,JSON.stringify(u,null,2))}return r}async function A(h){const t=process.env.DATA_STORE_ROOT||f.join(z.homedir(),".grafana-mcp-analyzer","data-store"),n=f.join(t,h),e=f.join(n,"data"),r=f.join(e,"chunking-metadata.json");try{const a=JSON.parse(await p.readFile(r,"utf8")),o=[],s=d.readdirSync(e).filter(i=>i.startsWith("chunk-")&&i.endsWith(".json"));for(const i of s.sort()){const c=f.join(e,i),u=JSON.parse(await p.readFile(c,"utf8"));o.push(u)}return{chunks:o,metadata:a}}catch{const o=f.join(e,"full.json");try{const s=JSON.parse(await p.readFile(o,"utf8")),i=[{id:"full-data",index:1,totalChunks:1,type:"data",contentType:"full-data",content:s,size:JSON.stringify(s).length}],c={totalSize:JSON.stringify(s).length,totalChunks:1,dataType:"full-data",chunkingStrategy:"full"};return{chunks:i,metadata:c}}catch{throw new Error(`\u65E0\u6CD5\u627E\u5230\u8BF7\u6C42ID ${h} \u7684\u5206\u5757\u6216\u5B8C\u6574\u6570\u636E`)}}}function D(h,t){const n=t||y();for(const e of h)if(e.size>n)return console.error(`\u5206\u5757 ${e.index} \u8D85\u8FC7\u5927\u5C0F\u9650\u5236: ${e.size} > ${n}`),!1;return!0}function b(h){if(h.length===0)return"\u6CA1\u6709\u6570\u636E\u9700\u8981\u5206\u5757";const t=h[0].metadata;return` ## \u6570\u636E\u5206\u5757\u4FE1\u606F - **\u6570\u636E\u7C7B\u578B**: ${t?.dataType||"unknown"} - **\u603B\u5757\u6570**: ${h.length} - **\u603B\u5927\u5C0F**: ${Math.round(t?.totalSize/1024)}KB - **\u5206\u5757\u5927\u5C0F**: ${Math.round(y()/1024)}KB ## \u5206\u6790\u6307\u5BFC 1. **\u8BFB\u53D6\u5143\u6570\u636E**: \u4ECE\u7B2C1\u5757\u83B7\u53D6\u6570\u636E\u6982\u89C8 2. **\u987A\u5E8F\u8BFB\u53D6**: \u6309\u5757\u53F7\u987A\u5E8F\u8BFB\u53D6\u6240\u6709\u6570\u636E\u5757 3. **\u9010\u6B65\u5206\u6790**: \u6BCF\u5757\u5206\u6790\u540E\u7D2F\u79EF\u7ED3\u679C 4. **\u6574\u5408\u7ED3\u8BBA**: \u57FA\u4E8E\u6240\u6709\u5757\u7684\u5206\u6790\u7ED3\u679C\u751F\u6210\u6700\u7EC8\u62A5\u544A ## \u5206\u5757\u8BE6\u60C5 ${h.map(e=>`- \u5757${e.index}: ${e.type} (${Math.round(e.size/1024)}KB) ${e.contentType}`).join(` `)} **\u91CD\u8981**: \u8BF7\u6309\u987A\u5E8F\u8BFB\u53D6\u6240\u6709\u5206\u5757\uFF0C\u786E\u4FDD\u5206\u6790\u5B8C\u6574\u6027\u3002 `}export{x as StrictChunker,w as chunkAndSave,C as createStrictChunker,b as generateChunkingGuidance,y as getMaxChunkSize,A as loadChunks,D as validateChunks};