diff --git a/apps/sim/app/api/docs/search/route.ts b/apps/sim/app/api/docs/search/route.ts index 3b51b57c61..c24b8e7924 100644 --- a/apps/sim/app/api/docs/search/route.ts +++ b/apps/sim/app/api/docs/search/route.ts @@ -1,58 +1,37 @@ -import { type NextRequest, NextResponse } from 'next/server' -import { z } from 'zod' -import { searchDocumentation } from '@/lib/copilot/service' +import { NextRequest, NextResponse } from 'next/server' import { createLogger } from '@/lib/logs/console-logger' +import { searchDocumentation } from '@/lib/copilot/service' const logger = createLogger('DocsSearchAPI') -const SearchSchema = z.object({ - query: z.string().min(1, 'Query is required'), - topK: z.number().min(1).max(20).default(5), -}) - -/** - * POST /api/docs/search - * Search documentation for copilot tools - */ -export async function POST(req: NextRequest) { - const requestId = crypto.randomUUID() - +export async function POST(request: NextRequest) { try { - const body = await req.json() - const { query, topK } = SearchSchema.parse(body) + const { query, topK = 5 } = await request.json() - logger.info(`[${requestId}] Documentation search request: "${query}"`, { topK }) + if (!query) { + return NextResponse.json({ error: 'Query is required' }, { status: 400 }) + } + + logger.info('Executing documentation search', { query, topK }) const results = await searchDocumentation(query, { topK }) - logger.info(`[${requestId}] Found ${results.length} documentation results`, { query }) + logger.info(`Found ${results.length} documentation results`, { query }) return NextResponse.json({ success: true, results, query, totalResults: results.length, - metadata: { - requestId, - query, - topK, - }, }) } catch (error) { - if (error instanceof z.ZodError) { - return NextResponse.json( - { error: 'Invalid request data', details: error.errors }, - { status: 400 } - ) - } - - logger.error(`[${requestId}] Documentation search error:`, error) + logger.error('Documentation search API failed', error) return NextResponse.json( { - error: 'Failed to search documentation', - details: error instanceof Error ? error.message : 'Unknown error', + success: false, + error: `Documentation search failed: ${error instanceof Error ? error.message : 'Unknown error'}`, }, { status: 500 } ) } -} +} \ No newline at end of file diff --git a/apps/sim/app/api/workflows/current/yaml/route.ts b/apps/sim/app/api/workflows/current/yaml/route.ts new file mode 100644 index 0000000000..197f035112 --- /dev/null +++ b/apps/sim/app/api/workflows/current/yaml/route.ts @@ -0,0 +1,56 @@ +import { NextRequest, NextResponse } from 'next/server' +import { createLogger } from '@/lib/logs/console-logger' +import { useWorkflowYamlStore } from '@/stores/workflows/yaml/store' + +const logger = createLogger('GetWorkflowYamlAPI') + +export async function POST(request: NextRequest) { + try { + const { includeMetadata = false } = await request.json() + + logger.info('Executing get user workflow', { includeMetadata }) + + // Get the workflow YAML using the same store as the UI + const yamlStore = useWorkflowYamlStore.getState() + const yamlContent = yamlStore.getYaml() + + if (!yamlContent) { + return NextResponse.json( + { success: false, error: 'No workflow content available' }, + { status: 404 } + ) + } + + let metadata = undefined + if (includeMetadata) { + // Get additional workflow metadata if requested + const workflowStore = yamlStore as any // Access internal state + metadata = { + name: workflowStore.workflow?.name || 'Unnamed Workflow', + description: workflowStore.workflow?.description || '', + createdAt: workflowStore.workflow?.createdAt, + updatedAt: workflowStore.workflow?.updatedAt, + } + } + + logger.info('Successfully generated workflow YAML', { + includeMetadata, + yamlLength: yamlContent.length + }) + + return NextResponse.json({ + success: true, + yaml: yamlContent, + metadata: metadata, + }) + } catch (error) { + logger.error('Get user workflow API failed', error) + return NextResponse.json( + { + success: false, + error: `Failed to get user workflow: ${error instanceof Error ? error.message : 'Unknown error'}`, + }, + { status: 500 } + ) + } +} \ No newline at end of file diff --git a/apps/sim/lib/copilot/service.ts b/apps/sim/lib/copilot/service.ts index f7c95f3e03..f801439499 100644 --- a/apps/sim/lib/copilot/service.ts +++ b/apps/sim/lib/copilot/service.ts @@ -406,7 +406,7 @@ export async function generateChatResponse( content: message, }) - // Define the documentation search tool for the LLM + // Define the tools available to the LLM const tools: ProviderToolConfig[] = [ { id: 'docs_search_internal', @@ -430,6 +430,24 @@ export async function generateChatResponse( required: ['query'], }, }, + { + id: 'get_user_workflow', + name: 'Get User Workflow', + description: + 'Get the current user workflow as YAML format. This shows all blocks, their configurations, inputs, and connections in the workflow.', + params: {}, + parameters: { + type: 'object', + properties: { + includeMetadata: { + type: 'boolean', + description: 'Whether to include additional metadata about the workflow (default: false)', + default: false, + }, + }, + required: [], + }, + }, ] const response = await executeProviderRequest(provider, { diff --git a/apps/sim/lib/copilot/tools.ts b/apps/sim/lib/copilot/tools.ts index 451d0a3816..2455a855b1 100644 --- a/apps/sim/lib/copilot/tools.ts +++ b/apps/sim/lib/copilot/tools.ts @@ -72,9 +72,77 @@ const docsSearchTool: CopilotTool = { }, } +// Get user workflow as YAML tool for copilot +const getUserWorkflowTool: CopilotTool = { + id: 'get_user_workflow', + name: 'Get User Workflow', + description: + 'Get the current user workflow as YAML format. This shows all blocks, their configurations, inputs, and connections in the workflow.', + parameters: { + type: 'object', + properties: { + includeMetadata: { + type: 'boolean', + description: 'Whether to include additional metadata about the workflow (default: false)', + default: false, + }, + }, + required: [], + }, + execute: async (args: Record): Promise => { + try { + const { includeMetadata = false } = args + + logger.info('Executing get user workflow', { includeMetadata }) + + // Import the workflow YAML store dynamically to avoid import issues + const { useWorkflowYamlStore } = await import('@/stores/workflows/yaml/store') + const { useWorkflowRegistry } = await import('@/stores/workflows/registry/store') + + // Get the current workflow YAML + const yamlContent = useWorkflowYamlStore.getState().getYaml() + + // Get additional metadata if requested + let metadata = {} + if (includeMetadata) { + const registry = useWorkflowRegistry.getState() + const activeWorkflowId = registry.activeWorkflowId + const activeWorkflow = activeWorkflowId ? registry.workflows[activeWorkflowId] : null + + if (activeWorkflow) { + metadata = { + workflowId: activeWorkflowId, + name: activeWorkflow.name, + description: activeWorkflow.description, + lastModified: activeWorkflow.lastModified, + workspaceId: activeWorkflow.workspaceId, + } + } + } + + logger.info('Successfully retrieved user workflow YAML') + + return { + success: true, + data: { + yaml: yamlContent, + metadata: includeMetadata ? metadata : undefined, + }, + } + } catch (error) { + logger.error('Get user workflow failed', error) + return { + success: false, + error: `Failed to get user workflow: ${error instanceof Error ? error.message : 'Unknown error'}`, + } + } + }, +} + // Copilot tools registry const copilotTools: Record = { docs_search_internal: docsSearchTool, + get_user_workflow: getUserWorkflowTool, } // Get a copilot tool by ID diff --git a/apps/sim/tools/docs/search.ts b/apps/sim/tools/docs/search.ts index f364bf2ca6..99c746a6fb 100644 --- a/apps/sim/tools/docs/search.ts +++ b/apps/sim/tools/docs/search.ts @@ -1,23 +1,6 @@ import type { ToolConfig } from '../types' -export interface DocsSearchParams { - query: string - topK?: number -} - -export interface DocsSearchResponse { - results: Array<{ - id: number - title: string - url: string - content: string - similarity: number - }> - query: string - totalResults: number -} - -export const docsSearchTool: ToolConfig = { +export const docsSearchTool: ToolConfig = { id: 'docs_search_internal', name: 'Search Documentation', description: @@ -34,7 +17,6 @@ export const docsSearchTool: ToolConfig = type: 'number', required: false, description: 'Number of results to return (default: 5, max: 10)', - default: 5, }, }, @@ -50,25 +32,4 @@ export const docsSearchTool: ToolConfig = }), isInternalRoute: true, }, - - transformResponse: async (response: Response): Promise => { - const data = await response.json() - - if (!response.ok) { - return { - success: false, - output: {}, - error: data.error || 'Failed to search documentation', - } - } - - return { - success: true, - output: { - results: data.results || [], - query: data.query || '', - totalResults: data.totalResults || 0, - }, - } - }, -} +} \ No newline at end of file diff --git a/apps/sim/tools/utils.ts b/apps/sim/tools/utils.ts index 34406801d3..d2df55b594 100644 --- a/apps/sim/tools/utils.ts +++ b/apps/sim/tools/utils.ts @@ -4,10 +4,17 @@ import { useCustomToolsStore } from '@/stores/custom-tools/store' import { useEnvironmentStore } from '@/stores/settings/environment/store' import { docsSearchTool } from './docs/search' import { tools } from './registry' +import { getUserWorkflowTool } from './workflow/get-yaml' import type { TableRow, ToolConfig, ToolResponse } from './types' const logger = createLogger('ToolsUtils') +// Internal-only tools (not exposed to users in workflows) +const internalTools: Record = { + docs_search_internal: docsSearchTool, + get_user_workflow: getUserWorkflowTool, +} + /** * Transforms a table from the store format to a key-value object * @param table Array of table rows from the store @@ -268,11 +275,6 @@ export function createCustomToolRequestBody( } } -// Internal-only tools (not exposed to users in workflows) -const internalTools: Record = { - docs_search_internal: docsSearchTool, -} - // Get a tool by its ID export function getTool(toolId: string): ToolConfig | undefined { // Check for internal tools first diff --git a/apps/sim/tools/workflow/get-yaml.ts b/apps/sim/tools/workflow/get-yaml.ts new file mode 100644 index 0000000000..bc5c0b21c3 --- /dev/null +++ b/apps/sim/tools/workflow/get-yaml.ts @@ -0,0 +1,29 @@ +import type { ToolConfig } from '../types' + +export const getUserWorkflowTool: ToolConfig = { + id: 'get_user_workflow', + name: 'Get User Workflow', + description: + 'Get the current user workflow as YAML format. This shows all blocks, their configurations, inputs, and connections in the workflow.', + version: '1.0.0', + + params: { + includeMetadata: { + type: 'boolean', + required: false, + description: 'Whether to include additional metadata about the workflow (default: false)', + }, + }, + + request: { + url: '/api/workflows/current/yaml', + method: 'POST', + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => ({ + includeMetadata: params.includeMetadata || false, + }), + isInternalRoute: true, + }, +} \ No newline at end of file