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
33 changes: 23 additions & 10 deletions apps/sim/app/api/proxy/video/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,18 +807,31 @@ async function generateWithFalAI(
// Build request body based on model requirements
const requestBody: any = { prompt }

// Format duration based on model requirements
const formattedDuration = formatDuration(model, duration)
if (formattedDuration !== undefined) {
requestBody.duration = formattedDuration
}
// Models that support duration and aspect_ratio parameters
const supportsStandardParams = [
'kling-2.5-turbo-pro',
'kling-2.1-pro',
'minimax-hailuo-2.3-pro',
'minimax-hailuo-2.3-standard',
]

// Models that only need prompt (minimal params)
const minimalParamModels = ['ltxv-0.9.8', 'wan-2.1', 'veo-3.1', 'sora-2']

if (supportsStandardParams.includes(model)) {
// Kling and MiniMax models support duration and aspect_ratio
const formattedDuration = formatDuration(model, duration)
if (formattedDuration !== undefined) {
requestBody.duration = formattedDuration
}

if (aspectRatio) {
requestBody.aspect_ratio = aspectRatio
}
if (aspectRatio) {
requestBody.aspect_ratio = aspectRatio
}

if (resolution) {
requestBody.resolution = resolution
if (resolution) {
requestBody.resolution = resolution
}
}

// MiniMax models support prompt optimizer
Expand Down
83 changes: 40 additions & 43 deletions apps/sim/app/api/templates/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const CreateTemplateSchema = z.object({
about: z.string().optional(), // Markdown long description
})
.optional(),
creatorId: z.string().optional(), // Creator profile ID
creatorId: z.string().min(1, 'Creator profile is required'),
tags: z.array(z.string()).max(10, 'Maximum 10 tags allowed').optional().default([]),
})

Expand Down Expand Up @@ -204,50 +204,47 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
}

