mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
* feat(logs): add additional metadata for workflow execution logs
* Revert "Feat(logs) upgrade mothership chat messages to error (#3772)"
This reverts commit 9d1b9763c5.
* Fix lint, address greptile comments
* improvement(sidebar): expand sidebar by hovering and clicking the edge (#3830)
* improvement(sidebar): expand sidebar by hovering and clicking the edge
* improvement(sidebar): add keyboard shortcuts for new workflow/task, center search modal, fix edge ARIA
* improvement(sidebar): use Tooltip.Shortcut for inline shortcut display
* fix(sidebar): change new workflow shortcut from Mod+Shift+W to Mod+Shift+P to avoid browser close-window conflict
* fix(hotkeys): fall back to event.code for international keyboard layout compatibility
* fix(sidebar): guard add-workflow shortcut with canEdit and isCreatingWorkflow checks
* feat(ui): handle image paste (#3826)
* feat(ui): handle image paste
* Fix lint
* Fix type error
---------
Co-authored-by: Theodore Li <theo@sim.ai>
* feat(files): interactive markdown checkbox toggling in preview (#3829)
* feat(files): interactive markdown checkbox toggling in preview
* fix(files): handle ordered-list checkboxes and fix index drift
* lint
* fix(files): remove counter offset that prevented checkbox toggling
* fix(files): apply task-list styling to ordered lists too
* fix(files): render single pass when interactive to avoid index drift
* fix(files): move useMemo above conditional return to fix Rules of Hooks
* fix(files): pass content directly to preview when not streaming to avoid stale frame
* improvement(home): position @ mention popup at caret and fix icon consistency (#3831)
* improvement(home): position @ mention popup at caret and fix icon consistency
* fix(home): pin mirror div to document origin and guard button anchor
* chore(auth): restore hybrid.ts to staging
* improvement(ui): sidebar (#3832)
* Fix logger tests
* Add metadata to mothership logs
---------
Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: Waleed <walif6@gmail.com>
Co-authored-by: Theodore Li <theo@sim.ai>
148 lines
4.5 KiB
TypeScript
148 lines
4.5 KiB
TypeScript
import { createLogger } from '@sim/logger'
|
|
import { type NextRequest, NextResponse } from 'next/server'
|
|
import { z } from 'zod'
|
|
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
|
import { createRunSegment } from '@/lib/copilot/async-runs/repository'
|
|
import { buildIntegrationToolSchemas } from '@/lib/copilot/chat-payload'
|
|
import { orchestrateCopilotStream } from '@/lib/copilot/orchestrator'
|
|
import { generateWorkspaceContext } from '@/lib/copilot/workspace-context'
|
|
import {
|
|
assertActiveWorkspaceAccess,
|
|
getUserEntityPermissions,
|
|
} from '@/lib/workspaces/permissions/utils'
|
|
|
|
export const maxDuration = 3600
|
|
|
|
const logger = createLogger('MothershipExecuteAPI')
|
|
|
|
const MessageSchema = z.object({
|
|
role: z.enum(['system', 'user', 'assistant']),
|
|
content: z.string(),
|
|
})
|
|
|
|
const ExecuteRequestSchema = z.object({
|
|
messages: z.array(MessageSchema).min(1, 'At least one message is required'),
|
|
responseFormat: z.any().optional(),
|
|
workspaceId: z.string().min(1, 'workspaceId is required'),
|
|
userId: z.string().min(1, 'userId is required'),
|
|
chatId: z.string().optional(),
|
|
})
|
|
|
|
/**
|
|
* POST /api/mothership/execute
|
|
*
|
|
* Non-streaming endpoint for Mothership block execution within workflows.
|
|
* Called by the executor via internal JWT auth, not by the browser directly.
|
|
* Consumes the Go SSE stream internally and returns a single JSON response.
|
|
*/
|
|
export async function POST(req: NextRequest) {
|
|
let messageId: string | undefined
|
|
|
|
try {
|
|
const auth = await checkInternalAuth(req, { requireWorkflowId: false })
|
|
if (!auth.success) {
|
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
const body = await req.json()
|
|
const { messages, responseFormat, workspaceId, userId, chatId } =
|
|
ExecuteRequestSchema.parse(body)
|
|
|
|
await assertActiveWorkspaceAccess(workspaceId, userId)
|
|
|
|
const effectiveChatId = chatId || crypto.randomUUID()
|
|
messageId = crypto.randomUUID()
|
|
const reqLogger = logger.withMetadata({ messageId })
|
|
const [workspaceContext, integrationTools, userPermission] = await Promise.all([
|
|
generateWorkspaceContext(workspaceId, userId),
|
|
buildIntegrationToolSchemas(userId, messageId),
|
|
getUserEntityPermissions(userId, 'workspace', workspaceId).catch(() => null),
|
|
])
|
|
|
|
const requestPayload: Record<string, unknown> = {
|
|
messages,
|
|
responseFormat,
|
|
userId,
|
|
chatId: effectiveChatId,
|
|
mode: 'agent',
|
|
messageId,
|
|
isHosted: true,
|
|
workspaceContext,
|
|
...(integrationTools.length > 0 ? { integrationTools } : {}),
|
|
...(userPermission ? { userPermission } : {}),
|
|
}
|
|
|
|
const executionId = crypto.randomUUID()
|
|
const runId = crypto.randomUUID()
|
|
|
|
await createRunSegment({
|
|
id: runId,
|
|
executionId,
|
|
chatId: effectiveChatId,
|
|
userId,
|
|
workspaceId,
|
|
streamId: messageId,
|
|
}).catch(() => {})
|
|
|
|
const result = await orchestrateCopilotStream(requestPayload, {
|
|
userId,
|
|
workspaceId,
|
|
chatId: effectiveChatId,
|
|
executionId,
|
|
runId,
|
|
goRoute: '/api/mothership/execute',
|
|
autoExecuteTools: true,
|
|
interactive: false,
|
|
})
|
|
|
|
if (!result.success) {
|
|
reqLogger.error('Mothership execute failed', {
|
|
error: result.error,
|
|
errors: result.errors,
|
|
})
|
|
return NextResponse.json(
|
|
{
|
|
error: result.error || 'Mothership execution failed',
|
|
content: result.content || '',
|
|
},
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
|
|
const clientToolNames = new Set(integrationTools.map((t) => t.name))
|
|
const clientToolCalls = (result.toolCalls || []).filter(
|
|
(tc: { name: string }) => clientToolNames.has(tc.name) || tc.name.startsWith('mcp-')
|
|
)
|
|
|
|
return NextResponse.json({
|
|
content: result.content,
|
|
model: 'mothership',
|
|
tokens: result.usage
|
|
? {
|
|
prompt: result.usage.prompt,
|
|
completion: result.usage.completion,
|
|
total: (result.usage.prompt || 0) + (result.usage.completion || 0),
|
|
}
|
|
: {},
|
|
cost: result.cost || undefined,
|
|
toolCalls: clientToolCalls,
|
|
})
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return NextResponse.json(
|
|
{ error: 'Invalid request data', details: error.errors },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
logger.withMetadata({ messageId }).error('Mothership execute error', {
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
})
|
|
|
|
return NextResponse.json(
|
|
{ error: error instanceof Error ? error.message : 'Internal server error' },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|