-
Notifications
You must be signed in to change notification settings - Fork 176
feat: implement DB Agent routing in LangGraph workflow #3496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
695e79f
7f23211
aa36dcc
9f3e8c2
ef325e0
427b1b8
8aac543
2414945
895ca3d
ef10307
4376bcb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,15 +5,18 @@ | |
* Important: Node retries do NOT count toward this limit. The limit only | ||
* applies to transitions between nodes. | ||
* | ||
* The workflow has 8 nodes: | ||
* - Normal execution: 9 transitions (START → 8 nodes → END) | ||
* - With error loops: May have additional transitions when errors occur | ||
* (e.g., validateSchema → designSchema) | ||
* TEMPORARY LIMITATION (set to 10): | ||
* Due to issues with schemaDesignTool, the DB Agent cannot resolve schema issues | ||
* on the second and subsequent attempts, causing infinite loops between: | ||
* leadAgent → dbAgent → qaAgent → leadAgent (when schemaIssues exist) | ||
* | ||
* Setting this to 50 ensures: | ||
* - Complete workflow execution under normal conditions | ||
* - Ample headroom for complex error handling loops and retries | ||
* - Protection against infinite loops while allowing for complex workflows | ||
* - Sufficient capacity for finding optimal workflow patterns | ||
* Current behavior with limit=10: | ||
* - Allows multiple iterations of: PM Agent → DB Agent → QA Agent → Lead Agent → DB Agent | ||
* - The workflow will fail after 10 loops if issues persist | ||
* - Provides more opportunities for the DB Agent to refine the schema | ||
* | ||
* TODO: Increase this limit after fixing schemaDesignTool to properly handle | ||
* schema modifications (e.g., unique constraint issues, JSON patch errors) | ||
* See: route06/liam-internal#5642 | ||
*/ | ||
Comment on lines
+8
to
21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainVerify impact of low recursion limit on legitimate workflows. A recursion limit of 10 is extremely restrictive and, as the comment acknowledges, will cause workflow failures after 10 loops. While the temporary nature is documented, this could break legitimate multi-step design sessions or complex schema scenarios. Consider the following:
Run the following script to check for existing workflows or tests that might be affected: Additionally, consider searching documentation for guidance on recursion limits: 🏁 Script executed: #!/bin/bash
# Search for integration tests or workflow configurations that might be sensitive to recursion limits
rg -n -C3 --type=ts 'recursionLimit|streamEvents.*config' | head -50 Length of output: 4241 🌐 Web query:
💡 Result: Short answer — default is 25 super-steps; in production teams typically keep the default for simple graphs and raise it for complex workflows (common ranges: 50–200, sometimes higher) while adding monitoring/guards. See below for sources and guidance.
If you want, tell me your graph’s structure (depth, loops, parallel branches, typical LLM call count) and I’ll recommend a concrete recursion_limit and monitoring configuration. Sources: [1] LangGraph low‑level docs (recursion_limit default 25). [2] LangGraph how‑to: control recursion limit. [3] LangChain docs / migration guide (examples of raising recursionLimit). [4] LangGraph troubleshooting / community discussions recommending higher limits. 🏁 Script executed: #!/bin/bash
rg -n "DEFAULT_RECURSION_LIMIT" -C2 --type=ts Length of output: 5998 Raise DEFAULT_RECURSION_LIMIT above 10
🤖 Prompt for AI Agents
|
||
export const DEFAULT_RECURSION_LIMIT = 50 | ||
export const DEFAULT_RECURSION_LIMIT = 10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { END } from '@langchain/langgraph' | ||
import { describe, expect, it } from 'vitest' | ||
import type { WorkflowState } from '../../types' | ||
import { classifyMessage } from './index' | ||
|
||
const createMockState = ( | ||
overrides: Partial<WorkflowState> = {}, | ||
): WorkflowState => ({ | ||
messages: [], | ||
analyzedRequirements: { | ||
goal: '', | ||
testcases: {}, | ||
}, | ||
schemaIssues: [], | ||
schemaData: { tables: {}, enums: {}, extensions: {} }, | ||
buildingSchemaId: 'test-schema-id', | ||
organizationId: 'test-org-id', | ||
userId: 'test-user-id', | ||
designSessionId: 'test-session-id', | ||
next: END, | ||
...overrides, | ||
}) | ||
|
||
describe('classifyMessage', () => { | ||
it('should route to dbAgent when schema issues exist', async () => { | ||
const state = createMockState({ | ||
schemaIssues: [ | ||
{ | ||
testcaseId: 'test-1', | ||
description: 'Missing foreign key constraint', | ||
}, | ||
], | ||
}) | ||
|
||
const result = await classifyMessage(state) | ||
|
||
expect(result.goto).toEqual([END]) | ||
expect(result.update).toEqual({ next: 'dbAgent' }) | ||
}) | ||
|
||
it('should route to dbAgent when multiple schema issues exist', async () => { | ||
const state = createMockState({ | ||
schemaIssues: [ | ||
{ | ||
testcaseId: 'test-1', | ||
description: 'Missing foreign key constraint', | ||
}, | ||
{ testcaseId: 'test-2', description: 'Invalid table name' }, | ||
], | ||
}) | ||
|
||
const result = await classifyMessage(state) | ||
|
||
expect(result.goto).toEqual([END]) | ||
expect(result.update).toEqual({ next: 'dbAgent' }) | ||
}) | ||
|
||
it('should prioritize schema issues over QA completion', async () => { | ||
const state = createMockState({ | ||
schemaIssues: [ | ||
{ testcaseId: 'test-1', description: 'Schema issue found' }, | ||
], | ||
analyzedRequirements: { | ||
goal: 'Test goal', | ||
testcases: { | ||
functional: [ | ||
{ | ||
id: 'test-1', | ||
title: 'Test case', | ||
type: 'SELECT', | ||
sql: 'SELECT 1', | ||
testResults: [], | ||
}, | ||
], | ||
}, | ||
}, | ||
}) | ||
|
||
const result = await classifyMessage(state) | ||
|
||
expect(result.goto).toEqual([END]) | ||
expect(result.update).toEqual({ next: 'dbAgent' }) | ||
}) | ||
|
||
it('should route to summarizeWorkflow when QA completed and no schema issues', async () => { | ||
const state = createMockState({ | ||
schemaIssues: [], | ||
analyzedRequirements: { | ||
goal: 'Test goal', | ||
testcases: { | ||
functional: [ | ||
{ | ||
id: 'test-1', | ||
title: 'Test case', | ||
type: 'SELECT', | ||
sql: 'SELECT 1', | ||
testResults: [], | ||
}, | ||
], | ||
}, | ||
}, | ||
}) | ||
|
||
const result = await classifyMessage(state) | ||
|
||
expect(result.goto).toEqual(['summarizeWorkflow']) | ||
expect(result.update).toBeUndefined() | ||
}) | ||
|
||
it('should route to pmAgent when no schema issues and QA not completed', async () => { | ||
const state = createMockState({ | ||
schemaIssues: [], | ||
analyzedRequirements: { | ||
goal: 'Test goal', | ||
testcases: {}, | ||
}, | ||
}) | ||
|
||
const result = await classifyMessage(state) | ||
|
||
expect(result.goto).toEqual([END]) | ||
expect(result.update).toEqual({ next: 'pmAgent' }) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,22 @@ | ||
import { Command, END } from '@langchain/langgraph' | ||
import type { WorkflowState } from '../../types' | ||
import { isQACompleted } from '../utils/workflowStatus' | ||
import { isQACompleted, shouldRouteDBAgent } from '../utils/workflowStatus' | ||
|
||
export async function classifyMessage(state: WorkflowState): Promise<Command> { | ||
// 1. Check if QA is completed first (highest priority) | ||
// 1. Check if DB Agent routing is needed (highest priority) | ||
if (shouldRouteDBAgent(state)) { | ||
return new Command({ | ||
update: { next: 'dbAgent' }, | ||
goto: END, | ||
}) | ||
} | ||
Comment on lines
+7
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
// 2. Check if QA is completed (second priority) | ||
if (isQACompleted(state)) { | ||
return new Command({ goto: 'summarizeWorkflow' }) | ||
} | ||
|
||
// 2. Direct routing to pmAgent | ||
// 3. Direct routing to pmAgent (default) | ||
return new Command({ | ||
update: { next: 'pmAgent' }, | ||
goto: END, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the hard tab character.
This line contains a hard tab character. Replace it with spaces for consistency with the rest of the file.
As per coding guidelines (markdownlint-cli2 MD010).
Apply this diff:
📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
24-24: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🤖 Prompt for AI Agents