Skip to content

improvement(connection) #527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions apps/docs/content/docs/blocks/workflow.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,17 @@ Define the data to pass to the child workflow:

- **Single Variable Input**: Select a variable or block output to pass to the child workflow
- **Variable References**: Use `<variable.name>` to reference workflow variables
- **Block References**: Use `<blockName.response.field>` to reference outputs from previous blocks
- **Automatic Mapping**: The selected data is automatically available as `start.response.input` in the child workflow
- **Block References**: Use `<blockName.field>` to reference outputs from previous blocks
- **Automatic Mapping**: The selected data is automatically available as `start.input` in the child workflow
- **Optional**: The input field is optional - child workflows can run without input data
- **Type Preservation**: Variable types (strings, numbers, objects, etc.) are preserved when passed to the child workflow

### Examples of Input References

- `<variable.customerData>` - Pass a workflow variable
- `<dataProcessor.response.result>` - Pass the result from a previous block
- `<start.response.input>` - Pass the original workflow input
- `<apiCall.response.data.user>` - Pass a specific field from an API response
- `<dataProcessor.result>` - Pass the result from a previous block
- `<start.input>` - Pass the original workflow input
- `<apiCall.data.user>` - Pass a specific field from an API response

### Execution Context

Expand Down Expand Up @@ -109,7 +109,7 @@ To prevent infinite recursion and ensure system stability, the Workflow block in
<strong>Workflow ID</strong>: The identifier of the workflow to execute
</li>
<li>
<strong>Input Variable</strong>: Variable or block reference to pass to the child workflow (e.g., `<variable.name>` or `<block.response.field>`)
<strong>Input Variable</strong>: Variable or block reference to pass to the child workflow (e.g., `<variable.name>` or `<block.field>`)
</li>
</ul>
</Tab>
Expand Down Expand Up @@ -150,31 +150,31 @@ blocks:
- type: workflow
name: "Setup Customer Account"
workflowId: "account-setup-workflow"
input: "<Validate Customer Data.response.result>"
input: "<Validate Customer Data.result>"

