Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion apps/docs/content/docs/tools/knowledge.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ In Sim Studio, the Knowledge Base block enables your agents to perform intellige

## Usage Instructions

Perform semantic vector search across one or more knowledge bases or upload new chunks to documents. Uses advanced AI embeddings to understand meaning and context for search operations.
Perform semantic vector search across knowledge bases, upload individual chunks to existing documents, or create new documents from text content. Uses advanced AI embeddings to understand meaning and context for search operations.



Expand Down Expand Up @@ -100,6 +100,25 @@ Upload a new chunk to a document in a knowledge base
| `createdAt` | string |
| `updatedAt` | string |

### `knowledge_create_document`

Create a new document in a knowledge base

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `knowledgeBaseId` | string | Yes | ID of the knowledge base containing the document |
| `name` | string | Yes | Name of the document |
| `content` | string | Yes | Content of the document |

#### Output

| Parameter | Type |
| --------- | ---- |
| `data` | string |
| `name` | string |



## Block Configuration
Expand Down
28 changes: 15 additions & 13 deletions apps/sim/app/api/chat/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,12 @@ export async function executeWorkflowForChat(

logger.debug(`[${requestId}] Using ${outputBlockIds.length} output blocks for extraction`)

// Find the workflow
// Find the workflow (deployedState is NOT deprecated - needed for chat execution)
const workflowResult = await db
.select({
state: workflow.state,
deployedState: workflow.deployedState,
isDeployed: workflow.isDeployed,
deployedState: workflow.deployedState,
variables: workflow.variables,
})
.from(workflow)
.where(eq(workflow.id, workflowId))
Expand All @@ -308,9 +308,14 @@ export async function executeWorkflowForChat(
throw new Error('Workflow not available')
}

// Use deployed state for execution
const state = workflowResult[0].deployedState || workflowResult[0].state
const { blocks, edges, loops, parallels } = state as WorkflowState
// For chat execution, use ONLY the deployed state (no fallback)
if (!workflowResult[0].deployedState) {
throw new Error(`Workflow must be deployed to be available for chat`)
}

// Use deployed state for chat execution (this is the stable, deployed version)
const deployedState = workflowResult[0].deployedState as WorkflowState
const { blocks, edges, loops, parallels } = deployedState

// Prepare for execution, similar to use-workflow-execution.ts
const mergedStates = mergeSubblockState(blocks)
Expand Down Expand Up @@ -344,16 +349,13 @@ export async function executeWorkflowForChat(
logger.warn(`[${requestId}] Could not fetch environment variables:`, error)
}

// Get workflow variables
let workflowVariables = {}
try {
// The workflow state may contain variables
const workflowState = state as any
if (workflowState.variables) {
if (workflowResult[0].variables) {
workflowVariables =
typeof workflowState.variables === 'string'
? JSON.parse(workflowState.variables)
: workflowState.variables
typeof workflowResult[0].variables === 'string'
? JSON.parse(workflowResult[0].variables)
: workflowResult[0].variables
}
} catch (error) {
logger.warn(`[${requestId}] Could not parse workflow variables:`, error)
Expand Down
219 changes: 219 additions & 0 deletions apps/sim/app/api/function/execute/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,225 @@ describe('Function Execute API Route', () => {
})
})

describe('Enhanced Error Handling', () => {
it('should provide detailed syntax error with line content', async () => {
// Mock VM Script to throw a syntax error
const mockScript = vi.fn().mockImplementation(() => {
const error = new Error('Invalid or unexpected token')
error.name = 'SyntaxError'
error.stack = `user-function.js:5
description: "This has a missing closing quote
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

SyntaxError: Invalid or unexpected token
at new Script (node:vm:117:7)
at POST (/path/to/route.ts:123:24)`
throw error
})

vi.doMock('vm', () => ({
createContext: mockCreateContext,
Script: mockScript,
}))

const req = createMockRequest('POST', {
code: 'const obj = {\n name: "test",\n description: "This has a missing closing quote\n};\nreturn obj;',
timeout: 5000,
})

const { POST } = await import('./route')
const response = await POST(req)
const data = await response.json()

expect(response.status).toBe(500)
expect(data.success).toBe(false)
expect(data.error).toContain('Syntax Error')
expect(data.error).toContain('Line 3')
expect(data.error).toContain('description: "This has a missing closing quote')
expect(data.error).toContain('Invalid or unexpected token')
expect(data.error).toContain('(Check for missing quotes, brackets, or semicolons)')

// Check debug information
expect(data.debug).toBeDefined()
expect(data.debug.line).toBe(3)
expect(data.debug.errorType).toBe('SyntaxError')
expect(data.debug.lineContent).toBe('description: "This has a missing closing quote')
})

it('should provide detailed runtime error with line and column', async () => {
// Create the error object first
const runtimeError = new Error("Cannot read properties of null (reading 'someMethod')")
runtimeError.name = 'TypeError'
runtimeError.stack = `TypeError: Cannot read properties of null (reading 'someMethod')
at user-function.js:4:16
at user-function.js:9:3
at Script.runInContext (node:vm:147:14)`

// Mock successful script creation but runtime error
const mockScript = vi.fn().mockImplementation(() => ({
runInContext: vi.fn().mockRejectedValue(runtimeError),
}))

vi.doMock('vm', () => ({
createContext: mockCreateContext,
Script: mockScript,
}))

const req = createMockRequest('POST', {
code: 'const obj = null;\nreturn obj.someMethod();',
timeout: 5000,
})

const { POST } = await import('./route')
const response = await POST(req)
const data = await response.json()

expect(response.status).toBe(500)
expect(data.success).toBe(false)
expect(data.error).toContain('Type Error')
expect(data.error).toContain('Line 2')
expect(data.error).toContain('return obj.someMethod();')
expect(data.error).toContain('Cannot read properties of null')

// Check debug information
expect(data.debug).toBeDefined()
expect(data.debug.line).toBe(2)
expect(data.debug.column).toBe(16)
expect(data.debug.errorType).toBe('TypeError')
expect(data.debug.lineContent).toBe('return obj.someMethod();')
})

it('should handle ReferenceError with enhanced details', async () => {
// Create the error object first
const referenceError = new Error('undefinedVariable is not defined')
referenceError.name = 'ReferenceError'
referenceError.stack = `ReferenceError: undefinedVariable is not defined
at user-function.js:4:8
at Script.runInContext (node:vm:147:14)`

const mockScript = vi.fn().mockImplementation(() => ({
runInContext: vi.fn().mockRejectedValue(referenceError),
}))

vi.doMock('vm', () => ({
createContext: mockCreateContext,
Script: mockScript,
}))

const req = createMockRequest('POST', {
code: 'const x = 42;\nreturn undefinedVariable + x;',
timeout: 5000,
})

const { POST } = await import('./route')
const response = await POST(req)
const data = await response.json()

expect(response.status).toBe(500)
expect(data.success).toBe(false)
expect(data.error).toContain('Reference Error')
expect(data.error).toContain('Line 2')
expect(data.error).toContain('return undefinedVariable + x;')
expect(data.error).toContain('undefinedVariable is not defined')
})

it('should handle errors without line content gracefully', async () => {
const mockScript = vi.fn().mockImplementation(() => {
const error = new Error('Generic error without stack trace')
error.name = 'Error'
// No stack trace
throw error
})

vi.doMock('vm', () => ({
createContext: mockCreateContext,
Script: mockScript,
}))

const req = createMockRequest('POST', {
code: 'return "test";',
timeout: 5000,
})

const { POST } = await import('./route')
const response = await POST(req)
const data = await response.json()

expect(response.status).toBe(500)
expect(data.success).toBe(false)
expect(data.error).toBe('Generic error without stack trace')

// Should still have debug info, but without line details
expect(data.debug).toBeDefined()
expect(data.debug.errorType).toBe('Error')
expect(data.debug.line).toBeUndefined()
expect(data.debug.lineContent).toBeUndefined()
})

it('should extract line numbers from different stack trace formats', async () => {
const mockScript = vi.fn().mockImplementation(() => {
const error = new Error('Test error')
error.name = 'Error'
error.stack = `Error: Test error
at user-function.js:7:25
at async function
at Script.runInContext (node:vm:147:14)`
throw error
})

vi.doMock('vm', () => ({
createContext: mockCreateContext,
Script: mockScript,
}))

const req = createMockRequest('POST', {
code: 'const a = 1;\nconst b = 2;\nconst c = 3;\nconst d = 4;\nreturn a + b + c + d;',
timeout: 5000,
})

const { POST } = await import('./route')
const response = await POST(req)
const data = await response.json()

expect(response.status).toBe(500)
expect(data.success).toBe(false)

// Line 7 in VM should map to line 5 in user code (7 - 3 + 1 = 5)
expect(data.debug.line).toBe(5)
expect(data.debug.column).toBe(25)
expect(data.debug.lineContent).toBe('return a + b + c + d;')
})

it('should provide helpful suggestions for common syntax errors', async () => {
const mockScript = vi.fn().mockImplementation(() => {
const error = new Error('Unexpected end of input')
error.name = 'SyntaxError'
error.stack = 'user-function.js:4\nSyntaxError: Unexpected end of input'
throw error
})

vi.doMock('vm', () => ({
createContext: mockCreateContext,
Script: mockScript,
}))

const req = createMockRequest('POST', {
code: 'const obj = {\n name: "test"\n// Missing closing brace',
timeout: 5000,
})

const { POST } = await import('./route')
const response = await POST(req)
const data = await response.json()

expect(response.status).toBe(500)
expect(data.success).toBe(false)
expect(data.error).toContain('Syntax Error')
expect(data.error).toContain('Unexpected end of input')
expect(data.error).toContain('(Check for missing closing brackets or braces)')
})
})

describe('Utility Functions', () => {
it('should properly escape regex special characters', async () => {
// This tests the escapeRegExp function indirectly
Expand Down
Loading