(deps: AgentDeps, opts: BuildGraphOptions = {})
| 75 | } |
| 76 | |
| 77 | export function buildGraph(deps: AgentDeps, opts: BuildGraphOptions = {}): Compiled { |
| 78 | const { config } = deps; |
| 79 | const analysts = opts.selectedAnalysts ?? DEFAULT_ANALYSTS; |
| 80 | const graph = new StateGraph(GraphState) as unknown as GraphBuilder; |
| 81 | |
| 82 | type Node = (state: GraphStateType) => Promise<Partial<GraphStateType>>; |
| 83 | const node = (fn: AgentNode): Node => fn as unknown as Node; |
| 84 | |
| 85 | // --- Analysts (serial chain) --- |
| 86 | for (const key of analysts) { |
| 87 | graph.addNode(ANALYST_NODE[key], node(ANALYST_FACTORY[key](deps))); |
| 88 | } |
| 89 | // --- Researchers + manager --- |
| 90 | graph.addNode("Bull Researcher", node(createBullResearcher(deps))); |
| 91 | graph.addNode("Bear Researcher", node(createBearResearcher(deps))); |
| 92 | graph.addNode("Research Manager", node(createResearchManager(deps))); |
| 93 | // --- Trader --- |
| 94 | graph.addNode("Trader", node(createTrader(deps))); |
| 95 | // --- Risk debators --- |
| 96 | graph.addNode("Aggressive Analyst", node(createAggressiveDebator(deps))); |
| 97 | graph.addNode("Conservative Analyst", node(createConservativeDebator(deps))); |
| 98 | graph.addNode("Neutral Analyst", node(createNeutralDebator(deps))); |
| 99 | // --- Portfolio manager --- |
| 100 | graph.addNode("Portfolio Manager", node(createPortfolioManager(deps))); |
| 101 | |
| 102 | // --- Edges: START → analyst chain → Bull --- |
| 103 | const analystNodes = analysts.map((k) => ANALYST_NODE[k]); |
| 104 | graph.addEdge(START, analystNodes[0]!); |
| 105 | for (let i = 0; i < analystNodes.length - 1; i++) { |
| 106 | graph.addEdge(analystNodes[i]!, analystNodes[i + 1]!); |
| 107 | } |
| 108 | graph.addEdge(analystNodes[analystNodes.length - 1]!, "Bull Researcher"); |
| 109 | |
| 110 | // --- Investment-debate loop (Bull ⇄ Bear → Research Manager) --- |
| 111 | const debateCond = (s: GraphStateType) => |
| 112 | shouldContinueDebate(s, config.maxDebateRounds); |
| 113 | graph.addConditionalEdges("Bull Researcher", debateCond, { |
| 114 | "Research Manager": "Research Manager", |
| 115 | "Bear Researcher": "Bear Researcher", |
| 116 | "Bull Researcher": "Bull Researcher", |
| 117 | }); |
| 118 | graph.addConditionalEdges("Bear Researcher", debateCond, { |
| 119 | "Research Manager": "Research Manager", |
| 120 | "Bear Researcher": "Bear Researcher", |
| 121 | "Bull Researcher": "Bull Researcher", |
| 122 | }); |
| 123 | |
| 124 | // --- Research Manager → Trader → first risk debator --- |
| 125 | graph.addEdge("Research Manager", "Trader"); |
| 126 | graph.addEdge("Trader", "Aggressive Analyst"); |
| 127 | |
| 128 | // --- Risk-debate rotation (Aggressive → Conservative → Neutral → PM) --- |
| 129 | const riskCond = (s: GraphStateType) => |
| 130 | shouldContinueRiskAnalysis(s, config.maxRiskDiscussRounds); |
| 131 | const riskMap = { |
| 132 | "Portfolio Manager": "Portfolio Manager", |
| 133 | "Conservative Analyst": "Conservative Analyst", |
| 134 | "Neutral Analyst": "Neutral Analyst", |
no test coverage detected