Skip to main content
Glama
scriptResultFormatter.ts5.69 kB
/** * Script Result Formatter * * Formats Python script execution results with token optimization. * Used by EXECUTE_PYTHON_SCRIPT tool. */ import { DEFAULT_PRESENTER_FORMAT, type PresenterFormat, presentStructuredData, } from "./presenter.js"; import type { FormatterOptions } from "./responseFormatter.js"; import { finalizeFormattedText, mergeFormatterOptions, } from "./responseFormatter.js"; /** * Script execution result structure */ export interface ScriptResultData { success?: boolean; data?: { result?: unknown; output?: string; error?: string; }; [key: string]: unknown; } interface ScriptSummaryContext { snippet: string; resultType: string; resultPreview: string; hasOutput: boolean; outputType: string; outputPreview?: string; } /** * Format script execution result */ export function formatScriptResult( data: ScriptResultData | undefined, scriptSnippet?: string, options?: FormatterOptions, ): string { const opts = mergeFormatterOptions(options); if (!data) { return "No result returned."; } const success = data.success ?? true; const result = data.data?.result; const output = data.data?.output; const error = data.data?.error; // Error case - always show full details if (!success || error) { return formatError(error, scriptSnippet); } if (opts.detailLevel === "detailed") { return formatDetailed(data, opts.responseFormat); } let formattedText = ""; let context: ScriptSummaryContext | undefined; switch (opts.detailLevel) { case "minimal": formattedText = formatMinimal(result); context = buildScriptContext(scriptSnippet, result, output); break; case "summary": { const summary = formatSummary(result, output, scriptSnippet); formattedText = summary.text; context = summary.context; break; } } const ctx = context as unknown as Record<string, unknown> | undefined; return finalizeFormattedText(formattedText, opts, { context: ctx, structured: ctx, template: "scriptSummary", }); } /** * Format error result */ function formatError(error: unknown, scriptSnippet?: string): string { const errorMsg = typeof error === "string" ? error : JSON.stringify(error); const snippet = scriptSnippet ? `\nScript: ${truncateScript(scriptSnippet)}` : ""; return `❌ Script execution failed:${snippet}\n\nError: ${errorMsg}`; } /** * Minimal mode: Just the result value */ function formatMinimal(result: unknown): string { if (result === undefined || result === null) { return "✓ Script executed successfully (no return value)"; } if ( typeof result === "string" || typeof result === "number" || typeof result === "boolean" ) { return `✓ Result: ${result}`; } if (Array.isArray(result)) { return `✓ Result: Array[${result.length}]`; } if (typeof result === "object") { const keys = Object.keys(result); return `✓ Result: Object{${keys.length} keys}`; } return `✓ Result: ${String(result)}`; } /** * Summary mode: Result with context */ function formatSummary( result: unknown, output?: string, scriptSnippet?: string, ): { text: string; context: ScriptSummaryContext } { let formatted = "✓ Script executed successfully\n\n"; const snippet = scriptSnippet ? truncateScript(scriptSnippet) : ""; if (snippet) { formatted += `Script: ${snippet}\n\n`; } let resultPreview = "(none)"; if (result !== undefined && result !== null) { resultPreview = formatResultValue(result, 500); formatted += `Result: ${resultPreview}\n`; } let outputPreview: string | undefined; if (output?.trim()) { outputPreview = output.length > 200 ? `${output.substring(0, 200)}...` : output; formatted += `\nOutput:\n${outputPreview}`; } return { context: { hasOutput: Boolean(outputPreview), outputPreview, outputType: getValueType(output), resultPreview, resultType: getValueType(result), snippet, }, text: formatted, }; } function buildScriptContext( scriptSnippet: string | undefined, result: unknown, output?: string, ): ScriptSummaryContext { return { hasOutput: Boolean(output?.trim()), outputPreview: output, outputType: getValueType(output), resultPreview: formatResultValue(result ?? "", 200), resultType: getValueType(result), snippet: scriptSnippet ? truncateScript(scriptSnippet) : "", }; } /** * Detailed mode: Full JSON */ function formatDetailed( data: ScriptResultData, format: PresenterFormat | undefined, ): string { const title = "Script Result"; const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT; return presentStructuredData( { context: { payloadFormat, title, }, detailLevel: "detailed", structured: data, template: "detailedPayload", text: title, }, payloadFormat, ); } function getValueType(value: unknown): string { if (value === undefined || value === null) { return "none"; } if (Array.isArray(value)) { return `array(${value.length})`; } return typeof value; } /** * Format result value with size limit */ function formatResultValue(value: unknown, maxChars: number): string { const str = typeof value === "object" ? JSON.stringify(value, null, 2) : String(value); if (str.length <= maxChars) { return str; } return `${str.substring(0, maxChars)}...\n💡 Result truncated. Use detailLevel='detailed' for full output.`; } /** * Truncate script for display */ function truncateScript(script: string, maxLength = 100): string { const trimmed = script.trim(); if (trimmed.length <= maxLength) { return trimmed; } const firstLine = trimmed.split("\n")[0]; if (firstLine.length <= maxLength) { return `${firstLine}...`; } return `${trimmed.substring(0, maxLength)}...`; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/8beeeaaat/touchdesigner-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server