mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
* Improve * Hide is hosted * Remove hardcoded * fix * Fixes * v0 * Fix bugs * Restore settings * Handle compaction event type * Add keepalive * File streaming * Error tags * Abort defense * Edit hashes * DB backed tools * Fixes * progress on autolayout improvements * Abort fixes * vertical insertion improvement * Consolidate file attachments * Fix lint * Manage agent result card fix * Remove hardcoded ff * Fix file streaming * Fix persisted writing file tab * Fix lint * Fix streaming file flash * Always set url to /file on file view * Edit perms for tables * Fix file edit perms * remove inline tool call json dump * Enforce name uniqueness (#3679) * Enforce name uniqueness * Use established pattern for error handling * Fix lint * Fix lint * Add kb name uniqueness to db * Fix lint * Handle name getting taken before restore * Enforce duplicate file name * Fix lint --------- Co-authored-by: Theodore Li <theo@sim.ai> * fix temp file creation * fix types * Streaming fixes * type xml tag structures + return invalid id linter errors back to LLM * Add image gen and viz tools * Tags * Workflow tags * Fix lint * Fix subagent abort * Fix subagent persistence * Fix subagent aborts * Nuke db migs * Re add db migrations * Fix lint --------- Co-authored-by: Theodore Li <teddy@zenobiapay.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> Co-authored-by: Theodore Li <theodoreqili@gmail.com> Co-authored-by: Theodore Li <theo@sim.ai>
124 lines
4.0 KiB
TypeScript
124 lines
4.0 KiB
TypeScript
import { createLogger } from '@sim/logger'
|
|
import { type NextRequest, NextResponse } from 'next/server'
|
|
import { z } from 'zod'
|
|
import { COPILOT_REQUEST_MODES } from '@/lib/copilot/models'
|
|
import { orchestrateCopilotStream } from '@/lib/copilot/orchestrator'
|
|
import { getWorkflowById, resolveWorkflowIdForUser } from '@/lib/workflows/utils'
|
|
import { authenticateV1Request } from '@/app/api/v1/auth'
|
|
|
|
export const maxDuration = 3600
|
|
|
|
const logger = createLogger('CopilotHeadlessAPI')
|
|
const DEFAULT_COPILOT_MODEL = 'claude-opus-4-6'
|
|
|
|
const RequestSchema = z.object({
|
|
message: z.string().min(1, 'message is required'),
|
|
workflowId: z.string().optional(),
|
|
workflowName: z.string().optional(),
|
|
chatId: z.string().optional(),
|
|
mode: z.enum(COPILOT_REQUEST_MODES).optional().default('agent'),
|
|
model: z.string().optional(),
|
|
autoExecuteTools: z.boolean().optional().default(true),
|
|
timeout: z.number().optional().default(3_600_000),
|
|
})
|
|
|
|
/**
|
|
* POST /api/v1/copilot/chat
|
|
* Headless copilot endpoint for server-side orchestration.
|
|
*
|
|
* workflowId is optional - if not provided:
|
|
* - If workflowName is provided, finds that workflow
|
|
* - Otherwise uses the user's first workflow as context
|
|
* - The copilot can still operate on any workflow using list_user_workflows
|
|
*/
|
|
export async function POST(req: NextRequest) {
|
|
const auth = await authenticateV1Request(req)
|
|
if (!auth.authenticated || !auth.userId) {
|
|
return NextResponse.json(
|
|
{ success: false, error: auth.error || 'Unauthorized' },
|
|
{ status: 401 }
|
|
)
|
|
}
|
|
|
|
try {
|
|
const body = await req.json()
|
|
const parsed = RequestSchema.parse(body)
|
|
const selectedModel = parsed.model || DEFAULT_COPILOT_MODEL
|
|
|
|
// Resolve workflow ID
|
|
const resolved = await resolveWorkflowIdForUser(
|
|
auth.userId,
|
|
parsed.workflowId,
|
|
parsed.workflowName,
|
|
auth.keyType === 'workspace' ? auth.workspaceId : undefined
|
|
)
|
|
if (!resolved) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: 'No workflows found. Create a workflow first or provide a valid workflowId.',
|
|
},
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
if (auth.keyType === 'workspace' && auth.workspaceId) {
|
|
const workflow = await getWorkflowById(resolved.workflowId)
|
|
if (!workflow?.workspaceId || workflow.workspaceId !== auth.workspaceId) {
|
|
return NextResponse.json(
|
|
{ success: false, error: 'API key is not authorized for this workspace' },
|
|
{ status: 403 }
|
|
)
|
|
}
|
|
}
|
|
|
|
// Transform mode to transport mode (same as client API)
|
|
// build and agent both map to 'agent' on the backend
|
|
const effectiveMode = parsed.mode === 'agent' ? 'build' : parsed.mode
|
|
const transportMode = effectiveMode === 'build' ? 'agent' : effectiveMode
|
|
|
|
// Always generate a chatId - required for artifacts system to work with subagents
|
|
const chatId = parsed.chatId || crypto.randomUUID()
|
|
|
|
const requestPayload = {
|
|
message: parsed.message,
|
|
workflowId: resolved.workflowId,
|
|
userId: auth.userId,
|
|
model: selectedModel,
|
|
mode: transportMode,
|
|
messageId: crypto.randomUUID(),
|
|
chatId,
|
|
}
|
|
|
|
const result = await orchestrateCopilotStream(requestPayload, {
|
|
userId: auth.userId,
|
|
workflowId: resolved.workflowId,
|
|
chatId,
|
|
goRoute: '/api/mcp',
|
|
autoExecuteTools: parsed.autoExecuteTools,
|
|
timeout: parsed.timeout,
|
|
interactive: false,
|
|
})
|
|
|
|
return NextResponse.json({
|
|
success: result.success,
|
|
content: result.content,
|
|
toolCalls: result.toolCalls,
|
|
chatId: result.chatId || chatId,
|
|
error: result.error,
|
|
})
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return NextResponse.json(
|
|
{ success: false, error: 'Invalid request', details: error.errors },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
logger.error('Headless copilot request failed', {
|
|
error: error instanceof Error ? error.message : String(error),
|
|
})
|
|
return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
|
|
}
|
|
}
|