"use strict"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Type } from "typebox"; import { StringEnum } from "@earendil-works/pi-ai"; import { getConfig } from "../config"; import { processImage } from "../media"; import { visionCompletion } from "../client"; import { DATA_VIZ_ANALYSIS } from "../prompts"; import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, } from "@earendil-works/pi-coding-agent"; export function registerAnalyzeDataVizTool(pi: ExtensionAPI) { pi.registerTool({ name: "analyze_data_visualization", label: "Analyze Data Visualization", description: "Analyze charts, graphs, dashboards, and data visualizations. Extracts key metrics, identifies trends, anomalies, and patterns, and provides actionable recommendations. Supports local files (png, jpg) and HTTP(S) URLs.", promptSnippet: "Analyze charts/dashboards — extract metrics, trends, anomalies, recommendations", promptGuidelines: [ "Use analyze_data_visualization when the user provides a chart, graph, dashboard, or data visualization and wants insights. Use the optional analysisFocus parameter ('trends', 'anomalies', 'comparison', 'summary', 'forecast') to guide the analysis angle.", ], parameters: Type.Object({ imagePath: Type.String({ description: "Path to the data visualization image (relative to cwd, supports @ prefix) or HTTP(S) URL.", }), prompt: Type.Optional( Type.String({ description: "What should the AI focus on? Defaults to a comprehensive analysis.", }) ), analysisFocus: Type.Optional( StringEnum([ "trends", "anomalies", "comparison", "summary", "forecast", ] as const) ), }), async execute(_toolCallId, params, signal, onUpdate, ctx) { const config = getConfig(); onUpdate?.({ content: [{ type: "text", text: "正在调用 GLM-4.6V 分析数据可视化..." }], }); const image = processImage(params.imagePath, ctx.cwd, config.maxImageSizeMB); let userPrompt = params.prompt || "Analyze this data visualization and extract key metrics, trends, anomalies, and actionable recommendations."; if (params.analysisFocus) { userPrompt = `Analysis focus: ${params.analysisFocus}\n\n${userPrompt}`; } const result = await visionCompletion( config, DATA_VIZ_ANALYSIS, [image], userPrompt, signal ); const truncated = truncateHead(result, { maxLines: DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES, }); let text = truncated.content; if (truncated.truncated) { text += `\n\n[输出已截断: ${truncated.outputLines}/${truncated.totalLines} 行]`; } return { content: [{ type: "text", text }], details: { raw: result }, }; }, }); }