fix: added check for empty strings in executor to be treated as null, add debug logs for async workflow executions with agent response format

This commit is contained in:
Waleed Latif
2025-03-11 01:48:22 -07:00
parent 6985b2e174
commit 1959b3b2c8
6 changed files with 226 additions and 23 deletions
+35 -1
View File
@@ -272,9 +272,43 @@ export async function GET(req: NextRequest) {
workflowId: schedule.workflowId,
}
// Process the block states to ensure response formats are properly parsed
// This is crucial for agent blocks with response format
const processedBlockStates = Object.entries(currentBlockStates).reduce(
(acc, [blockId, blockState]) => {
// Check if this block has a responseFormat that needs to be parsed
if (blockState.responseFormat && typeof blockState.responseFormat === 'string') {
try {
console.log(
`[Schedule Debug] Block ${blockId} has responseFormat as string:`,
blockState.responseFormat
)
// Attempt to parse the responseFormat if it's a string
const parsedResponseFormat = JSON.parse(blockState.responseFormat)
console.log(
`[Schedule Debug] Successfully parsed responseFormat for block ${blockId}:`,
parsedResponseFormat
)
acc[blockId] = {
...blockState,
responseFormat: parsedResponseFormat,
}
} catch (error) {
console.warn(`Failed to parse responseFormat for block ${blockId}:`, error)
acc[blockId] = blockState
}
} else {
acc[blockId] = blockState
}
return acc
},
{} as Record<string, Record<string, any>>
)
const executor = new Executor(
serializedWorkflow,
currentBlockStates,
processedBlockStates, // Use the processed block states
decryptedEnvVars,
input
)
+35 -1
View File
@@ -277,9 +277,43 @@ export async function POST(
console.log('Executing workflow with workflowId:', foundWorkflow.id)
// Process the block states to ensure response formats are properly parsed
// This is crucial for agent blocks with response format
const processedBlockStates = Object.entries(currentBlockStates).reduce(
(acc, [blockId, blockState]) => {
// Check if this block has a responseFormat that needs to be parsed
if (blockState.responseFormat && typeof blockState.responseFormat === 'string') {
try {
console.log(
`[Webhook Debug] Block ${blockId} has responseFormat as string:`,
blockState.responseFormat
)
// Attempt to parse the responseFormat if it's a string
const parsedResponseFormat = JSON.parse(blockState.responseFormat)
console.log(
`[Webhook Debug] Successfully parsed responseFormat for block ${blockId}:`,
parsedResponseFormat
)
acc[blockId] = {
...blockState,
responseFormat: parsedResponseFormat,
}
} catch (error) {
console.warn(`Failed to parse responseFormat for block ${blockId}:`, error)
acc[blockId] = blockState
}
} else {
acc[blockId] = blockState
}
return acc
},
{} as Record<string, Record<string, any>>
)
const executor = new Executor(
serializedWorkflow,
currentBlockStates,
processedBlockStates, // Use the processed block states
decryptedEnvVars,
enrichedInput
)
+35 -1
View File
@@ -111,9 +111,43 @@ async function executeWorkflow(workflow: any, input?: any) {
}
}
// Process the block states to ensure response formats are properly parsed
// This is crucial for agent blocks with response format
const processedBlockStates = Object.entries(currentBlockStates).reduce(
(acc, [blockId, blockState]) => {
// Check if this block has a responseFormat that needs to be parsed
if (blockState.responseFormat && typeof blockState.responseFormat === 'string') {
try {
console.log(
`[API Debug] Block ${blockId} has responseFormat as string:`,
blockState.responseFormat
)
// Attempt to parse the responseFormat if it's a string
const parsedResponseFormat = JSON.parse(blockState.responseFormat)
console.log(
`[API Debug] Successfully parsed responseFormat for block ${blockId}:`,
parsedResponseFormat
)
acc[blockId] = {
...blockState,
responseFormat: parsedResponseFormat,
}
} catch (error) {
console.warn(`Failed to parse responseFormat for block ${blockId}:`, error)
acc[blockId] = blockState
}
} else {
acc[blockId] = blockState
}
return acc
},
{} as Record<string, Record<string, any>>
)
// Serialize and execute the workflow
const serializedWorkflow = new Serializer().serializeWorkflow(mergedStates, edges, loops)
const executor = new Executor(serializedWorkflow, currentBlockStates, decryptedEnvVars, input)
const executor = new Executor(serializedWorkflow, processedBlockStates, decryptedEnvVars, input)
const result = await executor.execute(workflowId)
// Log each execution step and the final result
@@ -120,6 +120,20 @@ export function useWorkflowExecution() {
useSubBlockStore.getState().workflowValues[activeWorkflowId]
)
console.log('Merged Block States for Execution:', currentBlockStates)
// Debug any responseFormat fields
const blockWithResponseFormat = Object.entries(currentBlockStates).find(
([_id, state]) => state.responseFormat
)
if (blockWithResponseFormat) {
console.log(
'ResponseFormat found:',
blockWithResponseFormat[0],
typeof blockWithResponseFormat[1].responseFormat,
blockWithResponseFormat[1].responseFormat
)
}
console.groupEnd()
// Get environment variables
+74 -12
View File
@@ -106,21 +106,42 @@ export class AgentBlockHandler implements BlockHandler {
inputs: Record<string, any>,
context: ExecutionContext
): Promise<BlockOutput> {
console.log(`[AgentBlockHandler Debug] Executing agent block: ${block.id}`)
console.log(`[AgentBlockHandler Debug] Block inputs:`, JSON.stringify(inputs, null, 2))
// Parse response format if provided
let responseFormat: any = undefined
if (inputs.responseFormat) {
try {
responseFormat =
typeof inputs.responseFormat === 'string'
? JSON.parse(inputs.responseFormat)
: inputs.responseFormat
} catch (error: any) {
throw new Error(`Invalid response format: ${error.message}`)
// Handle empty string case - treat it as no response format
if (inputs.responseFormat === '') {
console.log(
`[AgentBlockHandler Debug] Response format is an empty string, treating as undefined`
)
responseFormat = undefined
} else {
try {
console.log(
`[AgentBlockHandler Debug] Response format before parsing:`,
typeof inputs.responseFormat,
inputs.responseFormat
)
responseFormat =
typeof inputs.responseFormat === 'string'
? JSON.parse(inputs.responseFormat)
: inputs.responseFormat
console.log(`[AgentBlockHandler Debug] Response format after parsing:`, responseFormat)
} catch (error: any) {
console.error(`[AgentBlockHandler Error] Failed to parse response format:`, error)
throw new Error(`Invalid response format: ${error.message}`)
}
}
}
const model = inputs.model || 'gpt-4o'
const providerId = getProviderFromModel(model)
console.log(`[AgentBlockHandler Debug] Using provider: ${providerId}, model: ${model}`)
// Format tools for provider API
const formattedTools = Array.isArray(inputs.tools)
@@ -208,10 +229,27 @@ export class AgentBlockHandler implements BlockHandler {
responseFormat,
})
// Return structured or standard response based on responseFormat
return responseFormat
? {
...JSON.parse(response.content),
console.log(`[AgentBlockHandler Debug] Provider response:`, {
content: response.content,
contentType: typeof response.content,
contentLength: response.content ? response.content.length : 0,
model: response.model,
hasTokens: !!response.tokens,
hasToolCalls: !!response.toolCalls,
})
// For structured responses, try to parse the content
if (responseFormat) {
try {
console.log(
`[AgentBlockHandler Debug] Attempting to parse response content as JSON:`,
response.content
)
const parsedContent = JSON.parse(response.content)
console.log(`[AgentBlockHandler Debug] Successfully parsed content:`, parsedContent)
return {
...parsedContent,
tokens: response.tokens || {
prompt: 0,
completion: 0,
@@ -224,7 +262,12 @@ export class AgentBlockHandler implements BlockHandler {
}
: undefined,
}
: {
} catch (error) {
console.error(`[AgentBlockHandler Error] Failed to parse response content:`, error)
console.log(`[AgentBlockHandler Debug] Falling back to standard response format`)
// Fall back to standard response if parsing fails
return {
response: {
content: response.content,
model: response.model,
@@ -239,6 +282,25 @@ export class AgentBlockHandler implements BlockHandler {
},
},
}
}
}
// Return standard response if no responseFormat
return {
response: {
content: response.content,
model: response.model,
tokens: response.tokens || {
prompt: 0,
completion: 0,
total: 0,
},
toolCalls: {
list: response.toolCalls || [],
count: response.toolCalls?.length || 0,
},
},
}
}
}
+33 -8
View File
@@ -5,6 +5,9 @@ export async function executeProviderRequest(
providerId: string,
request: ProviderRequest
): Promise<ProviderResponse> {
console.log(`[Provider Debug] Executing request with provider: ${providerId}`)
console.log(`[Provider Debug] Request has responseFormat:`, !!request.responseFormat)
const provider = getProvider(providerId)
if (!provider) {
throw new Error(`Provider not found: ${providerId}`)
@@ -16,17 +19,39 @@ export async function executeProviderRequest(
// If responseFormat is provided, modify the system prompt to enforce structured output
if (request.responseFormat) {
const structuredOutputInstructions = generateStructuredOutputInstructions(
request.responseFormat
)
// Handle empty string case
if (typeof request.responseFormat === 'string' && request.responseFormat === '') {
console.log(`[Provider Debug] Response format is an empty string, ignoring it`)
request.responseFormat = undefined
} else {
console.log(`[Provider Debug] Response format:`, request.responseFormat)
// Only add additional instructions if they're not empty
if (structuredOutputInstructions.trim()) {
request.systemPrompt =
`${request.systemPrompt || ''}\n\n${structuredOutputInstructions}`.trim()
const structuredOutputInstructions = generateStructuredOutputInstructions(
request.responseFormat
)
console.log(`[Provider Debug] Generated instructions:`, structuredOutputInstructions)
// Only add additional instructions if they're not empty
if (structuredOutputInstructions.trim()) {
const originalPrompt = request.systemPrompt || ''
request.systemPrompt = `${originalPrompt}\n\n${structuredOutputInstructions}`.trim()
console.log(`[Provider Debug] Updated system prompt with instructions`)
}
}
}
// Execute the request using the provider's implementation
return await provider.executeRequest(request)
const response = await provider.executeRequest(request)
console.log(`[Provider Debug] Provider response:`, {
contentType: typeof response.content,
contentLength: response.content ? response.content.length : 0,
model: response.model,
hasTokens: !!response.tokens,
hasToolCalls: !!response.toolCalls,
})
return response
}