Skip to content

Commit 1604ce4

Browse files
icecrasher321waleedlatif1aadamgoughAdam GoughVikhyath Mondreti
authored
v0.2.4: feat, improvement, fix (#595)
* feat(function): added more granular error logs for function execution for easier debugging (#593) * added more granular error logs for function execution * added tests * fixed syntax error reporting * feat(models): added temp controls for gpt-4.1 family of models (#594) * improvement(knowledge-upload): create and upload document to KB (#579) * improvement: added knowledge upload * improvement: added greptile comments (#579) * improvement: changed to text to doc (#579) * improvement: removed comment (#579) * added input validation, tested persistence of KB selector * update docs --------- Co-authored-by: Adam Gough <[email protected]> Co-authored-by: Waleed Latif <[email protected]> * fix(remove workflow.state usage): no more usage of deprecated state column in any routes (#586) * fix(remove workflow.state usage): no more usage of deprecated state col in routes * fix lint * fix chat route to only use deployed state * fix lint * better typing * remove useless logs * fix lint * restore workflow handler file * removed all other usages of deprecated 'state' column from workflows table, updated tests --------- Co-authored-by: Vikhyath Mondreti <[email protected]> Co-authored-by: Waleed Latif <[email protected]> --------- Co-authored-by: Waleed Latif <[email protected]> Co-authored-by: Adam Gough <[email protected]> Co-authored-by: Adam Gough <[email protected]> Co-authored-by: Vikhyath Mondreti <[email protected]>
1 parent 86168f1 commit 1604ce4

File tree

26 files changed

+1235
-204
lines changed

26 files changed

+1235
-204
lines changed

apps/docs/content/docs/tools/knowledge.mdx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ In Sim Studio, the Knowledge Base block enables your agents to perform intellige
4949

5050
## Usage Instructions
5151

52-
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.
52+
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.
5353

5454

5555

@@ -100,6 +100,25 @@ Upload a new chunk to a document in a knowledge base
100100
| `createdAt` | string |
101101
| `updatedAt` | string |
102102

103+
### `knowledge_create_document`
104+
105+
Create a new document in a knowledge base
106+
107+
#### Input
108+
109+
| Parameter | Type | Required | Description |
110+
| --------- | ---- | -------- | ----------- |
111+
| `knowledgeBaseId` | string | Yes | ID of the knowledge base containing the document |
112+
| `name` | string | Yes | Name of the document |
113+
| `content` | string | Yes | Content of the document |
114+
115+
#### Output
116+
117+
| Parameter | Type |
118+
| --------- | ---- |
119+
| `data` | string |
120+
| `name` | string |
121+
103122

104123

105124
## Block Configuration

apps/sim/app/api/chat/utils.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,12 @@ export async function executeWorkflowForChat(
292292

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

295-
// Find the workflow
295+
// Find the workflow (deployedState is NOT deprecated - needed for chat execution)
296296
const workflowResult = await db
297297
.select({
298-
state: workflow.state,
299-
deployedState: workflow.deployedState,
300298
isDeployed: workflow.isDeployed,
299+
deployedState: workflow.deployedState,
300+
variables: workflow.variables,
301301
})
302302
.from(workflow)
303303
.where(eq(workflow.id, workflowId))
@@ -308,9 +308,14 @@ export async function executeWorkflowForChat(
308308
throw new Error('Workflow not available')
309309
}
310310

311-
// Use deployed state for execution
312-
const state = workflowResult[0].deployedState || workflowResult[0].state
313-
const { blocks, edges, loops, parallels } = state as WorkflowState
311+
// For chat execution, use ONLY the deployed state (no fallback)
312+
if (!workflowResult[0].deployedState) {
313+
throw new Error(`Workflow must be deployed to be available for chat`)
314+
}
315+
316+
// Use deployed state for chat execution (this is the stable, deployed version)
317+
const deployedState = workflowResult[0].deployedState as WorkflowState
318+
const { blocks, edges, loops, parallels } = deployedState
314319

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

347-
// Get workflow variables
348352
let workflowVariables = {}
349353
try {
350-
// The workflow state may contain variables
351-
const workflowState = state as any
352-
if (workflowState.variables) {
354+
if (workflowResult[0].variables) {
353355
workflowVariables =
354-
typeof workflowState.variables === 'string'
355-
? JSON.parse(workflowState.variables)
356-
: workflowState.variables
356+
typeof workflowResult[0].variables === 'string'
357+
? JSON.parse(workflowResult[0].variables)
358+
: workflowResult[0].variables
357359
}
358360
} catch (error) {
359361
logger.warn(`[${requestId}] Could not parse workflow variables:`, error)

apps/sim/app/api/function/execute/route.test.ts

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,225 @@ describe('Function Execute API Route', () => {
391391
})
392392
})
393393

394+
describe('Enhanced Error Handling', () => {
395+
it('should provide detailed syntax error with line content', async () => {
396+
// Mock VM Script to throw a syntax error
397+
const mockScript = vi.fn().mockImplementation(() => {
398+
const error = new Error('Invalid or unexpected token')
399+
error.name = 'SyntaxError'
400+
error.stack = `user-function.js:5
401+
description: "This has a missing closing quote
402+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
403+
404+
SyntaxError: Invalid or unexpected token
405+
at new Script (node:vm:117:7)
406+
at POST (/path/to/route.ts:123:24)`
407+
throw error
408+
})
409+
410+
vi.doMock('vm', () => ({
411+
createContext: mockCreateContext,
412+
Script: mockScript,
413+
}))
414+
415+
const req = createMockRequest('POST', {
416+
code: 'const obj = {\n name: "test",\n description: "This has a missing closing quote\n};\nreturn obj;',
417+
timeout: 5000,
418+
})
419+
420+
const { POST } = await import('./route')
421+
const response = await POST(req)
422+
const data = await response.json()
423+
424+
expect(response.status).toBe(500)
425+
expect(data.success).toBe(false)
426+
expect(data.error).toContain('Syntax Error')
427+
expect(data.error).toContain('Line 3')
428+
expect(data.error).toContain('description: "This has a missing closing quote')
429+
expect(data.error).toContain('Invalid or unexpected token')
430+
expect(data.error).toContain('(Check for missing quotes, brackets, or semicolons)')
431+
432+
// Check debug information
433+
expect(data.debug).toBeDefined()
434+
expect(data.debug.line).toBe(3)
435+
expect(data.debug.errorType).toBe('SyntaxError')
436+
expect(data.debug.lineContent).toBe('description: "This has a missing closing quote')
437+
})
438+
439+
it('should provide detailed runtime error with line and column', async () => {
440+
// Create the error object first
441+
const runtimeError = new Error("Cannot read properties of null (reading 'someMethod')")
442+
runtimeError.name = 'TypeError'
443+
runtimeError.stack = `TypeError: Cannot read properties of null (reading 'someMethod')
444+
at user-function.js:4:16
445+
at user-function.js:9:3
446+
at Script.runInContext (node:vm:147:14)`
447+
448+
// Mock successful script creation but runtime error
449+
const mockScript = vi.fn().mockImplementation(() => ({
450+
runInContext: vi.fn().mockRejectedValue(runtimeError),
451+
}))
452+
453+
vi.doMock('vm', () => ({
454+
createContext: mockCreateContext,
455+
Script: mockScript,
456+
}))
457+
458+
const req = createMockRequest('POST', {
459+
code: 'const obj = null;\nreturn obj.someMethod();',
460+
timeout: 5000,
461+
})
462+
463+
const { POST } = await import('./route')
464+
const response = await POST(req)
465+
const data = await response.json()
466+
467+
expect(response.status).toBe(500)
468+
expect(data.success).toBe(false)
469+
expect(data.error).toContain('Type Error')
470+
expect(data.error).toContain('Line 2')
471+
expect(data.error).toContain('return obj.someMethod();')
472+
expect(data.error).toContain('Cannot read properties of null')
473+
474+
// Check debug information
475+
expect(data.debug).toBeDefined()
476+
expect(data.debug.line).toBe(2)
477+
expect(data.debug.column).toBe(16)
478+
expect(data.debug.errorType).toBe('TypeError')
479+
expect(data.debug.lineContent).toBe('return obj.someMethod();')
480+
})
481+
482+
it('should handle ReferenceError with enhanced details', async () => {
483+
// Create the error object first
484+
const referenceError = new Error('undefinedVariable is not defined')
485+
referenceError.name = 'ReferenceError'
486+
referenceError.stack = `ReferenceError: undefinedVariable is not defined
487+
at user-function.js:4:8
488+
at Script.runInContext (node:vm:147:14)`
489+
490+
const mockScript = vi.fn().mockImplementation(() => ({
491+
runInContext: vi.fn().mockRejectedValue(referenceError),
492+
}))
493+
494+
vi.doMock('vm', () => ({
495+
createContext: mockCreateContext,
496+
Script: mockScript,
497+
}))
498+
499+
const req = createMockRequest('POST', {
500+
code: 'const x = 42;\nreturn undefinedVariable + x;',
501+
timeout: 5000,
502+
})
503+
504+
const { POST } = await import('./route')
505+
const response = await POST(req)
506+
const data = await response.json()
507+
508+
expect(response.status).toBe(500)
509+
expect(data.success).toBe(false)
510+
expect(data.error).toContain('Reference Error')
511+
expect(data.error).toContain('Line 2')
512+
expect(data.error).toContain('return undefinedVariable + x;')
513+
expect(data.error).toContain('undefinedVariable is not defined')
514+
})
515+
516+
it('should handle errors without line content gracefully', async () => {
517+
const mockScript = vi.fn().mockImplementation(() => {
518+
const error = new Error('Generic error without stack trace')
519+
error.name = 'Error'
520+
// No stack trace
521+
throw error
522+
})
523+
524+
vi.doMock('vm', () => ({
525+
createContext: mockCreateContext,
526+
Script: mockScript,
527+
}))
528+
529+
const req = createMockRequest('POST', {
530+
code: 'return "test";',
531+
timeout: 5000,
532+
})
533+
534+
const { POST } = await import('./route')
535+
const response = await POST(req)
536+
const data = await response.json()
537+
538+
expect(response.status).toBe(500)
539+
expect(data.success).toBe(false)
540+
expect(data.error).toBe('Generic error without stack trace')
541+
542+
// Should still have debug info, but without line details
543+
expect(data.debug).toBeDefined()
544+
expect(data.debug.errorType).toBe('Error')
545+
expect(data.debug.line).toBeUndefined()
546+
expect(data.debug.lineContent).toBeUndefined()
547+
})
548+
549+
it('should extract line numbers from different stack trace formats', async () => {
550+
const mockScript = vi.fn().mockImplementation(() => {
551+
const error = new Error('Test error')
552+
error.name = 'Error'
553+
error.stack = `Error: Test error
554+
at user-function.js:7:25
555+
at async function
556+
at Script.runInContext (node:vm:147:14)`
557+
throw error
558+
})
559+
560+
vi.doMock('vm', () => ({
561+
createContext: mockCreateContext,
562+
Script: mockScript,
563+
}))
564+
565+
const req = createMockRequest('POST', {
566+
code: 'const a = 1;\nconst b = 2;\nconst c = 3;\nconst d = 4;\nreturn a + b + c + d;',
567+
timeout: 5000,
568+
})
569+
570+
const { POST } = await import('./route')
571+
const response = await POST(req)
572+
const data = await response.json()
573+
574+
expect(response.status).toBe(500)
575+
expect(data.success).toBe(false)
576+
577+
// Line 7 in VM should map to line 5 in user code (7 - 3 + 1 = 5)
578+
expect(data.debug.line).toBe(5)
579+
expect(data.debug.column).toBe(25)
580+
expect(data.debug.lineContent).toBe('return a + b + c + d;')
581+
})
582+
583+
it('should provide helpful suggestions for common syntax errors', async () => {
584+
const mockScript = vi.fn().mockImplementation(() => {
585+
const error = new Error('Unexpected end of input')
586+
error.name = 'SyntaxError'
587+
error.stack = 'user-function.js:4\nSyntaxError: Unexpected end of input'
588+
throw error
589+
})
590+
591+
vi.doMock('vm', () => ({
592+
createContext: mockCreateContext,
593+
Script: mockScript,
594+
}))
595+
596+
const req = createMockRequest('POST', {
597+
code: 'const obj = {\n name: "test"\n// Missing closing brace',
598+
timeout: 5000,
599+
})
600+
601+
const { POST } = await import('./route')
602+
const response = await POST(req)
603+
const data = await response.json()
604+
605+
expect(response.status).toBe(500)
606+
expect(data.success).toBe(false)
607+
expect(data.error).toContain('Syntax Error')
608+
expect(data.error).toContain('Unexpected end of input')
609+
expect(data.error).toContain('(Check for missing closing brackets or braces)')
610+
})
611+
})
612+
394613
describe('Utility Functions', () => {
395614
it('should properly escape regex special characters', async () => {
396615
// This tests the escapeRegExp function indirectly

0 commit comments

Comments
 (0)