mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
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:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user