Skip to content

Commit a588317

Browse files
committed
Get user workflow tool
1 parent f6b25bf commit a588317

File tree

8 files changed

+447
-283
lines changed

8 files changed

+447
-283
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { createLogger } from '@/lib/logs/console-logger'
3+
import { db } from '@/db'
4+
import { workflow as workflowTable } from '@/db/schema'
5+
import { eq } from 'drizzle-orm'
6+
import { generateWorkflowYaml } from '@/lib/workflows/yaml-generator'
7+
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/db-helpers'
8+
9+
const logger = createLogger('GetUserWorkflowAPI')
10+
11+
export async function POST(request: NextRequest) {
12+
try {
13+
const body = await request.json()
14+
const { workflowId, includeMetadata = false } = body
15+
16+
if (!workflowId) {
17+
return NextResponse.json(
18+
{ success: false, error: 'Workflow ID is required' },
19+
{ status: 400 }
20+
)
21+
}
22+
23+
logger.info('Fetching workflow for YAML generation', { workflowId })
24+
25+
// Fetch workflow from database
26+
const [workflowRecord] = await db
27+
.select()
28+
.from(workflowTable)
29+
.where(eq(workflowTable.id, workflowId))
30+
.limit(1)
31+
32+
if (!workflowRecord) {
33+
return NextResponse.json(
34+
{ success: false, error: `Workflow ${workflowId} not found` },
35+
{ status: 404 }
36+
)
37+
}
38+
39+
// Try to load from normalized tables first, fallback to JSON blob
40+
let workflowState: any = null
41+
let subBlockValues: Record<string, Record<string, any>> = {}
42+
43+
const normalizedData = await loadWorkflowFromNormalizedTables(workflowId)
44+
if (normalizedData) {
45+
workflowState = {
46+
blocks: normalizedData.blocks,
47+
edges: normalizedData.edges,
48+
loops: normalizedData.loops,
49+
parallels: normalizedData.parallels,
50+
}
51+
52+
// Extract subblock values from normalized data
53+
Object.entries(normalizedData.blocks).forEach(([blockId, block]) => {
54+
subBlockValues[blockId] = {}
55+
Object.entries((block as any).subBlocks || {}).forEach(([subBlockId, subBlock]) => {
56+
if ((subBlock as any).value !== undefined) {
57+
subBlockValues[blockId][subBlockId] = (subBlock as any).value
58+
}
59+
})
60+
})
61+
} else if (workflowRecord.state) {
62+
// Fallback to JSON blob
63+
workflowState = workflowRecord.state as any
64+
// For JSON blob, subblock values are embedded in the block state
65+
Object.entries((workflowState.blocks as any) || {}).forEach(([blockId, block]) => {
66+
subBlockValues[blockId] = {}
67+
Object.entries(((block as any).subBlocks || {})).forEach(([subBlockId, subBlock]) => {
68+
if ((subBlock as any).value !== undefined) {
69+
subBlockValues[blockId][subBlockId] = (subBlock as any).value
70+
}
71+
})
72+
})
73+
}
74+
75+
if (!workflowState || !workflowState.blocks) {
76+
return NextResponse.json(
77+
{ success: false, error: 'Workflow state is empty or invalid' },
78+
{ status: 400 }
79+
)
80+
}
81+
82+
// Generate YAML using server-side function
83+
const yaml = generateWorkflowYaml(workflowState, subBlockValues)
84+
85+
if (!yaml || yaml.trim() === '') {
86+
return NextResponse.json(
87+
{ success: false, error: 'Generated YAML is empty' },
88+
{ status: 400 }
89+
)
90+
}
91+
92+
// Prepare response
93+
const response: any = {
94+
yaml,
95+
format: 'yaml',
96+
blockCount: Object.keys(workflowState.blocks).length,
97+
edgeCount: (workflowState.edges || []).length,
98+
}
99+
100+
// Add metadata if requested
101+
if (includeMetadata) {
102+
response.metadata = {
103+
workflowId: workflowRecord.id,
104+
name: workflowRecord.name,
105+
description: workflowRecord.description,
106+
workspaceId: workflowRecord.workspaceId,
107+
createdAt: workflowRecord.createdAt,
108+
updatedAt: workflowRecord.updatedAt,
109+
}
110+
}
111+
112+
logger.info('Successfully generated workflow YAML', {
113+
workflowId,
114+
blockCount: response.blockCount,
115+
yamlLength: yaml.length,
116+
})
117+
118+
return NextResponse.json({
119+
success: true,
120+
output: response,
121+
})
122+
} catch (error) {
123+
logger.error('Failed to get workflow YAML:', error)
124+
return NextResponse.json(
125+
{
126+
success: false,
127+
error: `Failed to get workflow YAML: ${error instanceof Error ? error.message : 'Unknown error'}`,
128+
},
129+
{ status: 500 }
130+
)
131+
}
132+
}