// Validate creator profile if provided
if (data.creatorId) {
// Verify the creator profile exists and user has access
const creatorProfile = await db
.select()
.from(templateCreators)
.where(eq(templateCreators.id, data.creatorId))
.limit(1)
// Validate creator profile - required for all templates
const creatorProfile = await db
.select()
.from(templateCreators)
.where(eq(templateCreators.id, data.creatorId))
.limit(1)

if (creatorProfile.length === 0) {
logger.warn(`[${requestId}] Creator profile not found: ${data.creatorId}`)
return NextResponse.json({ error: 'Creator profile not found' }, { status: 404 })
}

const creator = creatorProfile[0]

if (creatorProfile.length === 0) {
logger.warn(`[${requestId}] Creator profile not found: ${data.creatorId}`)
return NextResponse.json({ error: 'Creator profile not found' }, { status: 404 })
// Verify user has permission to use this creator profile
if (creator.referenceType === 'user') {
if (creator.referenceId !== session.user.id) {
logger.warn(`[${requestId}] User cannot use creator profile: ${data.creatorId}`)
return NextResponse.json(
{ error: 'You do not have permission to use this creator profile' },
{ status: 403 }
)
}
} else if (creator.referenceType === 'organization') {
// Verify user is a member of the organization
const membership = await db
.select()
.from(member)
.where(
and(eq(member.userId, session.user.id), eq(member.organizationId, creator.referenceId))
)
.limit(1)

const creator = creatorProfile[0]

// Verify user has permission to use this creator profile
if (creator.referenceType === 'user') {
if (creator.referenceId !== session.user.id) {
logger.warn(`[${requestId}] User cannot use creator profile: ${data.creatorId}`)
return NextResponse.json(
{ error: 'You do not have permission to use this creator profile' },
{ status: 403 }
)
}
} else if (creator.referenceType === 'organization') {
// Verify user is a member of the organization
const membership = await db
.select()
.from(member)
.where(
and(eq(member.userId, session.user.id), eq(member.organizationId, creator.referenceId))
)
.limit(1)

if (membership.length === 0) {
logger.warn(
`[${requestId}] User not a member of organization for creator: ${data.creatorId}`
)
return NextResponse.json(
{ error: 'You must be a member of the organization to use its creator profile' },
{ status: 403 }
)
}
if (membership.length === 0) {
logger.warn(
`[${requestId}] User not a member of organization for creator: ${data.creatorId}`
)
return NextResponse.json(
{ error: 'You must be a member of the organization to use its creator profile' },
{ status: 403 }
)
}
}

Expand Down Expand Up @@ -307,7 +304,7 @@ export async function POST(request: NextRequest) {
workflowId: data.workflowId,
name: data.name,
details: data.details || null,
creatorId: data.creatorId || null,
creatorId: data.creatorId,
views: 0,
stars: 0,
status: 'pending' as const, // All new templates start as pending
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ export function TemplateDeploy({

const isSubmitting = createMutation.isPending || updateMutation.isPending
const isFormValid =
formData.name.trim().length > 0 && formData.name.length <= 100 && formData.tagline.length <= 200
formData.name.trim().length > 0 &&
formData.name.length <= 100 &&
formData.tagline.length <= 200 &&
formData.creatorId.length > 0

const updateField = <K extends keyof TemplateFormData>(field: K, value: TemplateFormData[K]) => {
setFormData((prev) => ({ ...prev, [field]: value }))
Expand Down Expand Up @@ -201,7 +204,7 @@ export function TemplateDeploy({
tagline: formData.tagline.trim(),
about: formData.about.trim(),
},
creatorId: formData.creatorId || undefined,
creatorId: formData.creatorId,
tags: formData.tags,
}

Expand Down Expand Up @@ -285,7 +288,7 @@ export function TemplateDeploy({

<div>
<Label className='mb-[6.5px] block pl-[2px] font-medium text-[13px] text-[var(--text-primary)]'>
Name
Name <span className='text-[var(--text-error)]'>*</span>
</Label>
<Input
placeholder='Deep Research Agent'
Expand Down Expand Up @@ -323,29 +326,34 @@ export function TemplateDeploy({

<div>
<Label className='mb-[6.5px] block pl-[2px] font-medium text-[13px] text-[var(--text-primary)]'>
Creator
Creator <span className='text-[var(--text-error)]'>*</span>
</Label>
{creatorOptions.length === 0 && !loadingCreators ? (
<Button
type='button'
variant='primary'
onClick={() => {
try {
const event = new CustomEvent('open-settings', {
detail: { tab: 'template-profile' },
})
window.dispatchEvent(event)
logger.info('Opened Settings modal at template-profile section')
} catch (error) {
logger.error('Failed to open Settings modal for template profile', {
error,
})
}
}}
className='gap-[8px]'
>
<span>Create Template Profile</span>
</Button>
<div className='space-y-[8px]'>
<p className='text-[12px] text-[var(--text-tertiary)]'>
A creator profile is required to publish templates.
</p>
<Button
type='button'
variant='primary'
onClick={() => {
try {
const event = new CustomEvent('open-settings', {
detail: { tab: 'template-profile' },
})
window.dispatchEvent(event)
logger.info('Opened Settings modal at template-profile section')
} catch (error) {
logger.error('Failed to open Settings modal for template profile', {
error,
})
}
}}
className='gap-[8px]'
>
<span>Create Template Profile</span>
</Button>
</div>
) : (
<Combobox
options={creatorOptions.map((option) => ({
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/blocks/blocks/grafana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
id: 'annotationDashboardUid',
title: 'Dashboard UID',
type: 'short-input',
placeholder: 'Optional - attach to specific dashboard',
placeholder: 'Enter dashboard UID',
required: true,
condition: {
field: 'operation',
value: ['grafana_create_annotation', 'grafana_list_annotations'],
Expand Down
45 changes: 45 additions & 0 deletions apps/sim/blocks/blocks/video_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,29 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
required: false,
},

// Duration selection - Fal.ai (only for Kling and MiniMax models)
{
id: 'duration',
title: 'Duration (seconds)',
type: 'dropdown',
condition: {
field: 'model',
value: [
'kling-2.5-turbo-pro',
'kling-2.1-pro',
'minimax-hailuo-2.3-pro',
'minimax-hailuo-2.3-standard',
],
},
options: [
{ label: '5', id: '5' },
{ label: '8', id: '8' },
{ label: '10', id: '10' },
],
value: () => '5',
required: false,
},

// Aspect ratio selection - Veo (only 16:9 and 9:16)
{
id: 'aspectRatio',
Expand Down Expand Up @@ -213,6 +236,28 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
required: false,
},

// Aspect ratio selection - Fal.ai (only for Kling and MiniMax models)
{
id: 'aspectRatio',
title: 'Aspect Ratio',
type: 'dropdown',
condition: {
field: 'model',
value: [
'kling-2.5-turbo-pro',
'kling-2.1-pro',
'minimax-hailuo-2.3-pro',
'minimax-hailuo-2.3-standard',
],
},
options: [
{ label: '16:9', id: '16:9' },
{ label: '9:16', id: '9:16' },
],
value: () => '16:9',
required: false,
},

// Note: MiniMax aspect ratio is fixed at 16:9 (not configurable)

// Note: Runway Gen-4 Turbo outputs at 720p natively (no resolution selector needed)
Expand Down
20 changes: 10 additions & 10 deletions apps/sim/blocks/blocks/zep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,26 +276,26 @@ export const ZepBlock: BlockConfig<ZepResponse> = {
metadata: { type: 'json', description: 'User metadata' },
},
outputs: {
// Thread operations
threadId: { type: 'string', description: 'Thread identifier' },
userId: { type: 'string', description: 'User identifier' },
uuid: { type: 'string', description: 'Internal UUID' },
createdAt: { type: 'string', description: 'Creation timestamp' },
updatedAt: { type: 'string', description: 'Update timestamp' },
threads: { type: 'json', description: 'Array of threads' },
deleted: { type: 'boolean', description: 'Deletion status' },
// Message operations
messages: { type: 'json', description: 'Message data' },
messageIds: { type: 'json', description: 'Message identifiers' },
context: { type: 'string', description: 'User context string' },
facts: { type: 'json', description: 'Extracted facts' },
entities: { type: 'json', description: 'Extracted entities' },
summary: { type: 'string', description: 'Conversation summary' },
batchId: { type: 'string', description: 'Batch operation ID' },
messageIds: { type: 'json', description: 'Array of added message UUIDs' },
added: { type: 'boolean', description: 'Whether messages were added successfully' },
// Context operations
context: { type: 'string', description: 'User context string (summary or basic mode)' },
// User operations
userId: { type: 'string', description: 'User identifier' },
email: { type: 'string', description: 'User email' },
firstName: { type: 'string', description: 'User first name' },
lastName: { type: 'string', description: 'User last name' },
metadata: { type: 'json', description: 'User metadata' },
responseCount: { type: 'number', description: 'Number of items in response' },
totalCount: { type: 'number', description: 'Total number of items available' },
rowCount: { type: 'number', description: 'Number of rows in response' },
// Counts
totalCount: { type: 'number', description: 'Total number of items returned' },
},
}
5 changes: 2 additions & 3 deletions apps/sim/tools/grafana/create_annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ export const createAnnotationTool: ToolConfig<
},
dashboardUid: {
type: 'string',
required: false,
required: true,
visibility: 'user-or-llm',
description:
'UID of the dashboard to add the annotation to (optional for global annotations)',
description: 'UID of the dashboard to add the annotation to',
},
panelId: {
type: 'number',
Expand Down
40 changes: 40 additions & 0 deletions apps/sim/tools/grafana/create_folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,45 @@ export const createFolderTool: ToolConfig<GrafanaCreateFolderParams, GrafanaCrea
type: 'string',
description: 'The URL path to the folder',
},
hasAcl: {
type: 'boolean',
description: 'Whether the folder has custom ACL permissions',
},
canSave: {
type: 'boolean',
description: 'Whether the current user can save the folder',
},
canEdit: {
type: 'boolean',
description: 'Whether the current user can edit the folder',
},
canAdmin: {
type: 'boolean',
description: 'Whether the current user has admin rights on the folder',
},
canDelete: {
type: 'boolean',
description: 'Whether the current user can delete the folder',
},
createdBy: {
type: 'string',
description: 'Username of who created the folder',
},
created: {
type: 'string',
description: 'Timestamp when the folder was created',
},
updatedBy: {
type: 'string',
description: 'Username of who last updated the folder',
},
updated: {
type: 'string',
description: 'Timestamp when the folder was last updated',
},
version: {
type: 'number',
description: 'Version number of the folder',
},
},
}
Loading