Skip to content
Merged
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
4 changes: 2 additions & 2 deletions apps/docs/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5127,11 +5127,11 @@ export function SimilarwebIcon(props: SVGProps<SVGSVGElement>) {
<path
d='M22.099 5.781c-1.283 -2 -3.14 -3.67 -5.27 -4.52l-0.63 -0.213a7.433 7.433 0 0 0 -2.15 -0.331c-2.307 0.01 -4.175 1.92 -4.175 4.275a4.3 4.3 0 0 0 0.867 2.602l-0.26 -0.342c0.124 0.186 0.26 0.37 0.417 0.556 0.663 0.802 1.604 1.635 2.822 2.58 2.999 2.32 4.943 4.378 5.104 6.93 0.038 0.344 0.062 0.696 0.062 1.051 0 1.297 -0.283 2.67 -0.764 3.635h0.005s-0.207 0.377 -0.077 0.487c0.066 0.057 0.21 0.1 0.46 -0.053a12.104 12.104 0 0 0 3.4 -3.33 12.111 12.111 0 0 0 2.088 -6.635 12.098 12.098 0 0 0 -1.9 -6.692zm-9.096 8.718 -1.878 -1.55c-3.934 -2.87 -5.98 -5.966 -4.859 -9.783a8.73 8.73 0 0 1 0.37 -1.016v-0.004s0.278 -0.583 -0.327 -0.295a12.067 12.067 0 0 0 -6.292 9.975 12.11 12.11 0 0 0 2.053 7.421 9.394 9.394 0 0 0 2.154 2.168H4.22c4.148 3.053 7.706 1.446 7.706 1.446h0.003a4.847 4.847 0 0 0 2.962 -4.492 4.855 4.855 0 0 0 -1.889 -3.87z'
fill='currentColor'
/>
/>
</svg>
)
}

export function CalComIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5131,7 +5131,7 @@ export function SimilarwebIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}

export function CalComIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
Expand Down
43 changes: 23 additions & 20 deletions apps/sim/hooks/queries/deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,28 +421,27 @@ interface GenerateVersionDescriptionVariables {
onStreamChunk?: (accumulated: string) => void
}

