From 022a61b77a6ac4cf17badeb717bdd6726d67ae76 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Thu, 24 Jul 2025 12:51:24 -0700 Subject: [PATCH] improvement(cleanup): remove old logging code (#779) --- apps/sim/app/api/__test-utils__/utils.ts | 8 - apps/sim/app/api/logs/route.test.ts | 759 --- apps/sim/app/api/logs/route.ts | 246 - .../app/api/schedules/execute/route.test.ts | 6 - .../api/webhooks/trigger/[path]/route.test.ts | 5 - .../api/workflows/[id]/execute/route.test.ts | 5 - apps/sim/app/api/workflows/[id]/log/route.ts | 2 +- .../0062_previous_phantom_reporter.sql | 1 + .../sim/db/migrations/meta/0062_snapshot.json | 5529 +++++++++++++++++ apps/sim/db/migrations/meta/_journal.json | 7 + apps/sim/db/schema.ts | 24 - apps/sim/lib/logs/execution-logger.ts | 928 --- 12 files changed, 5538 insertions(+), 1982 deletions(-) delete mode 100644 apps/sim/app/api/logs/route.test.ts delete mode 100644 apps/sim/app/api/logs/route.ts create mode 100644 apps/sim/db/migrations/0062_previous_phantom_reporter.sql create mode 100644 apps/sim/db/migrations/meta/0062_snapshot.json delete mode 100644 apps/sim/lib/logs/execution-logger.ts diff --git a/apps/sim/app/api/__test-utils__/utils.ts b/apps/sim/app/api/__test-utils__/utils.ts index 29c0aa767..7e2b0e47d 100644 --- a/apps/sim/app/api/__test-utils__/utils.ts +++ b/apps/sim/app/api/__test-utils__/utils.ts @@ -279,11 +279,6 @@ export function mockExecutionDependencies() { } }) - vi.mock('@/lib/logs/execution-logger', () => ({ - persistExecutionLogs: vi.fn().mockResolvedValue(undefined), - persistExecutionError: vi.fn().mockResolvedValue(undefined), - })) - vi.mock('@/lib/logs/trace-spans', () => ({ buildTraceSpans: vi.fn().mockReturnValue({ traceSpans: [], @@ -380,7 +375,6 @@ export function mockWorkflowAccessValidation(shouldSucceed = true) { export async function getMockedDependencies() { const utilsModule = await import('@/lib/utils') - const logsModule = await import('@/lib/logs/execution-logger') const traceSpansModule = await import('@/lib/logs/trace-spans') const workflowUtilsModule = await import('@/lib/workflows/utils') const executorModule = await import('@/executor') @@ -389,8 +383,6 @@ export async function getMockedDependencies() { return { decryptSecret: utilsModule.decryptSecret, - persistExecutionLogs: logsModule.persistExecutionLogs, - persistExecutionError: logsModule.persistExecutionError, buildTraceSpans: traceSpansModule.buildTraceSpans, updateWorkflowRunCounts: workflowUtilsModule.updateWorkflowRunCounts, Executor: executorModule.Executor, diff --git a/apps/sim/app/api/logs/route.test.ts b/apps/sim/app/api/logs/route.test.ts deleted file mode 100644 index 8dbdd5c40..000000000 --- a/apps/sim/app/api/logs/route.test.ts +++ /dev/null @@ -1,759 +0,0 @@ -/** - * Tests for workflow logs API route - * - * @vitest-environment node - */ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -describe('Workflow Logs API Route', () => { - const mockWorkflowLogs = [ - { - id: 'log-1', - workflowId: 'workflow-1', - executionId: 'exec-1', - level: 'info', - message: 'Workflow started', - duration: '1.2s', - trigger: 'manual', - createdAt: new Date('2024-01-01T10:00:00.000Z'), - }, - { - id: 'log-2', - workflowId: 'workflow-1', - executionId: 'exec-1', - level: 'error', - message: 'API call failed', - duration: '0.5s', - trigger: 'manual', - createdAt: new Date('2024-01-01T10:01:00.000Z'), - }, - { - id: 'log-3', - workflowId: 'workflow-2', - executionId: 'exec-2', - level: 'info', - message: 'Task completed', - duration: '2.1s', - trigger: 'api', - createdAt: new Date('2024-01-01T10:02:00.000Z'), - }, - { - id: 'log-4', - workflowId: 'workflow-3', - executionId: 'exec-3', - level: 'info', - message: 'Root workflow executed', - duration: '0.8s', - trigger: 'webhook', - createdAt: new Date('2024-01-01T10:03:00.000Z'), - }, - ] - - const mockWorkflows = [ - { - id: 'workflow-1', - userId: 'user-123', - workspaceId: 'workspace-123', - folderId: 'folder-1', - name: 'Test Workflow 1', - color: '#3972F6', - description: 'First test workflow', - state: {}, - createdAt: new Date('2024-01-01T00:00:00.000Z'), - updatedAt: new Date('2024-01-01T00:00:00.000Z'), - }, - { - id: 'workflow-2', - userId: 'user-123', - workspaceId: 'workspace-123', - folderId: 'folder-2', - name: 'Test Workflow 2', - color: '#FF6B6B', - description: 'Second test workflow', - state: {}, - createdAt: new Date('2024-01-01T00:00:00.000Z'), - updatedAt: new Date('2024-01-01T00:00:00.000Z'), - }, - { - id: 'workflow-3', - userId: 'user-123', - workspaceId: 'workspace-123', - folderId: null, - name: 'Test Workflow 3', - color: '#22C55E', - description: 'Third test workflow (no folder)', - state: {}, - createdAt: new Date('2024-01-01T00:00:00.000Z'), - updatedAt: new Date('2024-01-01T00:00:00.000Z'), - }, - ] - - beforeEach(() => { - vi.resetModules() - vi.clearAllMocks() - - vi.stubGlobal('crypto', { - randomUUID: vi.fn().mockReturnValue('mock-request-id-12345678'), - }) - - vi.doMock('@/lib/logs/console-logger', () => ({ - createLogger: vi.fn().mockReturnValue({ - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }), - })) - - vi.doMock('@/lib/auth', () => ({ - getSession: vi.fn().mockResolvedValue({ - user: { id: 'user-123' }, - }), - })) - }) - - afterEach(() => { - vi.clearAllMocks() - }) - - function setupDatabaseMock({ - userWorkflows = mockWorkflows.filter((w) => w.userId === 'user-123'), - logs = mockWorkflowLogs, - workflows = mockWorkflows, - throwError = false, - } = {}) { - const createChainableMock = (data: any[]) => { - const mock = { - select: vi.fn().mockReturnThis(), - from: vi.fn().mockReturnThis(), - where: vi.fn().mockReturnThis(), - orderBy: vi.fn().mockReturnThis(), - limit: vi.fn().mockReturnThis(), - offset: vi.fn().mockReturnThis(), - then: vi.fn((resolve) => resolve(data)), - } - return mock - } - - let dbCallCount = 0 - - vi.doMock('@/db', () => ({ - db: { - select: vi.fn().mockImplementation((selection?: any) => { - if (throwError) { - throw new Error('Database connection failed') - } - - dbCallCount++ - - // First call: get user workflows - if (dbCallCount === 1) { - return createChainableMock( - userWorkflows.map((w) => ({ id: w.id, folderId: w.folderId })) - ) - } - - // Second call: get logs - if (dbCallCount === 2) { - return createChainableMock(logs) - } - - // Third call: get count - if (dbCallCount === 3) { - // If selection is provided and has count property, return count result - if (selection && Object.keys(selection).some((key) => key === 'count')) { - return createChainableMock([{ count: logs.length }]) - } - return createChainableMock([{ count: logs.length }]) - } - - // Fourth call: get workflows for includeWorkflow - if (dbCallCount === 4) { - return createChainableMock(workflows) - } - - return createChainableMock([]) - }), - }, - })) - - vi.doMock('drizzle-orm', () => ({ - eq: vi.fn().mockImplementation((field, value) => ({ type: 'eq', field, value })), - and: vi.fn().mockImplementation((...conditions) => ({ type: 'and', conditions })), - or: vi.fn().mockImplementation((...conditions) => ({ type: 'or', conditions })), - gte: vi.fn().mockImplementation((field, value) => ({ type: 'gte', field, value })), - lte: vi.fn().mockImplementation((field, value) => ({ type: 'lte', field, value })), - sql: vi.fn().mockImplementation((strings, ...values) => ({ - type: 'sql', - sql: strings, - values, - })), - })) - - vi.doMock('@/db/schema', () => ({ - workflow: { - id: 'workflow.id', - userId: 'workflow.userId', - workspaceId: 'workflow.workspaceId', - name: 'workflow.name', - color: 'workflow.color', - description: 'workflow.description', - }, - workflowLogs: { - id: 'workflowLogs.id', - workflowId: 'workflowLogs.workflowId', - level: 'workflowLogs.level', - trigger: 'workflowLogs.trigger', - createdAt: 'workflowLogs.createdAt', - message: 'workflowLogs.message', - executionId: 'workflowLogs.executionId', - }, - })) - } - - describe('GET /api/logs', () => { - it('should return logs successfully with default parameters', async () => { - setupDatabaseMock() - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data).toHaveProperty('data') - expect(data).toHaveProperty('total', 4) - expect(data).toHaveProperty('page', 1) - expect(data).toHaveProperty('pageSize', 100) - expect(data).toHaveProperty('totalPages', 1) - expect(Array.isArray(data.data)).toBe(true) - expect(data.data).toHaveLength(4) - }) - - it('should include workflow data when includeWorkflow=true', async () => { - setupDatabaseMock() - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&includeWorkflow=true' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data[0]).toHaveProperty('workflow') - expect(data.data[0].workflow).toHaveProperty('name') - expect(data.data[0].workflow).toHaveProperty('color') - }) - - it('should filter logs by level', async () => { - const errorLogs = mockWorkflowLogs.filter((log) => log.level === 'error') - setupDatabaseMock({ logs: errorLogs }) - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123&level=error') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(1) - expect(data.data[0].level).toBe('error') - }) - - it('should filter logs by specific workflow IDs', async () => { - const workflow1Logs = mockWorkflowLogs.filter((log) => log.workflowId === 'workflow-1') - setupDatabaseMock({ logs: workflow1Logs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&workflowIds=workflow-1' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(2) - expect(data.data.every((log: any) => log.workflowId === 'workflow-1')).toBe(true) - }) - - it('should filter logs by multiple workflow IDs', async () => { - // Only get logs for workflow-1 and workflow-2 (not workflow-3) - const filteredLogs = mockWorkflowLogs.filter( - (log) => log.workflowId === 'workflow-1' || log.workflowId === 'workflow-2' - ) - setupDatabaseMock({ logs: filteredLogs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&workflowIds=workflow-1,workflow-2' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(3) - }) - - it('should filter logs by date range', async () => { - const startDate = '2024-01-01T10:00:30.000Z' - const filteredLogs = mockWorkflowLogs.filter( - (log) => new Date(log.createdAt) >= new Date(startDate) - ) - setupDatabaseMock({ logs: filteredLogs }) - - const url = new URL( - `http://localhost:3000/api/logs?workspaceId=workspace-123&startDate=${startDate}` - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(filteredLogs.length) - }) - - it('should search logs by message content', async () => { - const searchLogs = mockWorkflowLogs.filter((log) => - log.message.toLowerCase().includes('failed') - ) - setupDatabaseMock({ logs: searchLogs }) - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123&search=failed') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(1) - expect(data.data[0].message).toContain('failed') - }) - - it('should handle pagination correctly', async () => { - const paginatedLogs = mockWorkflowLogs.slice(1, 3) - setupDatabaseMock({ logs: paginatedLogs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&limit=2&offset=1' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(2) - expect(data.page).toBe(1) - expect(data.pageSize).toBe(2) - expect(data.total).toBe(2) - expect(data.totalPages).toBe(1) - }) - - it('should return empty array when user has no workflows', async () => { - setupDatabaseMock({ userWorkflows: [], logs: [], workflows: [] }) - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toEqual([]) - expect(data.total).toBe(0) - }) - - it('should return 403 for unauthorized workflow access', async () => { - // Set up mock to simulate user not owning the requested workflow - setupDatabaseMock({ - userWorkflows: mockWorkflows.filter((w) => w.id !== 'unauthorized-workflow'), - }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&workflowIds=unauthorized-workflow' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(403) - expect(data).toHaveProperty('error', 'Unauthorized access to workflows') - }) - - it('should return 401 for unauthenticated requests', async () => { - // Mock auth to return no session - vi.doMock('@/lib/auth', () => ({ - getSession: vi.fn().mockResolvedValue(null), - })) - - setupDatabaseMock() - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(401) - expect(data).toHaveProperty('error', 'Unauthorized') - }) - - it('should validate query parameters', async () => { - setupDatabaseMock() - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123&limit=invalid') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(400) - expect(data).toHaveProperty('error', 'Invalid request parameters') - expect(data).toHaveProperty('details') - }) - - it('should handle database errors gracefully', async () => { - setupDatabaseMock({ throwError: true }) - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(500) - expect(data).toHaveProperty('error') - }) - - it('should combine multiple filters correctly', async () => { - const filteredLogs = mockWorkflowLogs.filter( - (log) => - log.level === 'info' && - log.workflowId === 'workflow-1' && - log.message.toLowerCase().includes('started') - ) - setupDatabaseMock({ logs: filteredLogs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&level=info&workflowIds=workflow-1&search=started' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(1) - expect(data.data[0].level).toBe('info') - expect(data.data[0].workflowId).toBe('workflow-1') - expect(data.data[0].message).toContain('started') - }) - - it('should handle end date filter', async () => { - const endDate = '2024-01-01T10:01:30.000Z' - const filteredLogs = mockWorkflowLogs.filter( - (log) => new Date(log.createdAt) <= new Date(endDate) - ) - setupDatabaseMock({ logs: filteredLogs }) - - const url = new URL( - `http://localhost:3000/api/logs?workspaceId=workspace-123&endDate=${endDate}` - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(2) - }) - - it('should handle large offset values', async () => { - setupDatabaseMock({ logs: [] }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&limit=10&offset=1000' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toEqual([]) - expect(data.page).toBe(101) // (1000 / 10) + 1 - expect(data.total).toBe(0) - }) - - it('should handle search by execution ID', async () => { - const searchLogs = mockWorkflowLogs.filter((log) => log.executionId?.includes('exec-1')) - setupDatabaseMock({ logs: searchLogs }) - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123&search=exec-1') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(2) - expect(data.data.every((log: any) => log.executionId === 'exec-1')).toBe(true) - }) - - it('should filter logs by single trigger type', async () => { - const apiLogs = mockWorkflowLogs.filter((log) => log.trigger === 'api') - setupDatabaseMock({ logs: apiLogs }) - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123&triggers=api') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(1) - expect(data.data[0].trigger).toBe('api') - }) - - it('should filter logs by multiple trigger types', async () => { - const manualAndApiLogs = mockWorkflowLogs.filter( - (log) => log.trigger === 'manual' || log.trigger === 'api' - ) - setupDatabaseMock({ logs: manualAndApiLogs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&triggers=manual,api' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(3) - expect(data.data.every((log: any) => ['manual', 'api'].includes(log.trigger))).toBe(true) - }) - - it('should combine trigger filter with other filters', async () => { - const filteredLogs = mockWorkflowLogs.filter( - (log) => log.trigger === 'manual' && log.level === 'info' && log.workflowId === 'workflow-1' - ) - setupDatabaseMock({ logs: filteredLogs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&triggers=manual&level=info&workflowIds=workflow-1' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(1) - expect(data.data[0].trigger).toBe('manual') - expect(data.data[0].level).toBe('info') - expect(data.data[0].workflowId).toBe('workflow-1') - }) - - it('should filter logs by single folder ID', async () => { - const folder1Logs = mockWorkflowLogs.filter((log) => log.workflowId === 'workflow-1') - setupDatabaseMock({ logs: folder1Logs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=folder-1' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(2) - expect(data.data.every((log: any) => log.workflowId === 'workflow-1')).toBe(true) - }) - - it('should filter logs by multiple folder IDs', async () => { - const folder1And2Logs = mockWorkflowLogs.filter( - (log) => log.workflowId === 'workflow-1' || log.workflowId === 'workflow-2' - ) - setupDatabaseMock({ logs: folder1And2Logs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=folder-1,folder-2' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(3) - expect( - data.data.every((log: any) => ['workflow-1', 'workflow-2'].includes(log.workflowId)) - ).toBe(true) - }) - - it('should filter logs by root folder (workflows without folders)', async () => { - const rootLogs = mockWorkflowLogs.filter((log) => log.workflowId === 'workflow-3') - setupDatabaseMock({ logs: rootLogs }) - - const url = new URL('http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=root') - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(1) - expect(data.data[0].workflowId).toBe('workflow-3') - expect(data.data[0].message).toContain('Root workflow executed') - }) - - it('should combine root folder with other folders', async () => { - const rootAndFolder1Logs = mockWorkflowLogs.filter( - (log) => log.workflowId === 'workflow-1' || log.workflowId === 'workflow-3' - ) - setupDatabaseMock({ logs: rootAndFolder1Logs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=root,folder-1' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(3) - expect( - data.data.every((log: any) => ['workflow-1', 'workflow-3'].includes(log.workflowId)) - ).toBe(true) - }) - - it('should combine folder filter with workflow filter', async () => { - // Filter by folder-1 and specific workflow-1 (should return same results) - const filteredLogs = mockWorkflowLogs.filter((log) => log.workflowId === 'workflow-1') - setupDatabaseMock({ logs: filteredLogs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=folder-1&workflowIds=workflow-1' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(2) - expect(data.data.every((log: any) => log.workflowId === 'workflow-1')).toBe(true) - }) - - it('should return empty when folder and workflow filters conflict', async () => { - // Try to filter by folder-1 but workflow-2 (which is in folder-2) - setupDatabaseMock({ logs: [] }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=folder-1&workflowIds=workflow-2' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toEqual([]) - expect(data.total).toBe(0) - }) - - it('should combine folder filter with other filters', async () => { - const filteredLogs = mockWorkflowLogs.filter( - (log) => log.workflowId === 'workflow-1' && log.level === 'info' - ) - setupDatabaseMock({ logs: filteredLogs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=folder-1&level=info' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(1) - expect(data.data[0].workflowId).toBe('workflow-1') - expect(data.data[0].level).toBe('info') - }) - - it('should return empty result when no workflows match folder filter', async () => { - setupDatabaseMock({ logs: [] }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=non-existent-folder' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toEqual([]) - expect(data.total).toBe(0) - }) - - it('should handle folder filter with includeWorkflow=true', async () => { - const folder1Logs = mockWorkflowLogs.filter((log) => log.workflowId === 'workflow-1') - setupDatabaseMock({ logs: folder1Logs }) - - const url = new URL( - 'http://localhost:3000/api/logs?workspaceId=workspace-123&folderIds=folder-1&includeWorkflow=true' - ) - const req = new Request(url.toString()) - - const { GET } = await import('./route') - const response = await GET(req as any) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.data).toHaveLength(2) - expect(data.data[0]).toHaveProperty('workflow') - expect(data.data[0].workflow).toHaveProperty('name') - expect(data.data.every((log: any) => log.workflowId === 'workflow-1')).toBe(true) - }) - }) -}) diff --git a/apps/sim/app/api/logs/route.ts b/apps/sim/app/api/logs/route.ts deleted file mode 100644 index f442f929f..000000000 --- a/apps/sim/app/api/logs/route.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { and, eq, gte, lte, or, type SQL, sql } from 'drizzle-orm' -import { type NextRequest, NextResponse } from 'next/server' -import { z } from 'zod' -import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console-logger' -import { db } from '@/db' -import { workflow, workflowLogs } from '@/db/schema' - -const logger = createLogger('WorkflowLogsAPI') - -export const dynamic = 'force-dynamic' -export const revalidate = 0 - -const QueryParamsSchema = z.object({ - includeWorkflow: z.enum(['true', 'false']).optional().default('false'), - limit: z.coerce.number().optional().default(100), - offset: z.coerce.number().optional().default(0), - level: z.string().optional(), - workflowIds: z.string().optional(), // Comma-separated list of workflow IDs - folderIds: z.string().optional(), // Comma-separated list of folder IDs - triggers: z.string().optional(), // Comma-separated list of trigger types - startDate: z.string().optional(), - endDate: z.string().optional(), - search: z.string().optional(), - workspaceId: z.string(), -}) - -// Used to retrieve and display workflow logs -export async function GET(request: NextRequest) { - const requestId = crypto.randomUUID().slice(0, 8) - - try { - const session = await getSession() - if (!session?.user?.id) { - logger.warn(`[${requestId}] Unauthorized workflow logs access attempt`) - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - - const userId = session.user.id - - try { - const { searchParams } = new URL(request.url) - const params = QueryParamsSchema.parse(Object.fromEntries(searchParams.entries())) - - const workflowConditions = and( - eq(workflow.workspaceId, params.workspaceId), - eq(workflow.userId, userId) - ) - - const userWorkflows = await db - .select({ id: workflow.id, folderId: workflow.folderId }) - .from(workflow) - .where(workflowConditions) - - const userWorkflowIds = userWorkflows.map((w) => w.id) - - if (userWorkflowIds.length === 0) { - return NextResponse.json({ data: [], total: 0 }, { status: 200 }) - } - - // Handle folder filtering - let targetWorkflowIds = userWorkflowIds - if (params.folderIds) { - const requestedFolderIds = params.folderIds.split(',').map((id) => id.trim()) - - // Filter workflows by folder IDs (including 'root' for workflows without folders) - const workflowsInFolders = userWorkflows.filter((w) => { - if (requestedFolderIds.includes('root')) { - return requestedFolderIds.includes('root') && w.folderId === null - } - return w.folderId && requestedFolderIds.includes(w.folderId) - }) - - // Handle 'root' folder (workflows without folders) - if (requestedFolderIds.includes('root')) { - const rootWorkflows = userWorkflows.filter((w) => w.folderId === null) - const folderWorkflows = userWorkflows.filter( - (w) => - w.folderId && requestedFolderIds.filter((id) => id !== 'root').includes(w.folderId!) - ) - targetWorkflowIds = [...rootWorkflows, ...folderWorkflows].map((w) => w.id) - } else { - targetWorkflowIds = workflowsInFolders.map((w) => w.id) - } - - if (targetWorkflowIds.length === 0) { - return NextResponse.json({ data: [], total: 0 }, { status: 200 }) - } - } - - // Build the conditions for the query - let conditions: SQL | undefined - - // Apply workflow filtering - if (params.workflowIds) { - const requestedWorkflowIds = params.workflowIds.split(',').map((id) => id.trim()) - // Ensure all requested workflows belong to the user - const unauthorizedIds = requestedWorkflowIds.filter((id) => !userWorkflowIds.includes(id)) - if (unauthorizedIds.length > 0) { - logger.warn(`[${requestId}] Unauthorized access to workflow logs`, { - unauthorizedWorkflowIds: unauthorizedIds, - }) - return NextResponse.json({ error: 'Unauthorized access to workflows' }, { status: 403 }) - } - // Further filter by folder constraints if both filters are active - const finalWorkflowIds = params.folderIds - ? requestedWorkflowIds.filter((id) => targetWorkflowIds.includes(id)) - : requestedWorkflowIds - - if (finalWorkflowIds.length === 0) { - return NextResponse.json({ data: [], total: 0 }, { status: 200 }) - } - conditions = or(...finalWorkflowIds.map((id) => eq(workflowLogs.workflowId, id))) - } else { - // No specific workflows requested, filter by target workflows (considering folder filter) - if (targetWorkflowIds.length === 1) { - conditions = eq(workflowLogs.workflowId, targetWorkflowIds[0]) - } else { - conditions = or(...targetWorkflowIds.map((id) => eq(workflowLogs.workflowId, id))) - } - } - - // Apply additional filters if provided - if (params.level) { - conditions = and(conditions, eq(workflowLogs.level, params.level)) - } - - if (params.triggers) { - const triggerTypes = params.triggers.split(',').map((trigger) => trigger.trim()) - if (triggerTypes.length === 1) { - conditions = and(conditions, eq(workflowLogs.trigger, triggerTypes[0])) - } else { - conditions = and( - conditions, - or(...triggerTypes.map((trigger) => eq(workflowLogs.trigger, trigger))) - ) - } - } - - if (params.startDate) { - const startDate = new Date(params.startDate) - conditions = and(conditions, gte(workflowLogs.createdAt, startDate)) - } - - if (params.endDate) { - const endDate = new Date(params.endDate) - conditions = and(conditions, lte(workflowLogs.createdAt, endDate)) - } - - if (params.search) { - const searchTerm = `%${params.search}%` - conditions = and( - conditions, - or( - sql`${workflowLogs.message} ILIKE ${searchTerm}`, - sql`${workflowLogs.executionId} ILIKE ${searchTerm}` - ) - ) - } - - // Execute the query with all conditions - const logs = await db - .select() - .from(workflowLogs) - .where(conditions) - .orderBy(sql`${workflowLogs.createdAt} DESC`) - .limit(params.limit) - .offset(params.offset) - - // Get total count for pagination - const countResult = await db - .select({ count: sql`count(*)` }) - .from(workflowLogs) - .where(conditions) - - const count = countResult[0]?.count || 0 - - // If includeWorkflow is true, fetch the associated workflow data - if (params.includeWorkflow === 'true' && logs.length > 0) { - // Get unique workflow IDs from logs - const uniqueWorkflowIds = [...new Set(logs.map((log) => log.workflowId))] - - // Create conditions for workflow query - let workflowConditions: SQL | undefined - - if (uniqueWorkflowIds.length === 1) { - workflowConditions = eq(workflow.id, uniqueWorkflowIds[0]) - } else { - workflowConditions = or(...uniqueWorkflowIds.map((id) => eq(workflow.id, id))) - } - - // Fetch workflows - const workflowData = await db.select().from(workflow).where(workflowConditions) - - // Create a map of workflow data for easy lookup - const workflowMap = new Map(workflowData.map((w) => [w.id, w])) - - // Attach workflow data to each log - const logsWithWorkflow = logs.map((log) => ({ - ...log, - workflow: workflowMap.get(log.workflowId) || null, - })) - - return NextResponse.json( - { - data: logsWithWorkflow, - total: Number(count), - page: Math.floor(params.offset / params.limit) + 1, - pageSize: params.limit, - totalPages: Math.ceil(Number(count) / params.limit), - }, - { status: 200 } - ) - } - - // Return logs without workflow data - return NextResponse.json( - { - data: logs, - total: Number(count), - page: Math.floor(params.offset / params.limit) + 1, - pageSize: params.limit, - totalPages: Math.ceil(Number(count) / params.limit), - }, - { status: 200 } - ) - } catch (validationError) { - if (validationError instanceof z.ZodError) { - logger.warn(`[${requestId}] Invalid workflow logs request parameters`, { - errors: validationError.errors, - }) - return NextResponse.json( - { - error: 'Invalid request parameters', - details: validationError.errors, - }, - { status: 400 } - ) - } - throw validationError - } - } catch (error: any) { - logger.error(`[${requestId}] Workflow logs fetch error`, error) - return NextResponse.json({ error: error.message }, { status: 500 }) - } -} diff --git a/apps/sim/app/api/schedules/execute/route.test.ts b/apps/sim/app/api/schedules/execute/route.test.ts index 3b8ae0dd6..41fad0365 100644 --- a/apps/sim/app/api/schedules/execute/route.test.ts +++ b/apps/sim/app/api/schedules/execute/route.test.ts @@ -131,12 +131,6 @@ describe('Scheduled Workflow Execution API Route', () => { }) it('should handle errors during scheduled execution gracefully', async () => { - const persistExecutionErrorMock = vi.fn().mockResolvedValue(undefined) - - vi.doMock('@/lib/logs/execution-logger', () => ({ - persistExecutionError: persistExecutionErrorMock, - })) - vi.doMock('@/executor', () => ({ Executor: vi.fn().mockImplementation(() => ({ execute: vi.fn().mockRejectedValue(new Error('Execution failed')), diff --git a/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts b/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts index 55e2397be..dda7083ef 100644 --- a/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts +++ b/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts @@ -32,7 +32,6 @@ const executeMock = vi.fn().mockResolvedValue({ endTime: new Date().toISOString(), }, }) -const persistExecutionErrorMock = vi.fn().mockResolvedValue(undefined) // Mock the DB schema objects const webhookMock = { @@ -78,10 +77,6 @@ vi.mock('@/executor', () => ({ })), })) -vi.mock('@/lib/logs/execution-logger', () => ({ - persistExecutionError: persistExecutionErrorMock, -})) - // Mock setTimeout and other timer functions vi.mock('timers', () => { return { diff --git a/apps/sim/app/api/workflows/[id]/execute/route.test.ts b/apps/sim/app/api/workflows/[id]/execute/route.test.ts index 8a36b8612..0f415abba 100644 --- a/apps/sim/app/api/workflows/[id]/execute/route.test.ts +++ b/apps/sim/app/api/workflows/[id]/execute/route.test.ts @@ -157,11 +157,6 @@ describe('Workflow Execution API Route', () => { getRotatingApiKey: vi.fn().mockReturnValue('rotated-api-key'), })) - vi.doMock('@/lib/logs/execution-logger', () => ({ - persistExecutionLogs: vi.fn().mockResolvedValue(undefined), - persistExecutionError: vi.fn().mockResolvedValue(undefined), - })) - vi.doMock('@/lib/logs/enhanced-logging-session', () => ({ EnhancedLoggingSession: vi.fn().mockImplementation(() => ({ safeStart: vi.fn().mockResolvedValue(undefined), diff --git a/apps/sim/app/api/workflows/[id]/log/route.ts b/apps/sim/app/api/workflows/[id]/log/route.ts index dee260c3e..22cfe9c5f 100644 --- a/apps/sim/app/api/workflows/[id]/log/route.ts +++ b/apps/sim/app/api/workflows/[id]/log/route.ts @@ -23,7 +23,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{ const body = await request.json() const { logs, executionId, result } = body - // If result is provided, use persistExecutionLogs for full tool call extraction + // If result is provided, use enhanced logging system for full tool call extraction if (result) { logger.info(`[${requestId}] Persisting execution result for workflow: ${id}`, { executionId, diff --git a/apps/sim/db/migrations/0062_previous_phantom_reporter.sql b/apps/sim/db/migrations/0062_previous_phantom_reporter.sql new file mode 100644 index 000000000..89bbae0d9 --- /dev/null +++ b/apps/sim/db/migrations/0062_previous_phantom_reporter.sql @@ -0,0 +1 @@ +DROP TABLE "workflow_logs" CASCADE; \ No newline at end of file diff --git a/apps/sim/db/migrations/meta/0062_snapshot.json b/apps/sim/db/migrations/meta/0062_snapshot.json new file mode 100644 index 000000000..55e988225 --- /dev/null +++ b/apps/sim/db/migrations/meta/0062_snapshot.json @@ -0,0 +1,5529 @@ +{ + "id": "5fe645b1-2d33-4fd7-8144-49dc2f3a3fd6", + "prevId": "7be680c6-0074-4d4b-80a9-641e5237f2f2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_key": { + "name": "api_key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used": { + "name": "last_used", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "api_key_user_id_user_id_fk": { + "name": "api_key_user_id_user_id_fk", + "tableFrom": "api_key", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_key_key_unique": { + "name": "api_key_key_unique", + "nullsNotDistinct": false, + "columns": ["key"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat": { + "name": "chat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subdomain": { + "name": "subdomain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customizations": { + "name": "customizations", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_emails": { + "name": "allowed_emails", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "output_configs": { + "name": "output_configs", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "subdomain_idx": { + "name": "subdomain_idx", + "columns": [ + { + "expression": "subdomain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_workflow_id_workflow_id_fk": { + "name": "chat_workflow_id_workflow_id_fk", + "tableFrom": "chat", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_chats": { + "name": "copilot_chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "messages": { + "name": "messages", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'claude-3-7-sonnet-latest'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_chats_user_id_idx": { + "name": "copilot_chats_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_workflow_id_idx": { + "name": "copilot_chats_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workflow_idx": { + "name": "copilot_chats_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_created_at_idx": { + "name": "copilot_chats_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_updated_at_idx": { + "name": "copilot_chats_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_chats_user_id_user_id_fk": { + "name": "copilot_chats_user_id_user_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_workflow_id_workflow_id_fk": { + "name": "copilot_chats_workflow_id_workflow_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_checkpoints": { + "name": "copilot_checkpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "yaml": { + "name": "yaml", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_checkpoints_user_id_idx": { + "name": "copilot_checkpoints_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_checkpoints_workflow_id_idx": { + "name": "copilot_checkpoints_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_checkpoints_chat_id_idx": { + "name": "copilot_checkpoints_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_checkpoints_user_workflow_idx": { + "name": "copilot_checkpoints_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_checkpoints_workflow_chat_idx": { + "name": "copilot_checkpoints_workflow_chat_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_checkpoints_created_at_idx": { + "name": "copilot_checkpoints_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_checkpoints_chat_created_at_idx": { + "name": "copilot_checkpoints_chat_created_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_checkpoints_user_id_user_id_fk": { + "name": "copilot_checkpoints_user_id_user_id_fk", + "tableFrom": "copilot_checkpoints", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_checkpoints_workflow_id_workflow_id_fk": { + "name": "copilot_checkpoints_workflow_id_workflow_id_fk", + "tableFrom": "copilot_checkpoints", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_checkpoints_chat_id_copilot_chats_id_fk": { + "name": "copilot_checkpoints_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_checkpoints", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_tools": { + "name": "custom_tools", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema": { + "name": "schema", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "custom_tools_user_id_user_id_fk": { + "name": "custom_tools_user_id_user_id_fk", + "tableFrom": "custom_tools", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.docs_embeddings": { + "name": "docs_embeddings", + "schema": "", + "columns": { + "chunk_id": { + "name": "chunk_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chunk_text": { + "name": "chunk_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_document": { + "name": "source_document", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_link": { + "name": "source_link", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_text": { + "name": "header_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_level": { + "name": "header_level", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "chunk_text_tsv": { + "name": "chunk_text_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"docs_embeddings\".\"chunk_text\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "docs_emb_source_document_idx": { + "name": "docs_emb_source_document_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_header_level_idx": { + "name": "docs_emb_header_level_idx", + "columns": [ + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_source_header_idx": { + "name": "docs_emb_source_header_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_model_idx": { + "name": "docs_emb_model_idx", + "columns": [ + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_created_at_idx": { + "name": "docs_emb_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_embedding_vector_hnsw_idx": { + "name": "docs_embedding_vector_hnsw_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "docs_emb_metadata_gin_idx": { + "name": "docs_emb_metadata_gin_idx", + "columns": [ + { + "expression": "metadata", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "docs_emb_chunk_text_fts_idx": { + "name": "docs_emb_chunk_text_fts_idx", + "columns": [ + { + "expression": "chunk_text_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "docs_embedding_not_null_check": { + "name": "docs_embedding_not_null_check", + "value": "\"embedding\" IS NOT NULL" + }, + "docs_header_level_check": { + "name": "docs_header_level_check", + "value": "\"header_level\" >= 1 AND \"header_level\" <= 6" + } + }, + "isRLSEnabled": false + }, + "public.document": { + "name": "document", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "character_count": { + "name": "character_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "processing_status": { + "name": "processing_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_completed_at": { + "name": "processing_completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_error": { + "name": "processing_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "doc_kb_id_idx": { + "name": "doc_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_filename_idx": { + "name": "doc_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_uploaded_at_idx": { + "name": "doc_kb_uploaded_at_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "uploaded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_processing_status_idx": { + "name": "doc_processing_status_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "processing_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag1_idx": { + "name": "doc_tag1_idx", + "columns": [ + { + "expression": "tag1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag2_idx": { + "name": "doc_tag2_idx", + "columns": [ + { + "expression": "tag2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag3_idx": { + "name": "doc_tag3_idx", + "columns": [ + { + "expression": "tag3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag4_idx": { + "name": "doc_tag4_idx", + "columns": [ + { + "expression": "tag4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag5_idx": { + "name": "doc_tag5_idx", + "columns": [ + { + "expression": "tag5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag6_idx": { + "name": "doc_tag6_idx", + "columns": [ + { + "expression": "tag6", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag7_idx": { + "name": "doc_tag7_idx", + "columns": [ + { + "expression": "tag7", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_knowledge_base_id_knowledge_base_id_fk": { + "name": "document_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "document", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.embedding": { + "name": "embedding", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_index": { + "name": "chunk_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "chunk_hash": { + "name": "chunk_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_length": { + "name": "content_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "start_offset": { + "name": "start_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_offset": { + "name": "end_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"embedding\".\"content\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "emb_kb_id_idx": { + "name": "emb_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_id_idx": { + "name": "emb_doc_id_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_chunk_idx": { + "name": "emb_doc_chunk_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chunk_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_model_idx": { + "name": "emb_kb_model_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_enabled_idx": { + "name": "emb_kb_enabled_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_enabled_idx": { + "name": "emb_doc_enabled_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_vector_hnsw_idx": { + "name": "embedding_vector_hnsw_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "emb_tag1_idx": { + "name": "emb_tag1_idx", + "columns": [ + { + "expression": "tag1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag2_idx": { + "name": "emb_tag2_idx", + "columns": [ + { + "expression": "tag2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag3_idx": { + "name": "emb_tag3_idx", + "columns": [ + { + "expression": "tag3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag4_idx": { + "name": "emb_tag4_idx", + "columns": [ + { + "expression": "tag4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag5_idx": { + "name": "emb_tag5_idx", + "columns": [ + { + "expression": "tag5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag6_idx": { + "name": "emb_tag6_idx", + "columns": [ + { + "expression": "tag6", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag7_idx": { + "name": "emb_tag7_idx", + "columns": [ + { + "expression": "tag7", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_content_fts_idx": { + "name": "emb_content_fts_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_knowledge_base_id_knowledge_base_id_fk": { + "name": "embedding_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "embedding", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "embedding_document_id_document_id_fk": { + "name": "embedding_document_id_document_id_fk", + "tableFrom": "embedding", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_not_null_check": { + "name": "embedding_not_null_check", + "value": "\"embedding\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "environment_user_id_user_id_fk": { + "name": "environment_user_id_user_id_fk", + "tableFrom": "environment", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "environment_user_id_unique": { + "name": "environment_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": ["inviter_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_base": { + "name": "knowledge_base", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "embedding_dimension": { + "name": "embedding_dimension", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1536 + }, + "chunking_config": { + "name": "chunking_config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{\"maxSize\": 1024, \"minSize\": 100, \"overlap\": 200}'" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kb_user_id_idx": { + "name": "kb_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_id_idx": { + "name": "kb_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_user_workspace_idx": { + "name": "kb_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_deleted_at_idx": { + "name": "kb_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_base_user_id_user_id_fk": { + "name": "knowledge_base_user_id_user_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_workspace_id_workspace_id_fk": { + "name": "knowledge_base_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.marketplace": { + "name": "marketplace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_name": { + "name": "author_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "views": { + "name": "views", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "marketplace_workflow_id_workflow_id_fk": { + "name": "marketplace_workflow_id_workflow_id_fk", + "tableFrom": "marketplace", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "marketplace_author_id_user_id_fk": { + "name": "marketplace_author_id_user_id_fk", + "tableFrom": "marketplace", + "tableTo": "user", + "columnsFrom": ["author_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory": { + "name": "memory", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "memory_key_idx": { + "name": "memory_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workflow_idx": { + "name": "memory_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workflow_key_idx": { + "name": "memory_workflow_key_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_workflow_id_workflow_id_fk": { + "name": "memory_workflow_id_workflow_id_fk", + "tableFrom": "memory", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permissions": { + "name": "permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permissions_user_id_idx": { + "name": "permissions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_entity_idx": { + "name": "permissions_entity_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_type_idx": { + "name": "permissions_user_entity_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_permission_idx": { + "name": "permissions_user_entity_permission_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_idx": { + "name": "permissions_user_entity_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_unique_constraint": { + "name": "permissions_unique_constraint", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permissions_user_id_user_id_fk": { + "name": "permissions_user_id_user_id_fk", + "tableFrom": "permissions", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "session_active_organization_id_organization_id_fk": { + "name": "session_active_organization_id_organization_id_fk", + "tableFrom": "session", + "tableTo": "organization", + "columnsFrom": ["active_organization_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.settings": { + "name": "settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "auto_connect": { + "name": "auto_connect", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_fill_env_vars": { + "name": "auto_fill_env_vars", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_pan": { + "name": "auto_pan", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "console_expanded_by_default": { + "name": "console_expanded_by_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "telemetry_enabled": { + "name": "telemetry_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "telemetry_notified_user": { + "name": "telemetry_notified_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "email_preferences": { + "name": "email_preferences", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "general": { + "name": "general", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "settings_user_id_user_id_fk": { + "name": "settings_user_id_user_id_fk", + "tableFrom": "settings", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "settings_user_id_unique": { + "name": "settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscription": { + "name": "subscription", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "period_start": { + "name": "period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "period_end": { + "name": "period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "seats": { + "name": "seats", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "trial_start": { + "name": "trial_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trial_end": { + "name": "trial_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "subscription_reference_status_idx": { + "name": "subscription_reference_status_idx", + "columns": [ + { + "expression": "reference_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "check_enterprise_metadata": { + "name": "check_enterprise_metadata", + "value": "plan != 'enterprise' OR (metadata IS NOT NULL AND (metadata->>'perSeatAllowance' IS NOT NULL OR metadata->>'totalAllowance' IS NOT NULL))" + } + }, + "isRLSEnabled": false + }, + "public.template_stars": { + "name": "template_stars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "starred_at": { + "name": "starred_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "template_stars_user_id_idx": { + "name": "template_stars_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_template_id_idx": { + "name": "template_stars_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_user_template_idx": { + "name": "template_stars_user_template_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_template_user_idx": { + "name": "template_stars_template_user_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_starred_at_idx": { + "name": "template_stars_starred_at_idx", + "columns": [ + { + "expression": "starred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_template_starred_at_idx": { + "name": "template_stars_template_starred_at_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_user_template_unique": { + "name": "template_stars_user_template_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "template_stars_user_id_user_id_fk": { + "name": "template_stars_user_id_user_id_fk", + "tableFrom": "template_stars", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "template_stars_template_id_templates_id_fk": { + "name": "template_stars_template_id_templates_id_fk", + "tableFrom": "template_stars", + "tableTo": "templates", + "columnsFrom": ["template_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.templates": { + "name": "templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "views": { + "name": "views", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "stars": { + "name": "stars", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#3972F6'" + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'FileText'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "templates_workflow_id_idx": { + "name": "templates_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_user_id_idx": { + "name": "templates_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_category_idx": { + "name": "templates_category_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_views_idx": { + "name": "templates_views_idx", + "columns": [ + { + "expression": "views", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_stars_idx": { + "name": "templates_stars_idx", + "columns": [ + { + "expression": "stars", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_category_views_idx": { + "name": "templates_category_views_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "views", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_category_stars_idx": { + "name": "templates_category_stars_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stars", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_user_category_idx": { + "name": "templates_user_category_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_created_at_idx": { + "name": "templates_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_updated_at_idx": { + "name": "templates_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "templates_workflow_id_workflow_id_fk": { + "name": "templates_workflow_id_workflow_id_fk", + "tableFrom": "templates", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "templates_user_id_user_id_fk": { + "name": "templates_user_id_user_id_fk", + "tableFrom": "templates", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_rate_limits": { + "name": "user_rate_limits", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "sync_api_requests": { + "name": "sync_api_requests", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "async_api_requests": { + "name": "async_api_requests", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "window_start": { + "name": "window_start", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_request_at": { + "name": "last_request_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_rate_limited": { + "name": "is_rate_limited", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "rate_limit_reset_at": { + "name": "rate_limit_reset_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_rate_limits_user_id_user_id_fk": { + "name": "user_rate_limits_user_id_user_id_fk", + "tableFrom": "user_rate_limits", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_stats": { + "name": "user_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_manual_executions": { + "name": "total_manual_executions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_api_calls": { + "name": "total_api_calls", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_webhook_triggers": { + "name": "total_webhook_triggers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_scheduled_executions": { + "name": "total_scheduled_executions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_chat_executions": { + "name": "total_chat_executions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_tokens_used": { + "name": "total_tokens_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cost": { + "name": "total_cost", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_usage_limit": { + "name": "current_usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'5'" + }, + "usage_limit_set_by": { + "name": "usage_limit_set_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "usage_limit_updated_at": { + "name": "usage_limit_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "current_period_cost": { + "name": "current_period_cost", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "billing_period_start": { + "name": "billing_period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "billing_period_end": { + "name": "billing_period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_period_cost": { + "name": "last_period_cost", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "last_active": { + "name": "last_active", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_stats_user_id_user_id_fk": { + "name": "user_stats_user_id_user_id_fk", + "tableFrom": "user_stats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_stats_user_id_unique": { + "name": "user_stats_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.waitlist": { + "name": "waitlist", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "waitlist_email_unique": { + "name": "waitlist_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook": { + "name": "webhook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_config": { + "name": "provider_config", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "path_idx": { + "name": "path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_workflow_id_workflow_id_fk": { + "name": "webhook_workflow_id_workflow_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhook_block_id_workflow_blocks_id_fk": { + "name": "webhook_block_id_workflow_blocks_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow_blocks", + "columnsFrom": ["block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow": { + "name": "workflow", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#3972F6'" + }, + "last_synced": { + "name": "last_synced", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "is_deployed": { + "name": "is_deployed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deployed_state": { + "name": "deployed_state", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "deployed_at": { + "name": "deployed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "collaborators": { + "name": "collaborators", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "is_published": { + "name": "is_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "marketplace_data": { + "name": "marketplace_data", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_user_id_idx": { + "name": "workflow_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_id_idx": { + "name": "workflow_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_user_workspace_idx": { + "name": "workflow_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_user_id_user_id_fk": { + "name": "workflow_user_id_user_id_fk", + "tableFrom": "workflow", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_workspace_id_workspace_id_fk": { + "name": "workflow_workspace_id_workspace_id_fk", + "tableFrom": "workflow", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_folder_id_workflow_folder_id_fk": { + "name": "workflow_folder_id_workflow_folder_id_fk", + "tableFrom": "workflow", + "tableTo": "workflow_folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_blocks": { + "name": "workflow_blocks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position_x": { + "name": "position_x", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "position_y": { + "name": "position_y", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "horizontal_handles": { + "name": "horizontal_handles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_wide": { + "name": "is_wide", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "advanced_mode": { + "name": "advanced_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "sub_blocks": { + "name": "sub_blocks", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "outputs": { + "name": "outputs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "extent": { + "name": "extent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_blocks_workflow_id_idx": { + "name": "workflow_blocks_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_parent_id_idx": { + "name": "workflow_blocks_parent_id_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_workflow_parent_idx": { + "name": "workflow_blocks_workflow_parent_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_workflow_type_idx": { + "name": "workflow_blocks_workflow_type_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_blocks_workflow_id_workflow_id_fk": { + "name": "workflow_blocks_workflow_id_workflow_id_fk", + "tableFrom": "workflow_blocks", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_edges": { + "name": "workflow_edges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_block_id": { + "name": "source_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_block_id": { + "name": "target_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_handle": { + "name": "source_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_handle": { + "name": "target_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_edges_workflow_id_idx": { + "name": "workflow_edges_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_source_block_idx": { + "name": "workflow_edges_source_block_idx", + "columns": [ + { + "expression": "source_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_target_block_idx": { + "name": "workflow_edges_target_block_idx", + "columns": [ + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_source_idx": { + "name": "workflow_edges_workflow_source_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_target_idx": { + "name": "workflow_edges_workflow_target_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_edges_workflow_id_workflow_id_fk": { + "name": "workflow_edges_workflow_id_workflow_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_source_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_source_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["source_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_target_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_target_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["target_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_logs": { + "name": "workflow_execution_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_snapshot_id": { + "name": "state_snapshot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_duration_ms": { + "name": "total_duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "block_count": { + "name": "block_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "success_count": { + "name": "success_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_count": { + "name": "error_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped_count": { + "name": "skipped_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cost": { + "name": "total_cost", + "type": "numeric(10, 6)", + "primaryKey": false, + "notNull": false + }, + "total_input_cost": { + "name": "total_input_cost", + "type": "numeric(10, 6)", + "primaryKey": false, + "notNull": false + }, + "total_output_cost": { + "name": "total_output_cost", + "type": "numeric(10, 6)", + "primaryKey": false, + "notNull": false + }, + "total_tokens": { + "name": "total_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_execution_logs_workflow_id_idx": { + "name": "workflow_execution_logs_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_execution_id_idx": { + "name": "workflow_execution_logs_execution_id_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_trigger_idx": { + "name": "workflow_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_level_idx": { + "name": "workflow_execution_logs_level_idx", + "columns": [ + { + "expression": "level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_started_at_idx": { + "name": "workflow_execution_logs_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_cost_idx": { + "name": "workflow_execution_logs_cost_idx", + "columns": [ + { + "expression": "total_cost", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_duration_idx": { + "name": "workflow_execution_logs_duration_idx", + "columns": [ + { + "expression": "total_duration_ms", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_execution_id_unique": { + "name": "workflow_execution_logs_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_logs_workflow_id_workflow_id_fk": { + "name": "workflow_execution_logs_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk": { + "name": "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow_execution_snapshots", + "columnsFrom": ["state_snapshot_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_snapshots": { + "name": "workflow_execution_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_data": { + "name": "state_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_snapshots_workflow_id_idx": { + "name": "workflow_snapshots_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_hash_idx": { + "name": "workflow_snapshots_hash_idx", + "columns": [ + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_workflow_hash_idx": { + "name": "workflow_snapshots_workflow_hash_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_created_at_idx": { + "name": "workflow_snapshots_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_snapshots_workflow_id_workflow_id_fk": { + "name": "workflow_execution_snapshots_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_snapshots", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_folder": { + "name": "workflow_folder", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'#6B7280'" + }, + "is_expanded": { + "name": "is_expanded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_folder_user_idx": { + "name": "workflow_folder_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_folder_workspace_parent_idx": { + "name": "workflow_folder_workspace_parent_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_folder_parent_sort_idx": { + "name": "workflow_folder_parent_sort_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_folder_user_id_user_id_fk": { + "name": "workflow_folder_user_id_user_id_fk", + "tableFrom": "workflow_folder", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_folder_workspace_id_workspace_id_fk": { + "name": "workflow_folder_workspace_id_workspace_id_fk", + "tableFrom": "workflow_folder", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_schedule": { + "name": "workflow_schedule", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_ran_at": { + "name": "last_ran_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_failed_at": { + "name": "last_failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_schedule_workflow_block_unique": { + "name": "workflow_schedule_workflow_block_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_schedule_workflow_id_workflow_id_fk": { + "name": "workflow_schedule_workflow_id_workflow_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_block_id_workflow_blocks_id_fk": { + "name": "workflow_schedule_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow_blocks", + "columnsFrom": ["block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_subflows": { + "name": "workflow_subflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_subflows_workflow_id_idx": { + "name": "workflow_subflows_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_subflows_workflow_type_idx": { + "name": "workflow_subflows_workflow_type_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_subflows_workflow_id_workflow_id_fk": { + "name": "workflow_subflows_workflow_id_workflow_id_fk", + "tableFrom": "workflow_subflows", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_owner_id_user_id_fk": { + "name": "workspace_owner_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_invitation": { + "name": "workspace_invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'admin'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_invitation_workspace_id_workspace_id_fk": { + "name": "workspace_invitation_workspace_id_workspace_id_fk", + "tableFrom": "workspace_invitation", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_invitation_inviter_id_user_id_fk": { + "name": "workspace_invitation_inviter_id_user_id_fk", + "tableFrom": "workspace_invitation", + "tableTo": "user", + "columnsFrom": ["inviter_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_invitation_token_unique": { + "name": "workspace_invitation_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.permission_type": { + "name": "permission_type", + "schema": "public", + "values": ["admin", "write", "read"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/sim/db/migrations/meta/_journal.json b/apps/sim/db/migrations/meta/_journal.json index 0ea4e574b..427c5fae9 100644 --- a/apps/sim/db/migrations/meta/_journal.json +++ b/apps/sim/db/migrations/meta/_journal.json @@ -428,6 +428,13 @@ "when": 1753380613269, "tag": "0061_swift_doctor_spectrum", "breakpoints": true + }, + { + "idx": 62, + "version": "7", + "when": 1753383446084, + "tag": "0062_previous_phantom_reporter", + "breakpoints": true } ] } diff --git a/apps/sim/db/schema.ts b/apps/sim/db/schema.ts index fb4ff5312..992f6557e 100644 --- a/apps/sim/db/schema.ts +++ b/apps/sim/db/schema.ts @@ -245,30 +245,6 @@ export const waitlist = pgTable('waitlist', { updatedAt: timestamp('updated_at').notNull().defaultNow(), }) -export const workflowLogs = pgTable( - 'workflow_logs', - { - id: text('id').primaryKey(), - workflowId: text('workflow_id') - .notNull() - .references(() => workflow.id, { onDelete: 'cascade' }), - executionId: text('execution_id'), - level: text('level').notNull(), // "info", "error", etc. - message: text('message').notNull(), - duration: text('duration'), // Store as text to allow 'NA' for errors - trigger: text('trigger'), // "api", "schedule", "manual" - createdAt: timestamp('created_at').notNull().defaultNow(), - metadata: json('metadata'), - }, - (table) => ({ - workflowIdIdx: index('workflow_logs_workflow_id_idx').on(table.workflowId), - workflowCreatedIdx: index('workflow_logs_workflow_created_idx').on( - table.workflowId, - table.createdAt - ), - }) -) - export const workflowExecutionSnapshots = pgTable( 'workflow_execution_snapshots', { diff --git a/apps/sim/lib/logs/execution-logger.ts b/apps/sim/lib/logs/execution-logger.ts deleted file mode 100644 index 5d580fb85..000000000 --- a/apps/sim/lib/logs/execution-logger.ts +++ /dev/null @@ -1,928 +0,0 @@ -import { eq, sql } from 'drizzle-orm' -import { v4 as uuidv4 } from 'uuid' -import { getCostMultiplier } from '@/lib/environment' -import { createLogger } from '@/lib/logs/console-logger' -import { redactApiKeys } from '@/lib/utils' -import { stripCustomToolPrefix } from '@/lib/workflows/utils' -import { db } from '@/db' -import { userStats, workflow, workflowLogs } from '@/db/schema' -import type { ExecutionResult as ExecutorResult } from '@/executor/types' -import { calculateCost } from '@/providers/utils' - -const logger = createLogger('ExecutionLogger') - -export interface LogEntry { - id: string - workflowId: string - executionId: string - level: string - message: string - createdAt: Date - duration?: string - trigger?: string - metadata?: ToolCallMetadata | Record -} - -export interface ToolCallMetadata { - toolCalls?: ToolCall[] - cost?: { - model?: string - input?: number - output?: number - total?: number - tokens?: { - prompt?: number - completion?: number - total?: number - } - pricing?: { - input: number - output: number - cachedInput?: number - updatedAt: string - } - } -} - -export interface ToolCall { - name: string - duration: number // in milliseconds - startTime: string // ISO timestamp - endTime: string // ISO timestamp - status: 'success' | 'error' // Status of the tool call - input?: Record // Input parameters (optional) - output?: Record // Output data (optional) - error?: string // Error message if status is 'error' -} - -export async function persistLog(log: LogEntry) { - await db.insert(workflowLogs).values(log) -} - -/** - * Persists logs for a workflow execution, including individual block logs and the final result - * @param workflowId - The ID of the workflow - * @param executionId - The ID of the execution - * @param result - The execution result - * @param triggerType - The type of trigger (api, webhook, schedule, manual, chat) - */ -export async function persistExecutionLogs( - workflowId: string, - executionId: string, - result: ExecutorResult, - triggerType: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' -) { - try { - // Get the workflow record to get the userId - const [workflowRecord] = await db - .select() - .from(workflow) - .where(eq(workflow.id, workflowId)) - .limit(1) - - if (!workflowRecord) { - logger.error(`Workflow ${workflowId} not found`) - return - } - - const userId = workflowRecord.userId - - // Track accumulated cost data across all LLM blocks (agent, router, and evaluator) - let totalCost = 0 - let totalInputCost = 0 - let totalOutputCost = 0 - let totalPromptTokens = 0 - let totalCompletionTokens = 0 - let totalTokens = 0 - const modelCounts: Record = {} - let primaryModel = '' - - // Log each execution step - for (const log of result.logs || []) { - // Check for agent block and tool calls - let metadata: ToolCallMetadata | undefined - - // If this is an agent, router, or evaluator block (all use LLM providers and generate costs) - if ( - (log.blockType === 'agent' || - log.blockType === 'router' || - log.blockType === 'evaluator') && - log.output - ) { - logger.debug('Processing LLM-based block output for tool calls and cost tracking', { - blockId: log.blockId, - blockName: log.blockName, - blockType: log.blockType, - outputKeys: Object.keys(log.output), - hasToolCalls: !!log.output.toolCalls, - hasResponse: !!log.output, - }) - - // FIRST PASS - Check if this is a no-tool scenario with tokens data not propagated - // In some cases, the token data from the streaming callback doesn't properly get into - // the agent block response. This ensures we capture it. - if ( - log.output && - (!log.output.tokens?.completion || log.output.tokens.completion === 0) && - (!log.output.toolCalls || - !log.output.toolCalls.list || - log.output.toolCalls.list.length === 0) - ) { - // Check if output has providerTiming - this indicates it's a streaming response - if (log.output.providerTiming) { - logger.debug('Processing streaming response without tool calls for token extraction', { - blockId: log.blockId, - hasTokens: !!log.output.tokens, - hasProviderTiming: !!log.output.providerTiming, - }) - - // Only for no-tool streaming cases, extract content length and estimate token count - const contentLength = log.output.content?.length || 0 - if (contentLength > 0) { - // Estimate completion tokens based on content length as a fallback - const estimatedCompletionTokens = Math.ceil(contentLength / 4) - const promptTokens = log.output.tokens?.prompt || 8 - - // Update the tokens object - log.output.tokens = { - prompt: promptTokens, - completion: estimatedCompletionTokens, - total: promptTokens + estimatedCompletionTokens, - } - - // Update cost information using the provider's cost model - const model = log.output.model || 'gpt-4o' - const costInfo = calculateCost(model, promptTokens, estimatedCompletionTokens) - log.output.cost = { - input: costInfo.input, - output: costInfo.output, - total: costInfo.total, - pricing: costInfo.pricing, - } - - logger.debug('Updated token information for streaming no-tool response', { - blockId: log.blockId, - contentLength, - estimatedCompletionTokens, - tokens: log.output.tokens, - }) - } - } - } - - // Special case for streaming responses from LLM blocks (agent, router, and evaluator) - // This format has both stream and executionData properties - if (log.output.stream && log.output.executionData) { - logger.debug('Found streaming response with executionData', { - blockId: log.blockId, - hasExecutionData: !!log.output.executionData, - executionDataKeys: log.output.executionData - ? Object.keys(log.output.executionData) - : [], - }) - - // Extract the executionData and use it as our primary source of information - const executionData = log.output.executionData - - // If executionData has output, merge it with our output - // This is especially important for streaming responses where the final content - // is set in the executionData structure by the executor - if (executionData.output) { - log.output = { ...log.output, ...executionData.output } - logger.debug('Using output from executionData', { - outputKeys: Object.keys(log.output), - hasContent: !!log.output.content, - contentLength: log.output.content?.length || 0, - hasToolCalls: !!log.output.toolCalls, - hasTokens: !!log.output.tokens, - hasCost: !!log.output.cost, - }) - } - } - - // Add cost information if available - if (log.output?.cost) { - const output = log.output - if (!metadata) metadata = {} - metadata.cost = { - model: output.model, - input: output.cost.input, - output: output.cost.output, - total: output.cost.total, - tokens: output.tokens, - pricing: output.cost.pricing, - } - - // Accumulate costs for workflow-level summary - if (output.cost.total) { - totalCost += output.cost.total - totalInputCost += output.cost.input || 0 - totalOutputCost += output.cost.output || 0 - - // Track tokens - if (output.tokens) { - totalPromptTokens += output.tokens.prompt || 0 - totalCompletionTokens += output.tokens.completion || 0 - totalTokens += output.tokens.total || 0 - } - - // Track model usage - if (output.model) { - modelCounts[output.model] = (modelCounts[output.model] || 0) + 1 - // Set the most frequently used model as primary - if (!primaryModel || modelCounts[output.model] > modelCounts[primaryModel]) { - primaryModel = output.model - } - } - } - } - - // Extract timing info - try various formats that providers might use - const blockStartTime = log.startedAt - const blockEndTime = log.endedAt || new Date().toISOString() - const blockDuration = log.durationMs || 0 - let toolCallData: any[] = [] - - // Case 1: Direct toolCalls array - if (Array.isArray(log.output.toolCalls)) { - // Log raw timing data for debugging - log.output.toolCalls.forEach((tc: any, idx: number) => { - logger.debug(`Tool call ${idx} raw timing data:`, { - name: stripCustomToolPrefix(tc.name), - startTime: tc.startTime, - endTime: tc.endTime, - duration: tc.duration, - timing: tc.timing, - argumentKeys: tc.arguments ? Object.keys(tc.arguments) : undefined, - }) - }) - - toolCallData = log.output.toolCalls.map((toolCall: any) => { - // Extract timing info - try various formats that providers might use - const duration = extractDuration(toolCall) - const timing = extractTimingInfo( - toolCall, - blockStartTime ? new Date(blockStartTime) : undefined, - blockEndTime ? new Date(blockEndTime) : undefined - ) - - return { - name: toolCall.name, - duration: duration, - startTime: timing.startTime, - endTime: timing.endTime, - status: toolCall.error ? 'error' : 'success', - input: toolCall.input || toolCall.arguments, - output: toolCall.output || toolCall.result, - error: toolCall.error, - } - }) - } - // Case 2: toolCalls with a list array (as seen in the screenshot) - else if (log.output.toolCalls && Array.isArray(log.output.toolCalls.list)) { - // Log raw timing data for debugging - log.output.toolCalls.list.forEach((tc: any, idx: number) => { - logger.debug(`Tool call list ${idx} raw timing data:`, { - name: stripCustomToolPrefix(tc.name), - startTime: tc.startTime, - endTime: tc.endTime, - duration: tc.duration, - timing: tc.timing, - argumentKeys: tc.arguments ? Object.keys(tc.arguments) : undefined, - }) - }) - - toolCallData = log.output.toolCalls.list.map((toolCall: any) => { - // Extract timing info - try various formats that providers might use - const duration = extractDuration(toolCall) - const timing = extractTimingInfo( - toolCall, - blockStartTime ? new Date(blockStartTime) : undefined, - blockEndTime ? new Date(blockEndTime) : undefined - ) - - // Log what we extracted - logger.debug('Tool call list timing extracted:', { - name: toolCall.name, - extracted_duration: duration, - extracted_startTime: timing.startTime, - extracted_endTime: timing.endTime, - }) - - return { - name: toolCall.name, - duration: duration, - startTime: timing.startTime, - endTime: timing.endTime, - status: toolCall.error ? 'error' : 'success', - input: toolCall.arguments || toolCall.input, - output: toolCall.result || toolCall.output, - error: toolCall.error, - } - }) - } - // Case 3: toolCalls is an object and has a list property - else if ( - log.output.toolCalls && - typeof log.output.toolCalls === 'object' && - log.output.toolCalls.list - ) { - const toolCalls = log.output.toolCalls - - logger.debug('Found toolCalls object with list property', { - count: toolCalls.list.length, - }) - - // Log raw timing data for debugging - toolCalls.list.forEach((tc: any, idx: number) => { - logger.debug(`toolCalls object list ${idx} raw timing data:`, { - name: stripCustomToolPrefix(tc.name), - startTime: tc.startTime, - endTime: tc.endTime, - duration: tc.duration, - timing: tc.timing, - argumentKeys: tc.arguments ? Object.keys(tc.arguments) : undefined, - }) - }) - - toolCallData = toolCalls.list.map((toolCall: any) => { - // Extract timing info - try various formats that providers might use - const duration = extractDuration(toolCall) - const timing = extractTimingInfo( - toolCall, - blockStartTime ? new Date(blockStartTime) : undefined, - blockEndTime ? new Date(blockEndTime) : undefined - ) - - // Log what we extracted - logger.debug('toolCalls object list timing extracted:', { - name: toolCall.name, - extracted_duration: duration, - extracted_startTime: timing.startTime, - extracted_endTime: timing.endTime, - }) - - return { - name: toolCall.name, - duration: duration, - startTime: timing.startTime, - endTime: timing.endTime, - status: toolCall.error ? 'error' : 'success', - input: toolCall.arguments || toolCall.input, - output: toolCall.result || toolCall.output, - error: toolCall.error, - } - }) - } - // Case 4: Look in executionData.output for streaming responses - else if (log.output.executionData?.output?.toolCalls) { - const toolCallsObj = log.output.executionData.output.toolCalls - const list = Array.isArray(toolCallsObj) ? toolCallsObj : toolCallsObj.list || [] - - logger.debug('Found toolCalls in executionData output response', { - count: list.length, - }) - - // Log raw timing data for debugging - list.forEach((tc: any, idx: number) => { - logger.debug(`executionData toolCalls ${idx} raw timing data:`, { - name: stripCustomToolPrefix(tc.name), - startTime: tc.startTime, - endTime: tc.endTime, - duration: tc.duration, - timing: tc.timing, - argumentKeys: tc.arguments ? Object.keys(tc.arguments) : undefined, - }) - }) - - toolCallData = list.map((toolCall: any) => { - // Extract timing info - try various formats that providers might use - const duration = extractDuration(toolCall) - const timing = extractTimingInfo( - toolCall, - blockStartTime ? new Date(blockStartTime) : undefined, - blockEndTime ? new Date(blockEndTime) : undefined - ) - - return { - name: toolCall.name, - duration: duration, - startTime: timing.startTime, - endTime: timing.endTime, - status: toolCall.error ? 'error' : 'success', - input: toolCall.arguments || toolCall.input, - output: toolCall.result || toolCall.output, - error: toolCall.error, - } - }) - } - // Case 5: Parse the output string for toolCalls as a last resort - else if (typeof log.output === 'string') { - const match = log.output.match(/"toolCalls"\s*:\s*({[^}]*}|(\[.*?\]))/s) - if (match) { - try { - const toolCallsJson = JSON.parse(`{${match[0]}}`) - const list = Array.isArray(toolCallsJson.toolCalls) - ? toolCallsJson.toolCalls - : toolCallsJson.toolCalls.list || [] - - logger.debug('Found toolCalls in parsed response string', { - count: list.length, - }) - - // Log raw timing data for debugging - list.forEach((tc: any, idx: number) => { - logger.debug(`Parsed response ${idx} raw timing data:`, { - name: stripCustomToolPrefix(tc.name), - startTime: tc.startTime, - endTime: tc.endTime, - duration: tc.duration, - timing: tc.timing, - argumentKeys: tc.arguments ? Object.keys(tc.arguments) : undefined, - }) - }) - - toolCallData = list.map((toolCall: any) => { - // Extract timing info - try various formats that providers might use - const duration = extractDuration(toolCall) - const timing = extractTimingInfo( - toolCall, - blockStartTime ? new Date(blockStartTime) : undefined, - blockEndTime ? new Date(blockEndTime) : undefined - ) - - // Log what we extracted - logger.debug('Parsed response timing extracted:', { - name: toolCall.name, - extracted_duration: duration, - extracted_startTime: timing.startTime, - extracted_endTime: timing.endTime, - }) - - return { - name: toolCall.name, - duration: duration, - startTime: timing.startTime, - endTime: timing.endTime, - status: toolCall.error ? 'error' : 'success', - input: toolCall.arguments || toolCall.input, - output: toolCall.result || toolCall.output, - error: toolCall.error, - } - }) - } catch (error) { - logger.error('Error parsing toolCalls from output string', { - error, - output: log.output, - }) - } - } - } - // Verbose output debugging as a fallback - else { - logger.debug('Could not find tool calls in standard formats, output data:', { - outputSample: `${JSON.stringify(log.output).substring(0, 500)}...`, - }) - } - - // Fill in missing timing information and merge with existing metadata - if (toolCallData.length > 0) { - const getToolCalls = getToolCallTimings( - toolCallData, - blockStartTime, - blockEndTime, - blockDuration - ) - - const redactedToolCalls = getToolCalls.map((toolCall) => ({ - ...toolCall, - input: redactApiKeys(toolCall.input), - })) - - // Merge with existing metadata instead of overwriting - if (!metadata) metadata = {} - metadata.toolCalls = redactedToolCalls - - logger.debug('Added tool calls to metadata', { - count: redactedToolCalls.length, - existingMetadata: Object.keys(metadata).filter((k) => k !== 'toolCalls'), - }) - } - } - - await persistLog({ - id: uuidv4(), - workflowId, - executionId, - level: log.success ? 'info' : 'error', - message: log.success - ? `Block ${log.blockName || log.blockId} (${log.blockType || 'unknown'}): ${ - log.output?.content || - log.output?.executionData?.output?.content || - JSON.stringify(log.output || {}) - }` - : `Block ${log.blockName || log.blockId} (${log.blockType || 'unknown'}): ${log.error || 'Failed'}`, - duration: log.success ? `${log.durationMs}ms` : 'NA', - trigger: triggerType, - createdAt: new Date(log.endedAt || log.startedAt), - metadata: { - ...metadata, - ...(log.input ? { blockInput: log.input } : {}), - }, - }) - - if (metadata) { - logger.debug('Persisted log with metadata', { - logId: uuidv4(), - executionId, - toolCallCount: metadata.toolCalls?.length || 0, - }) - } - } - - // Calculate total duration from successful block logs - const totalDuration = (result.logs || []) - .filter((log) => log.success) - .reduce((sum, log) => sum + log.durationMs, 0) - - // For parallel execution, calculate the actual duration from start to end times - let actualDuration = totalDuration - if (result.metadata?.startTime && result.metadata?.endTime) { - const startTime = result.metadata.startTime - ? new Date(result.metadata.startTime).getTime() - : 0 - const endTime = new Date(result.metadata.endTime).getTime() - actualDuration = endTime - startTime - } - - // Get trigger-specific message - const successMessage = getTriggerSuccessMessage(triggerType) - const errorPrefix = getTriggerErrorPrefix(triggerType) - - // Create workflow-level metadata with aggregated cost information - const workflowMetadata: any = { - traceSpans: (result as any).traceSpans || [], - totalDuration: (result as any).totalDuration || actualDuration, - } - - // Add accumulated cost data to workflow-level log - if (totalCost > 0) { - workflowMetadata.cost = { - model: primaryModel, - input: totalInputCost, - output: totalOutputCost, - total: totalCost, - tokens: { - prompt: totalPromptTokens, - completion: totalCompletionTokens, - total: totalTokens, - }, - } - - // Include pricing info if we have a model - if (primaryModel && result.logs && result.logs.length > 0) { - // Find the first agent log with pricing info - for (const log of result.logs) { - if (log.output?.cost?.pricing) { - workflowMetadata.cost.pricing = log.output.cost.pricing - break - } - } - } - - // If result has a direct cost field (for streaming responses completed with calculated cost), - // use that as a safety check to ensure we have cost data - if ( - result.metadata && - 'cost' in result.metadata && - (!workflowMetadata.cost || workflowMetadata.cost.total <= 0) - ) { - const resultCost = (result.metadata as any).cost - workflowMetadata.cost = { - model: primaryModel, - total: typeof resultCost === 'number' ? resultCost : resultCost?.total || 0, - input: resultCost?.input || 0, - output: resultCost?.output || 0, - tokens: { - prompt: totalPromptTokens, - completion: totalCompletionTokens, - total: totalTokens, - }, - } - } - - if (userId) { - try { - const userStatsRecords = await db - .select() - .from(userStats) - .where(eq(userStats.userId, userId)) - - const costMultiplier = getCostMultiplier() - const costToStore = totalCost * costMultiplier - - if (userStatsRecords.length === 0) { - await db.insert(userStats).values({ - id: crypto.randomUUID(), - userId: userId, - totalManualExecutions: 0, - totalApiCalls: 0, - totalWebhookTriggers: 0, - totalScheduledExecutions: 0, - totalChatExecutions: 0, - totalTokensUsed: totalTokens, - totalCost: costToStore.toString(), - currentPeriodCost: costToStore.toString(), // Initialize current period usage - lastActive: new Date(), - }) - } else { - await db - .update(userStats) - .set({ - totalTokensUsed: sql`total_tokens_used + ${totalTokens}`, - totalCost: sql`total_cost + ${costToStore}`, - currentPeriodCost: sql`current_period_cost + ${costToStore}`, // Track current billing period usage - lastActive: new Date(), - }) - .where(eq(userStats.userId, userId)) - } - } catch (error) { - logger.error('Error upserting user stats:', error) - } - } - } - - // Log the final execution result - await persistLog({ - id: uuidv4(), - workflowId, - executionId, - level: result.success ? 'info' : 'error', - message: result.success ? successMessage : `${errorPrefix} execution failed: ${result.error}`, - duration: result.success ? `${actualDuration}ms` : 'NA', - trigger: triggerType, - createdAt: new Date(), - metadata: workflowMetadata, - }) - } catch (error: any) { - logger.error(`Error persisting execution logs: ${error.message}`, { - error, - }) - } -} - -/** - * Persists an error log for a workflow execution - * @param workflowId - The ID of the workflow - * @param executionId - The ID of the execution - * @param error - The error that occurred - * @param triggerType - The type of trigger (api, webhook, schedule, manual, chat) - */ -export async function persistExecutionError( - workflowId: string, - executionId: string, - error: Error, - triggerType: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' -) { - try { - const errorPrefix = getTriggerErrorPrefix(triggerType) - - await persistLog({ - id: uuidv4(), - workflowId, - executionId, - level: 'error', - message: `${errorPrefix} execution failed: ${error.message}`, - duration: 'NA', - trigger: triggerType, - createdAt: new Date(), - }) - } catch (logError: any) { - logger.error(`Error persisting execution error log: ${logError.message}`, { - logError, - }) - } -} - -// Helper functions for trigger-specific messages -function getTriggerSuccessMessage( - triggerType: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' -): string { - switch (triggerType) { - case 'api': - return 'API workflow executed successfully' - case 'webhook': - return 'Webhook workflow executed successfully' - case 'schedule': - return 'Scheduled workflow executed successfully' - case 'manual': - return 'Manual workflow executed successfully' - case 'chat': - return 'Chat workflow executed successfully' - default: - return 'Workflow executed successfully' - } -} - -function getTriggerErrorPrefix( - triggerType: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' -): string { - switch (triggerType) { - case 'api': - return 'API workflow' - case 'webhook': - return 'Webhook workflow' - case 'schedule': - return 'Scheduled workflow' - case 'manual': - return 'Manual workflow' - case 'chat': - return 'Chat workflow' - default: - return 'Workflow' - } -} - -/** - * Extracts duration information for tool calls - * This function preserves actual timing data while ensuring duration is calculated - */ -function getToolCallTimings( - toolCalls: any[], - blockStart: string, - blockEnd: string, - totalDuration: number -): any[] { - if (!toolCalls || toolCalls.length === 0) return [] - - logger.debug('Estimating tool call timings', { - toolCallCount: toolCalls.length, - blockStartTime: blockStart, - blockEndTime: blockEnd, - totalDuration, - }) - - // First, try to preserve any existing timing data - const result = toolCalls.map((toolCall, index) => { - // Start with the original tool call - const enhancedToolCall = { ...toolCall } - - // If we don't have timing data, set it from the block timing info - // Divide block duration evenly among tools as a fallback - const toolDuration = totalDuration / toolCalls.length - const toolStartOffset = index * toolDuration - - // Force a minimum duration of 1000ms if none exists - if (!enhancedToolCall.duration || enhancedToolCall.duration === 0) { - enhancedToolCall.duration = Math.max(1000, toolDuration) - } - - // Force reasonable startTime and endTime if missing - if (!enhancedToolCall.startTime) { - const startTimestamp = new Date(blockStart).getTime() + toolStartOffset - enhancedToolCall.startTime = new Date(startTimestamp).toISOString() - } - - if (!enhancedToolCall.endTime) { - const endTimestamp = - new Date(enhancedToolCall.startTime).getTime() + enhancedToolCall.duration - enhancedToolCall.endTime = new Date(endTimestamp).toISOString() - } - - return enhancedToolCall - }) - - return result -} - -/** - * Extracts the duration from a tool call object, trying various property formats - * that different agent providers might use - */ -function extractDuration(toolCall: any): number { - if (!toolCall) return 0 - - // Direct duration fields (various formats providers might use) - if (typeof toolCall.duration === 'number' && toolCall.duration > 0) return toolCall.duration - if (typeof toolCall.durationMs === 'number' && toolCall.durationMs > 0) return toolCall.durationMs - if (typeof toolCall.duration_ms === 'number' && toolCall.duration_ms > 0) - return toolCall.duration_ms - if (typeof toolCall.executionTime === 'number' && toolCall.executionTime > 0) - return toolCall.executionTime - if (typeof toolCall.execution_time === 'number' && toolCall.execution_time > 0) - return toolCall.execution_time - if (typeof toolCall.timing?.duration === 'number' && toolCall.timing.duration > 0) - return toolCall.timing.duration - - // Try to calculate from timestamps if available - if (toolCall.startTime && toolCall.endTime) { - try { - const start = new Date(toolCall.startTime).getTime() - const end = new Date(toolCall.endTime).getTime() - if (!Number.isNaN(start) && !Number.isNaN(end) && end >= start) { - return end - start - } - } catch (_e) { - // Silently fail if date parsing fails - } - } - - // Also check for startedAt/endedAt format - if (toolCall.startedAt && toolCall.endedAt) { - try { - const start = new Date(toolCall.startedAt).getTime() - const end = new Date(toolCall.endedAt).getTime() - if (!Number.isNaN(start) && !Number.isNaN(end) && end >= start) { - return end - start - } - } catch (_e) { - // Silently fail if date parsing fails - } - } - - // For some providers, timing info might be in a separate object - if (toolCall.timing) { - if (toolCall.timing.startTime && toolCall.timing.endTime) { - try { - const start = new Date(toolCall.timing.startTime).getTime() - const end = new Date(toolCall.timing.endTime).getTime() - if (!Number.isNaN(start) && !Number.isNaN(end) && end >= start) { - return end - start - } - } catch (_e) { - // Silently fail if date parsing fails - } - } - } - - // No duration info found - return 0 -} - -/** - * Extract timing information from a tool call object - * @param toolCall The tool call object - * @param blockStartTime Optional block start time (for reference, not used as fallback anymore) - * @param blockEndTime Optional block end time (for reference, not used as fallback anymore) - * @returns Object with startTime and endTime properties - */ -function extractTimingInfo( - toolCall: any, - blockStartTime?: Date, - blockEndTime?: Date -): { startTime?: Date; endTime?: Date } { - let startTime: Date | undefined - let endTime: Date | undefined - - // Try to get direct timing properties - if (toolCall.startTime && isValidDate(toolCall.startTime)) { - startTime = new Date(toolCall.startTime) - } else if (toolCall.timing?.startTime && isValidDate(toolCall.timing.startTime)) { - startTime = new Date(toolCall.timing.startTime) - } else if (toolCall.timing?.start && isValidDate(toolCall.timing.start)) { - startTime = new Date(toolCall.timing.start) - } else if (toolCall.startedAt && isValidDate(toolCall.startedAt)) { - startTime = new Date(toolCall.startedAt) - } - - if (toolCall.endTime && isValidDate(toolCall.endTime)) { - endTime = new Date(toolCall.endTime) - } else if (toolCall.timing?.endTime && isValidDate(toolCall.timing.endTime)) { - endTime = new Date(toolCall.timing.endTime) - } else if (toolCall.timing?.end && isValidDate(toolCall.timing.end)) { - endTime = new Date(toolCall.timing.end) - } else if (toolCall.completedAt && isValidDate(toolCall.completedAt)) { - endTime = new Date(toolCall.completedAt) - } - - if (startTime && !endTime) { - const duration = extractDuration(toolCall) - if (duration > 0) { - endTime = new Date(startTime.getTime() + duration) - } - } - - logger.debug('Final extracted timing info', { - tool: toolCall.name, - startTime: startTime?.toISOString(), - endTime: endTime?.toISOString(), - hasStartTime: !!startTime, - hasEndTime: !!endTime, - }) - - return { startTime, endTime } -} - -/** - * Helper function to check if a string is a valid date - */ -function isValidDate(dateString: string): boolean { - if (!dateString) return false - - try { - const timestamp = Date.parse(dateString) - return !Number.isNaN(timestamp) - } catch (_e) { - return false - } -}