- type: workflow
name: "Send Welcome Email"
workflowId: "welcome-email-workflow"
input: "<Setup Customer Account.response.result.accountDetails>"
input: "<Setup Customer Account.result.accountDetails>"
```
### Child Workflow: Customer Validation
```yaml
# Reusable customer validation workflow
# Access the input data using: start.response.input
# Access the input data using: start.input
blocks:
- type: function
name: "Validate Email"
code: |
const customerData = start.response.input;
const customerData = start.input;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(customerData.email);
- type: api
name: "Check Credit Score"
url: "https://api.creditcheck.com/score"
method: "POST"
body: "<start.response.input>"
body: "<start.input>"
```
### Variable Reference Examples
Expand All @@ -184,13 +184,13 @@ blocks:
input: "<variable.customerInfo>"

# Using block outputs
input: "<dataProcessor.response.cleanedData>"
input: "<dataProcessor.cleanedData>"

# Using nested object properties
input: "<apiCall.response.data.user.profile>"
input: "<apiCall.data.user.profile>"

# Using array elements (if supported by the resolver)
input: "<listProcessor.response.items[0]>"
input: "<listProcessor.items[0]>"
```
## Access Control and Permissions
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/app/api/__test-utils__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const sampleWorkflowState = {
webhookPath: { id: 'webhookPath', type: 'short-input', value: '' },
},
outputs: {
response: { type: { input: 'any' } },
input: 'any',
},
enabled: true,
horizontalHandles: true,
Expand All @@ -111,7 +111,7 @@ export const sampleWorkflowState = {
type: 'long-input',
value: 'You are a helpful assistant',
},
context: { id: 'context', type: 'short-input', value: '<start.response.input>' },
context: { id: 'context', type: 'short-input', value: '<start.input>' },
model: { id: 'model', type: 'dropdown', value: 'gpt-4o' },
apiKey: { id: 'apiKey', type: 'short-input', value: '{{OPENAI_API_KEY}}' },
},
Expand Down
6 changes: 3 additions & 3 deletions apps/sim/app/api/chat/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export async function validateChatAuth(
/**
* Executes a workflow for a chat request and returns the formatted output.
*
* When workflows reference <start.response.input>, they receive a structured JSON
* When workflows reference <start.input>, they receive a structured JSON
* containing both the message and conversationId for maintaining chat context.
*
* @param chatId - Chat deployment identifier
Expand Down Expand Up @@ -461,8 +461,8 @@ export async function executeWorkflowForChat(
if (result && 'success' in result) {
result.logs?.forEach((log: BlockLog) => {
if (streamedContent.has(log.blockId)) {
if (log.output?.response) {
log.output.response.content = streamedContent.get(log.blockId)
if (log.output) {
log.output.content = streamedContent.get(log.blockId)
}
}
})
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/app/api/codegen/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ Example Scenario:
User Prompt: "Fetch user data from an API. Use the User ID passed in as 'userId' and an API Key stored as the 'SERVICE_API_KEY' environment variable."

Generated Code:
const userId = <block.response.content>; // Correct: Accessing input parameter without quotes
const userId = <block.content>; // Correct: Accessing input parameter without quotes
const apiKey = {{SERVICE_API_KEY}}; // Correct: Accessing environment variable without quotes
const url = \`https://api.example.com/users/\${userId}\`;

Expand Down Expand Up @@ -273,7 +273,7 @@ Do not include import/require statements unless absolutely necessary and they ar
Do not include markdown formatting or explanations.
Output only the raw TypeScript code. Use modern TypeScript features where appropriate. Do not use semicolons.
Example:
const userId = <block.response.content> as string
const userId = <block.content> as string
const apiKey = {{SERVICE_API_KEY}}
const response = await fetch(\`https://api.example.com/users/\${userId}\`, { headers: { Authorization: \`Bearer \${apiKey}\` } })
if (!response.ok) {
Expand Down
32 changes: 15 additions & 17 deletions apps/sim/app/api/providers/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,24 +137,22 @@ export async function POST(request: NextRequest) {
const safeExecutionData = {
success: executionData.success,
output: {
response: {
// Sanitize content to remove non-ASCII characters that would cause ByteString errors
content: executionData.output?.response?.content
? String(executionData.output.response.content).replace(/[\u0080-\uFFFF]/g, '')
: '',
model: executionData.output?.response?.model,
tokens: executionData.output?.response?.tokens || {
prompt: 0,
completion: 0,
total: 0,
},
// Sanitize any potential Unicode characters in tool calls
toolCalls: executionData.output?.response?.toolCalls
? sanitizeToolCalls(executionData.output.response.toolCalls)
: undefined,
providerTiming: executionData.output?.response?.providerTiming,
cost: executionData.output?.response?.cost,
// Sanitize content to remove non-ASCII characters that would cause ByteString errors
content: executionData.output?.content
? String(executionData.output.content).replace(/[\u0080-\uFFFF]/g, '')
: '',
model: executionData.output?.model,
tokens: executionData.output?.tokens || {
prompt: 0,
completion: 0,
total: 0,
},
// Sanitize any potential Unicode characters in tool calls
toolCalls: executionData.output?.toolCalls
? sanitizeToolCalls(executionData.output.toolCalls)
: undefined,
providerTiming: executionData.output?.providerTiming,
cost: executionData.output?.cost,
},
error: executionData.error,
logs: [], // Strip logs from header to avoid encoding issues
Expand Down
3 changes: 3 additions & 0 deletions apps/sim/app/api/user/settings/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const SettingsSchema = z.object({
debugMode: z.boolean().optional(),
autoConnect: z.boolean().optional(),
autoFillEnvVars: z.boolean().optional(),
autoPan: z.boolean().optional(),
telemetryEnabled: z.boolean().optional(),
telemetryNotifiedUser: z.boolean().optional(),
emailPreferences: z
Expand All @@ -32,6 +33,7 @@ const defaultSettings = {
debugMode: false,
autoConnect: true,
autoFillEnvVars: true,
autoPan: true,
telemetryEnabled: true,
telemetryNotifiedUser: false,
emailPreferences: {},
Expand Down Expand Up @@ -65,6 +67,7 @@ export async function GET() {
debugMode: userSettings.debugMode,
autoConnect: userSettings.autoConnect,
autoFillEnvVars: userSettings.autoFillEnvVars,
autoPan: userSettings.autoPan,
telemetryEnabled: userSettings.telemetryEnabled,
telemetryNotifiedUser: userSettings.telemetryNotifiedUser,
emailPreferences: userSettings.emailPreferences ?? {},
Expand Down
12 changes: 2 additions & 10 deletions apps/sim/app/api/workflows/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,7 @@ export async function POST(req: NextRequest) {
},
},
outputs: {
response: {
type: {
input: 'any',
},
},
input: 'any',
},
enabled: true,
horizontalHandles: true,
Expand Down Expand Up @@ -248,11 +244,7 @@ export async function POST(req: NextRequest) {
},
},
outputs: {
response: {
type: {
input: 'any',
},
},
input: 'any',
},
createdAt: now,
updatedAt: now,
Expand Down
8 changes: 2 additions & 6 deletions apps/sim/app/api/workspaces/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ async function createWorkspace(userId: string, name: string) {
},
},
outputs: {
response: { type: { input: 'any' } },
input: 'any',
},
enabled: true,
horizontalHandles: true,
Expand Down Expand Up @@ -230,11 +230,7 @@ async function createWorkspace(userId: string, name: string) {
},
},
outputs: {
response: {
type: {
input: 'any',
},
},
input: 'any',
},
createdAt: now,
updatedAt: now,
Expand Down
13 changes: 4 additions & 9 deletions apps/sim/app/w/[id]/components/panel/components/chat/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -258,17 +258,12 @@ export function Chat({ panelWidth, chatMessage, setChatMessage }: ChatProps) {
if (typeof output === 'string') {
content = output
} else if (output && typeof output === 'object') {
// Handle cases where output is { response: ... }
// Handle flattened output structure
const outputObj = output as Record<string, any>
const response = outputObj.response
if (response) {
if (typeof response.content === 'string') {
content = response.content
} else {
// Pretty print for better readability
content = `\`\`\`json\n${JSON.stringify(response, null, 2)}\n\`\`\``
}
if (typeof outputObj.content === 'string') {
content = outputObj.content
} else {
// Pretty print for better readability
content = `\`\`\`json\n${JSON.stringify(output, null, 2)}\n\`\`\``
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,41 @@ export function OutputSelect({
const addOutput = (path: string, outputObj: any, prefix = '') => {
const fullPath = prefix ? `${prefix}.${path}` : path

if (typeof outputObj === 'object' && outputObj !== null) {
// For objects, recursively add each property
// If not an object or is null, treat as leaf node
if (typeof outputObj !== 'object' || outputObj === null) {
const output = {
id: `${block.id}_${fullPath}`,
label: `${blockName}.${fullPath}`,
blockId: block.id,
blockName: block.name || `Block ${block.id}`,
blockType: block.type,
path: fullPath,
}
outputs.push(output)
return
}

// If has 'type' property, treat as schema definition (leaf node)
if ('type' in outputObj && typeof outputObj.type === 'string') {
const output = {
id: `${block.id}_${fullPath}`,
label: `${blockName}.${fullPath}`,
blockId: block.id,
blockName: block.name || `Block ${block.id}`,
blockType: block.type,
path: fullPath,
}
outputs.push(output)
return
}

// For objects without type, recursively add each property
if (!Array.isArray(outputObj)) {
Object.entries(outputObj).forEach(([key, value]) => {
addOutput(key, value, fullPath)
})
} else {
// Add leaf node as output option
// For arrays, treat as leaf node
outputs.push({
id: `${block.id}_${fullPath}`,
label: `${blockName}.${fullPath}`,
Expand All @@ -71,10 +99,10 @@ export function OutputSelect({
}
}

// Start with the response object
if (block.outputs.response) {
addOutput('response', block.outputs.response)
}
// Process all output properties directly (flattened structure)
Object.entries(block.outputs).forEach(([key, value]) => {
addOutput(key, value)
})
}
})

Expand Down
Loading