const VERSION_DESCRIPTION_SYSTEM_PROMPT = `You are a technical writer generating concise deployment version descriptions.
const VERSION_DESCRIPTION_SYSTEM_PROMPT = `You are writing deployment version descriptions for a workflow automation platform.
Given a diff of changes between two workflow versions, write a brief, factual description (1-2 sentences, under 300 characters) that states ONLY what changed.
Write a brief, factual description (1-3 sentences, under 400 characters) that states what changed between versions.
RULES:
- State specific values when provided (e.g. "model changed from X to Y")
- Do NOT wrap your response in quotes
- Do NOT add filler phrases like "streamlining the workflow", "for improved efficiency"
- Do NOT use markdown formatting
- Do NOT include version numbers
- Do NOT start with "This version" or similar phrases
Guidelines:
- Use the specific values provided (credential names, channel names, model names)
- Be precise: "Changes Slack channel from #general to #alerts" not "Updates channel configuration"
- Combine related changes: "Updates Agent model to claude-sonnet-4-5 and increases temperature to 0.8"
- For added/removed blocks, mention their purpose if clear from the type
Good examples:
- Changes model in Agent 1 from gpt-4o to claude-sonnet-4-20250514.
- Adds Slack notification block. Updates webhook URL to production endpoint.
- Removes Function block and its connection to Router.
Format rules:
- Plain text only, no quotes around the response
- No markdown formatting
- No filler phrases ("for improved efficiency", "streamlining the workflow")
- No version numbers or "This version" prefixes
Bad examples:
- "Changes model..." (NO - don't wrap in quotes)
- Changes model, streamlining the workflow. (NO - don't add filler)
Respond with ONLY the plain text description.`
Examples:
- Switches Agent model from gpt-4o to claude-sonnet-4-5. Changes Slack credential to Production OAuth.
- Adds Gmail notification block for sending alerts. Removes unused Function block. Updates Router conditions.
- Updates system prompt for more concise responses. Reduces temperature from 0.7 to 0.3.
- Connects Slack block to Router. Adds 2 new workflow connections. Configures error handling path.`

/**
* Hook for generating a version description using AI based on workflow diff
Expand All @@ -454,7 +453,7 @@ export function useGenerateVersionDescription() {
version,
onStreamChunk,
}: GenerateVersionDescriptionVariables): Promise<string> => {
const { generateWorkflowDiffSummary, formatDiffSummaryForDescription } = await import(
const { generateWorkflowDiffSummary, formatDiffSummaryForDescriptionAsync } = await import(
'@/lib/workflows/comparison/compare'
)

Expand All @@ -470,7 +469,11 @@ export function useGenerateVersionDescription() {
}

const diffSummary = generateWorkflowDiffSummary(currentState, previousState)
const diffText = formatDiffSummaryForDescription(diffSummary)
const diffText = await formatDiffSummaryForDescriptionAsync(
diffSummary,
currentState,
workflowId
)

const wandResponse = await fetch('/api/wand', {
method: 'POST',
Expand Down
144 changes: 131 additions & 13 deletions apps/sim/lib/workflows/comparison/compare.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createLogger } from '@sim/logger'
import type { WorkflowState } from '@/stores/workflows/workflow/types'
import {
extractBlockFieldsForComparison,
Expand All @@ -12,6 +13,9 @@ import {
normalizeVariables,
sanitizeVariable,
} from './normalize'
import { formatValueForDisplay, resolveValueForDisplay } from './resolve-values'

const logger = createLogger('WorkflowComparison')

/**
* Compare the current workflow state with the deployed state to detect meaningful changes.
Expand Down Expand Up @@ -318,19 +322,6 @@ export function generateWorkflowDiffSummary(
return result
}

function formatValueForDisplay(value: unknown): string {
if (value === null || value === undefined) return '(none)'
if (typeof value === 'string') {
if (value.length > 50) return `${value.slice(0, 50)}...`
return value || '(empty)'
}
if (typeof value === 'boolean') return value ? 'enabled' : 'disabled'
if (typeof value === 'number') return String(value)
if (Array.isArray(value)) return `[${value.length} items]`
if (typeof value === 'object') return `${JSON.stringify(value).slice(0, 50)}...`
return String(value)
}

/**
* Convert a WorkflowDiffSummary to a human-readable string for AI description generation
*/
Expand Down Expand Up @@ -406,3 +397,130 @@ export function formatDiffSummaryForDescription(summary: WorkflowDiffSummary): s

return changes.join('\n')
}

/**
* Converts a WorkflowDiffSummary to a human-readable string with resolved display names.
* Resolves IDs (credentials, channels, workflows, etc.) to human-readable names using
* the selector registry infrastructure.
*
* @param summary - The diff summary to format
* @param currentState - The current workflow state for context extraction
* @param workflowId - The workflow ID for API calls
* @returns A formatted string describing the changes with resolved names
*/
export async function formatDiffSummaryForDescriptionAsync(
summary: WorkflowDiffSummary,
currentState: WorkflowState,
workflowId: string
): Promise<string> {
if (!summary.hasChanges) {
return 'No structural changes detected (configuration may have changed)'
}

const changes: string[] = []

for (const block of summary.addedBlocks) {
const name = block.name || block.type
changes.push(`Added block: ${name} (${block.type})`)
}

for (const block of summary.removedBlocks) {
const name = block.name || block.type
changes.push(`Removed block: ${name} (${block.type})`)
}

const modifiedBlockPromises = summary.modifiedBlocks.map(async (block) => {
const name = block.name || block.type
const blockChanges: string[] = []

const changesToProcess = block.changes.slice(0, 3)
const resolvedChanges = await Promise.all(
changesToProcess.map(async (change) => {
const context = {
blockType: block.type,
subBlockId: change.field,
workflowId,
currentState,
blockId: block.id,
}

const [oldResolved, newResolved] = await Promise.all([
resolveValueForDisplay(change.oldValue, context),
resolveValueForDisplay(change.newValue, context),
])

return {
field: change.field,
oldLabel: oldResolved.displayLabel,
newLabel: newResolved.displayLabel,
}
})
)

for (const resolved of resolvedChanges) {
blockChanges.push(
`Modified ${name}: ${resolved.field} changed from "${resolved.oldLabel}" to "${resolved.newLabel}"`
)
}

if (block.changes.length > 3) {
blockChanges.push(` ...and ${block.changes.length - 3} more changes in ${name}`)
}

return blockChanges
})

const allModifiedBlockChanges = await Promise.all(modifiedBlockPromises)
for (const blockChanges of allModifiedBlockChanges) {
changes.push(...blockChanges)
}

if (summary.edgeChanges.added > 0) {
changes.push(`Added ${summary.edgeChanges.added} connection(s)`)
}
if (summary.edgeChanges.removed > 0) {
changes.push(`Removed ${summary.edgeChanges.removed} connection(s)`)
}

if (summary.loopChanges.added > 0) {
changes.push(`Added ${summary.loopChanges.added} loop(s)`)
}
if (summary.loopChanges.removed > 0) {
changes.push(`Removed ${summary.loopChanges.removed} loop(s)`)
}
if (summary.loopChanges.modified > 0) {
changes.push(`Modified ${summary.loopChanges.modified} loop(s)`)
}

if (summary.parallelChanges.added > 0) {
changes.push(`Added ${summary.parallelChanges.added} parallel group(s)`)
}
if (summary.parallelChanges.removed > 0) {
changes.push(`Removed ${summary.parallelChanges.removed} parallel group(s)`)
}
if (summary.parallelChanges.modified > 0) {
changes.push(`Modified ${summary.parallelChanges.modified} parallel group(s)`)
}

const varChanges: string[] = []
if (summary.variableChanges.added > 0) {
varChanges.push(`${summary.variableChanges.added} added`)
}
if (summary.variableChanges.removed > 0) {
varChanges.push(`${summary.variableChanges.removed} removed`)
}
if (summary.variableChanges.modified > 0) {
varChanges.push(`${summary.variableChanges.modified} modified`)
}
if (varChanges.length > 0) {
changes.push(`Variables: ${varChanges.join(', ')}`)
}

logger.info('Generated async diff description', {
workflowId,
changeCount: changes.length,
modifiedBlocks: summary.modifiedBlocks.length,
})

return changes.join('\n')
}
1 change: 1 addition & 0 deletions apps/sim/lib/workflows/comparison/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type { FieldChange, WorkflowDiffSummary } from './compare'
export {
formatDiffSummaryForDescription,
formatDiffSummaryForDescriptionAsync,
generateWorkflowDiffSummary,
hasWorkflowChanged,
} from './compare'
Expand Down
Loading