diff --git a/app/api/proxy/route.ts b/app/api/proxy/route.ts index 05704c7d5b..388d695cf9 100644 --- a/app/api/proxy/route.ts +++ b/app/api/proxy/route.ts @@ -16,13 +16,42 @@ export async function POST(request: Request) { throw new Error(`Tool not found: ${toolId}`) } - // Use executeTool with skipProxy=true to prevent recursive proxy calls - const result = await executeTool(toolId, params, true) + // Use executeTool with skipProxy=true to prevent recursive proxy calls, and skipPostProcess=true to prevent duplicate post-processing + const result = await executeTool(toolId, params, true, true) if (!result.success) { - throw new Error( - tool.transformError ? tool.transformError(result) : 'Tool returned an error' - ) + if (tool.transformError) { + try { + const errorResult = tool.transformError(result) + + // Handle both string and Promise return types + if (typeof errorResult === 'string') { + throw new Error(errorResult) + } else { + // It's a Promise, await it + const transformedError = await errorResult + // If it's a string or has an error property, use it + if (typeof transformedError === 'string') { + throw new Error(transformedError) + } else if ( + transformedError && + typeof transformedError === 'object' && + 'error' in transformedError + ) { + throw new Error(transformedError.error || 'Tool returned an error') + } + // Fallback + throw new Error('Tool returned an error') + } + } catch (e) { + if (e instanceof Error) { + throw e + } + throw new Error('Tool returned an error') + } + } else { + throw new Error('Tool returned an error') + } } return NextResponse.json(result) diff --git a/executor/types.ts b/executor/types.ts index 3cfcc9f936..1e40f1970b 100644 --- a/executor/types.ts +++ b/executor/types.ts @@ -164,7 +164,13 @@ export interface Tool

> { error?: string }> - transformError?: (error: any) => string // Function to format error messages + transformError?: (error: any) => + | string + | Promise<{ + success: boolean + output: O + error?: string + }> // Function to format error messages } /** diff --git a/tools/docs/create.ts b/tools/docs/create.ts index 1c9b0c76e1..6402faf4b0 100644 --- a/tools/docs/create.ts +++ b/tools/docs/create.ts @@ -50,6 +50,44 @@ export const createTool: ToolConfig { + // Only add content if it was provided and not already added during creation + // The Google Docs API doesn't directly support content in the create request, + // so we need to add it separately via the write tool + if (result.success && params.content) { + console.log('Google Docs create - Post-processing: Adding content to document') + const documentId = result.output.metadata.documentId + + if (documentId) { + try { + const writeParams = { + accessToken: params.accessToken, + documentId: documentId, + content: params.content, + } + console.log('Content to add:', params.content) + + // Use the write tool to add content + const writeResult = await executeTool('google_docs_write', writeParams) + + if (!writeResult.success) { + console.warn( + 'Failed to add content to document, but document was created:', + writeResult.error + ) + } else { + console.log('Google Docs create - Content added successfully') + } + } catch (error) { + console.warn('Error adding content to document:', error) + // Don't fail the overall operation if adding content fails + } + } + } + + // Return the original result regardless of post-processing outcome + return result + }, transformResponse: async (response: Response) => { if (!response.ok) { let errorText = '' @@ -67,7 +105,9 @@ export const createTool: ToolConfig { - if (typeof error === 'object' && error !== null) { - if (error.message) { - return error.message - } - return ( - JSON.stringify(error, null, 2) || 'An error occurred while creating Google Docs document' - ) - } + transformError: async (error) => { + console.error('Google Docs create - Transform error:', error) - return error.toString() || 'An error occurred while creating Google Docs document' + const errorMessage = + typeof error === 'object' && error !== null + ? error.message || JSON.stringify(error, null, 2) + : error.toString() || 'An error occurred while creating Google Docs document' + + return { + success: false, + output: { + metadata: { + documentId: '', + title: '', + }, + }, + error: errorMessage, + } }, } diff --git a/tools/docs/read.ts b/tools/docs/read.ts index 521eafca53..2912bf28ab 100644 --- a/tools/docs/read.ts +++ b/tools/docs/read.ts @@ -70,11 +70,23 @@ export const readTool: ToolConfig }, } }, - transformError: (error) => { - if (typeof error === 'object' && error !== null) { - return JSON.stringify(error) || 'An error occurred while reading from Google Docs' + transformError: async (error) => { + const errorMessage = + typeof error === 'object' && error !== null + ? error.message || JSON.stringify(error, null, 2) + : error.toString() || 'An error occurred while reading Google Docs document' + + return { + success: false, + output: { + content: '', + metadata: { + documentId: '', + title: '', + }, + }, + error: errorMessage, } - return error.message || 'An error occurred while reading from Google Docs' }, } diff --git a/tools/docs/write.ts b/tools/docs/write.ts index 6900e0dae0..852634f5a7 100644 --- a/tools/docs/write.ts +++ b/tools/docs/write.ts @@ -48,14 +48,13 @@ export const writeTool: ToolConfig { - if (typeof error === 'object' && error !== null) { - if (error.message) { - return error.message - } - return JSON.stringify(error, null, 2) || 'An error occurred while writing to Google Docs' - } + transformError: async (error) => { + const errorMessage = + typeof error === 'object' && error !== null + ? error.message || JSON.stringify(error, null, 2) + : error.toString() || 'An error occurred while writing to Google Docs' - return error.toString() || 'An error occurred while writing to Google Docs' + return { + success: false, + output: { + updatedContent: false, + metadata: { + documentId: '', + title: '', + }, + }, + error: errorMessage, + } }, } diff --git a/tools/index.ts b/tools/index.ts index 8d375bf40f..2761a43e35 100644 --- a/tools/index.ts +++ b/tools/index.ts @@ -279,7 +279,7 @@ function getCustomTool(customToolId: string): ToolConfig | undefined { error: undefined, } }, - transformError: (error: any) => + transformError: async (error: any) => `Custom tool execution error: ${error.message || 'Unknown error'}`, } } @@ -288,7 +288,8 @@ function getCustomTool(customToolId: string): ToolConfig | undefined { export async function executeTool( toolId: string, params: Record, - skipProxy = false + skipProxy = false, + skipPostProcess = false ): Promise { try { const tool = getTool(toolId) @@ -313,11 +314,36 @@ export async function executeTool( // For internal routes or when skipProxy is true, call the API directly if (tool.request.isInternalRoute || skipProxy) { const result = await handleInternalRequest(toolId, tool, params) + + // Apply post-processing if available and not skipped + if (tool.postProcess && result.success && !skipPostProcess) { + try { + return await tool.postProcess(result, params, executeTool) + } catch (error) { + console.error(`Error in post-processing for tool ${toolId}:`, error) + // Return original result if post-processing fails + return result + } + } + return result } // For external APIs, use the proxy - return await handleProxyRequest(toolId, params) + const result = await handleProxyRequest(toolId, params) + + // Apply post-processing if available and not skipped + if (tool.postProcess && result.success && !skipPostProcess) { + try { + return await tool.postProcess(result, params, executeTool) + } catch (error) { + console.error(`Error in post-processing for tool ${toolId}:`, error) + // Return original result if post-processing fails + return result + } + } + + return result } catch (error: any) { console.error(`Error executing tool ${toolId}:`, error) @@ -396,12 +422,58 @@ async function handleInternalRequest( error: undefined, } } catch (error: any) { + console.error(`Error executing internal tool ${toolId}:`, error) + // Use the tool's error transformer if available if (tool.transformError) { - return { - success: false, - output: {}, - error: tool.transformError(error), + try { + const errorResult = tool.transformError(error) + + // Handle both string and Promise return types + if (typeof errorResult === 'string') { + return { + success: false, + output: {}, + error: errorResult, + } + } else { + // It's a Promise, await it + const transformedError = await errorResult + // If it's a string or has an error property, use it + if (typeof transformedError === 'string') { + return { + success: false, + output: {}, + error: transformedError, + } + } else if (transformedError && typeof transformedError === 'object') { + // If it's already a ToolResponse, return it directly + if ('success' in transformedError) { + return transformedError + } + // If it has an error property, use it + if ('error' in transformedError) { + return { + success: false, + output: {}, + error: transformedError.error, + } + } + } + // Fallback + return { + success: false, + output: {}, + error: 'Unknown error', + } + } + } catch (transformError) { + console.error(`Error transforming error for tool ${toolId}:`, transformError) + return { + success: false, + output: {}, + error: error.message || 'Unknown error', + } } } diff --git a/tools/types.ts b/tools/types.ts index b12f753d72..fdbb145541 100644 --- a/tools/types.ts +++ b/tools/types.ts @@ -14,7 +14,7 @@ export interface OAuthConfig { additionalScopes?: string[] // Additional scopes required for the tool } -export interface ToolConfig

