mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
Feat/api input (#89)
* feat(api-input): correctly passing in body into executor; need to resolve tag value in blocks following starter * feat(api-input): input is now resolved * feat(api-input): cleaned up logs and added comments
This commit is contained in:
@@ -113,7 +113,7 @@ async function executeWorkflow(workflow: any, input?: any) {
|
||||
|
||||
// Serialize and execute the workflow
|
||||
const serializedWorkflow = new Serializer().serializeWorkflow(mergedStates, edges, loops)
|
||||
const executor = new Executor(serializedWorkflow, currentBlockStates, decryptedEnvVars)
|
||||
const executor = new Executor(serializedWorkflow, currentBlockStates, decryptedEnvVars, input)
|
||||
const result = await executor.execute(workflowId)
|
||||
|
||||
// Log each execution step
|
||||
|
||||
@@ -131,15 +131,12 @@ export const StarterBlock: BlockConfig = {
|
||||
access: [],
|
||||
},
|
||||
inputs: {
|
||||
code: { type: 'string', required: true },
|
||||
executionMode: { type: 'string', required: true },
|
||||
input: { type: 'json', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
result: 'any',
|
||||
stdout: 'string',
|
||||
executionTime: 'number',
|
||||
input: 'any',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -103,12 +103,15 @@ export class AgentBlockHandler implements BlockHandler {
|
||||
.filter((t): t is NonNullable<typeof t> => t !== null)
|
||||
: []
|
||||
|
||||
// Ensure context is properly formatted for the provider
|
||||
const response = await executeProviderRequest(providerId, {
|
||||
model,
|
||||
systemPrompt: inputs.systemPrompt,
|
||||
context: Array.isArray(inputs.context)
|
||||
? JSON.stringify(inputs.context, null, 2)
|
||||
: inputs.context,
|
||||
: typeof inputs.context === 'string'
|
||||
? inputs.context
|
||||
: JSON.stringify(inputs.context, null, 2),
|
||||
tools: formattedTools.length > 0 ? formattedTools : undefined,
|
||||
temperature: inputs.temperature,
|
||||
maxTokens: inputs.maxTokens,
|
||||
|
||||
+25
-2
@@ -28,13 +28,16 @@ export class Executor {
|
||||
private loopManager: LoopManager
|
||||
private pathTracker: PathTracker
|
||||
private blockHandlers: BlockHandler[]
|
||||
private workflowInput: any
|
||||
|
||||
constructor(
|
||||
private workflow: SerializedWorkflow,
|
||||
private initialBlockStates: Record<string, BlockOutput> = {},
|
||||
private environmentVariables: Record<string, string> = {}
|
||||
private environmentVariables: Record<string, string> = {},
|
||||
workflowInput?: any
|
||||
) {
|
||||
this.validateWorkflow()
|
||||
this.workflowInput = workflowInput || {}
|
||||
|
||||
this.resolver = new InputResolver(workflow, environmentVariables)
|
||||
this.loopManager = new LoopManager(workflow.loops || {})
|
||||
@@ -211,11 +214,22 @@ export class Executor {
|
||||
|
||||
const starterBlock = this.workflow.blocks.find((block) => block.metadata?.id === 'starter')
|
||||
if (starterBlock) {
|
||||
// Initialize the starter block with the workflow input
|
||||
const starterOutput = {
|
||||
response: {
|
||||
type: {
|
||||
input: this.workflowInput,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
context.blockStates.set(starterBlock.id, {
|
||||
output: { response: { result: true } },
|
||||
output: starterOutput,
|
||||
executed: true,
|
||||
executionTime: 0,
|
||||
})
|
||||
|
||||
// Mark the starter block as executed and add its connections to the active path
|
||||
context.executedBlocks.add(starterBlock.id)
|
||||
|
||||
const connectedToStarter = this.workflow.connections
|
||||
@@ -371,6 +385,15 @@ export class Executor {
|
||||
throw new Error(`Block ${blockId} not found`)
|
||||
}
|
||||
|
||||
// Special case for starter block - it's already been initialized in createExecutionContext
|
||||
// This ensures we don't re-execute the starter block and just return its existing state
|
||||
if (block.metadata?.id === 'starter') {
|
||||
const starterState = context.blockStates.get(blockId)
|
||||
if (starterState) {
|
||||
return starterState.output as NormalizedBlockOutput
|
||||
}
|
||||
}
|
||||
|
||||
const blockLog = this.createBlockLog(block)
|
||||
const addConsole = useConsoleStore.getState().addConsole
|
||||
|
||||
|
||||
+56
-2
@@ -14,12 +14,27 @@ export class InputResolver {
|
||||
) {
|
||||
// Create maps for efficient lookups
|
||||
this.blockById = new Map(workflow.blocks.map((block) => [block.id, block]))
|
||||
|
||||
// Initialize the normalized name map
|
||||
this.blockByNormalizedName = new Map(
|
||||
workflow.blocks.map((block) => [
|
||||
block.metadata?.name ? this.normalizeBlockName(block.metadata.name) : block.id,
|
||||
block,
|
||||
])
|
||||
)
|
||||
|
||||
// Add special handling for the starter block - allow referencing it as "start"
|
||||
const starterBlock = workflow.blocks.find((block) => block.metadata?.id === 'starter')
|
||||
if (starterBlock) {
|
||||
this.blockByNormalizedName.set('start', starterBlock)
|
||||
// Also add the normalized actual name if it exists
|
||||
if (starterBlock.metadata?.name) {
|
||||
this.blockByNormalizedName.set(
|
||||
this.normalizeBlockName(starterBlock.metadata.name),
|
||||
starterBlock
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,15 +114,53 @@ export class InputResolver {
|
||||
const path = match.slice(1, -1)
|
||||
const [blockRef, ...pathParts] = path.split('.')
|
||||
|
||||
let sourceBlock = this.blockById.get(blockRef)
|
||||
// Special case for "start" references
|
||||
// This allows users to reference the starter block using <start.response.type.input>
|
||||
// regardless of the actual name of the starter block
|
||||
if (blockRef.toLowerCase() === 'start') {
|
||||
// Find the starter block
|
||||
const starterBlock = this.workflow.blocks.find((block) => block.metadata?.id === 'starter')
|
||||
if (starterBlock) {
|
||||
const blockState = context.blockStates.get(starterBlock.id)
|
||||
if (blockState) {
|
||||
// Navigate through the path parts
|
||||
let replacementValue: any = blockState.output
|
||||
for (const part of pathParts) {
|
||||
if (!replacementValue || typeof replacementValue !== 'object') {
|
||||
throw new Error(`Invalid path "${part}" in "${path}" for starter block.`)
|
||||
}
|
||||
replacementValue = replacementValue[part]
|
||||
if (replacementValue === undefined) {
|
||||
throw new Error(`No value found at path "${path}" in starter block.`)
|
||||
}
|
||||
}
|
||||
|
||||
// Format the value
|
||||
const formattedValue =
|
||||
typeof replacementValue === 'object'
|
||||
? JSON.stringify(replacementValue)
|
||||
: String(replacementValue)
|
||||
|
||||
resolvedValue = resolvedValue.replace(match, formattedValue)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Standard block reference resolution
|
||||
let sourceBlock = this.blockById.get(blockRef)
|
||||
if (!sourceBlock) {
|
||||
const normalizedRef = this.normalizeBlockName(blockRef)
|
||||
sourceBlock = this.blockByNormalizedName.get(normalizedRef)
|
||||
}
|
||||
|
||||
if (!sourceBlock) {
|
||||
throw new Error(`Block reference "${blockRef}" was not found.`)
|
||||
// Provide a more helpful error message with available block names
|
||||
const availableBlocks = Array.from(this.blockByNormalizedName.keys()).join(', ')
|
||||
throw new Error(
|
||||
`Block reference "${blockRef}" was not found. Available blocks: ${availableBlocks}. ` +
|
||||
`For the starter block, try using "start" or the exact block name.`
|
||||
)
|
||||
}
|
||||
|
||||
if (sourceBlock.enabled === false) {
|
||||
@@ -156,6 +209,7 @@ export class InputResolver {
|
||||
`Invalid path "${part}" in "${path}" for block "${currentBlock.metadata?.name || currentBlock.id}".`
|
||||
)
|
||||
}
|
||||
|
||||
replacementValue = replacementValue[part]
|
||||
|
||||
if (replacementValue === undefined) {
|
||||
|
||||
@@ -176,9 +176,7 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
result: 'any',
|
||||
stdout: 'string',
|
||||
executionTime: 'number',
|
||||
input: 'any',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user