apps/sim/app/api/workflows/current/yaml/route.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { type NextRequest, NextResponse } from 'next/server'
2+
import { createLogger } from '@/lib/logs/console-logger'
3+
import { generateWorkflowYaml } from '@/lib/workflows/yaml-generator'
4+
5+
const logger = createLogger('WorkflowYamlAPI')
6+
7+
export async function POST(request: NextRequest) {
8+
const requestId = crypto.randomUUID().slice(0, 8)
9+
10+
try {
11+
logger.info(`[${requestId}] Converting workflow JSON to YAML`)
12+
13+
const body = await request.json()
14+
const { workflowState, subBlockValues, includeMetadata = false } = body
15+
16+
if (!workflowState) {
17+
return NextResponse.json(
18+
{ success: false, error: 'workflowState is required' },
19+
{ status: 400 }
20+
)
21+
}
22+
23+
// Generate YAML using the shared utility
24+
const yamlContent = generateWorkflowYaml(workflowState, subBlockValues)
25+
26+
logger.info(`[${requestId}] Successfully generated YAML`, {
27+
yamlLength: yamlContent.length,
28+
})
29+
30+
return NextResponse.json({
31+
success: true,
32+
yaml: yamlContent,
33+
})
34+
} catch (error) {
35+
logger.error(`[${requestId}] YAML generation failed`, error)
36+
return NextResponse.json(
37+
{
38+
success: false,
39+
error: `Failed to generate YAML: ${error instanceof Error ? error.message : 'Unknown error'}`,
40+
},
41+
{ status: 500 }
42+
)
43+
}
44+
}

apps/sim/lib/copilot/service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ export async function generateChatResponse(
460460
maxTokens: config.chat.maxTokens,
461461
apiKey,
462462
stream,
463+
workflowId: options.workflowId,
463464
})
464465

465466
// Handle StreamingExecution (from providers with tool calls)

apps/sim/lib/copilot/tools.ts

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -80,43 +80,32 @@ const getUserWorkflowTool: CopilotTool = {
8080
'Get the current user workflow as YAML format. This shows all blocks, their configurations, inputs, and connections in the workflow.',
8181
parameters: {
8282
type: 'object',
83-
properties: {
84-
includeMetadata: {
85-
type: 'boolean',
86-
description: 'Whether to include additional metadata about the workflow (default: false)',
87-
default: false,
88-
},
89-
},
83+
properties: {},
9084
required: [],
9185
},
9286
execute: async (args: Record<string, any>): Promise<CopilotToolResult> => {
9387
try {
94-
const { includeMetadata = false } = args
95-
96-
logger.info('Executing get user workflow', { includeMetadata })
88+
logger.info('Executing get user workflow')
9789

9890
// Import the workflow YAML store dynamically to avoid import issues
9991
const { useWorkflowYamlStore } = await import('@/stores/workflows/yaml/store')
10092
const { useWorkflowRegistry } = await import('@/stores/workflows/registry/store')
10193

102-
// Get the current workflow YAML
94+
// Get the current workflow YAML using the same logic as export
10395
const yamlContent = useWorkflowYamlStore.getState().getYaml()
10496

105-
// Get additional metadata if requested
106-
let metadata = {}
107-
if (includeMetadata) {
108-
const registry = useWorkflowRegistry.getState()
109-
const activeWorkflowId = registry.activeWorkflowId
110-
const activeWorkflow = activeWorkflowId ? registry.workflows[activeWorkflowId] : null
111-
112-
if (activeWorkflow) {
113-
metadata = {
114-
workflowId: activeWorkflowId,
115-
name: activeWorkflow.name,
116-
description: activeWorkflow.description,
117-
lastModified: activeWorkflow.lastModified,
118-
workspaceId: activeWorkflow.workspaceId,
119-
}
97+
// Get workflow metadata
98+
const registry = useWorkflowRegistry.getState()
99+
const activeWorkflowId = registry.activeWorkflowId
100+
const activeWorkflow = activeWorkflowId ? registry.workflows[activeWorkflowId] : null
101+
102+
let metadata = undefined
103+
if (activeWorkflow) {
104+
metadata = {
105+
workflowId: activeWorkflowId,
106+
name: activeWorkflow.name,
107+
description: activeWorkflow.description,
108+
workspaceId: activeWorkflow.workspaceId,
120109
}
121110
}
122111

@@ -126,7 +115,7 @@ const getUserWorkflowTool: CopilotTool = {
126115
success: true,
127116
data: {
128117
yaml: yamlContent,
129-
metadata: includeMetadata ? metadata : undefined,
118+
metadata: metadata,
130119
},
131120
}
132121
} catch (error) {

0 commit comments

Comments
 (0)