{ +export interface ToolConfig

{ // Basic tool identification id: string name: string @@ -48,9 +48,16 @@ export interface ToolConfig

{ // Direct execution in browser (optional) - bypasses HTTP request directExecution?: (params: P) => Promise + // Post-processing (optional) - allows additional processing after the initial request + postProcess?: ( + result: R extends ToolResponse ? R : ToolResponse, + params: P, + executeTool: (toolId: string, params: Record) => Promise + ) => Promise + // Response handling - transformResponse: (response: Response) => Promise - transformError: (error: any) => string + transformResponse?: (response: Response) => Promise + transformError?: (error: any) => string | Promise } export interface TableRow { diff --git a/tools/utils.ts b/tools/utils.ts index 209402b473..2547ff9ccd 100644 --- a/tools/utils.ts +++ b/tools/utils.ts @@ -77,12 +77,40 @@ export async function executeRequest( } // Use the tool's error transformer or a default message - const error = tool.transformError - ? tool.transformError(errorContent) - : errorContent.message || `${toolId} API error: ${externalResponse.statusText}` + if (tool.transformError) { + try { + const errorResult = tool.transformError(errorContent) - console.error(`${toolId} error:`, error) - throw new Error(error) + // Handle both string and Promise return types + if (typeof errorResult === 'string') { + throw new Error(errorResult) + } else { + // It's a Promise, await it + const transformedError = await errorResult + // If it's a string or has an error property, use it + if (typeof transformedError === 'string') { + throw new Error(transformedError) + } else if ( + transformedError && + typeof transformedError === 'object' && + 'error' in transformedError + ) { + throw new Error(transformedError.error || 'Tool returned an error') + } + // Fallback + throw new Error('Tool returned an error') + } + } catch (e) { + if (e instanceof Error) { + throw e + } + throw new Error(`${toolId} API error: ${externalResponse.statusText}`) + } + } else { + const error = errorContent.message || `${toolId} API error: ${externalResponse.statusText}` + console.error(`${toolId} error:`, error) + throw new Error(error) + } } const transformResponse =