redesign fully scaffolded and web login works
This commit is contained in:
138
gateway/src/harness/workflows/strategy-validation/graph.ts
Normal file
138
gateway/src/harness/workflows/strategy-validation/graph.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { StateGraph } from '@langchain/langgraph';
|
||||
import { BaseWorkflow, type WorkflowConfig } from '../base-workflow.js';
|
||||
import { StrategyValidationState, type StrategyValidationStateType } from './state.js';
|
||||
import {
|
||||
createCodeReviewNode,
|
||||
createFixCodeNode,
|
||||
createBacktestNode,
|
||||
createRiskAssessmentNode,
|
||||
createHumanApprovalNode,
|
||||
createRecommendationNode,
|
||||
} from './nodes.js';
|
||||
import type { FastifyBaseLogger } from 'fastify';
|
||||
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
||||
import type { CodeReviewerSubagent } from '../../subagents/code-reviewer/index.js';
|
||||
|
||||
/**
|
||||
* Strategy Validation Workflow
|
||||
*
|
||||
* Multi-step workflow with validation loop:
|
||||
* 1. Code Review (using CodeReviewerSubagent)
|
||||
* 2. If issues found → Fix Code → Loop back to Code Review
|
||||
* 3. Backtest (using user's MCP server)
|
||||
* 4. If backtest fails → Fix Code → Loop back to Code Review
|
||||
* 5. Risk Assessment
|
||||
* 6. Human Approval (pause for user input)
|
||||
* 7. Final Recommendation
|
||||
*
|
||||
* Features:
|
||||
* - Validation loop with max retries
|
||||
* - Human-in-the-loop approval gate
|
||||
* - Multi-file memory from CodeReviewerSubagent
|
||||
* - Comprehensive state tracking
|
||||
*/
|
||||
export class StrategyValidationWorkflow extends BaseWorkflow<StrategyValidationStateType> {
|
||||
constructor(
|
||||
config: WorkflowConfig,
|
||||
private model: BaseChatModel,
|
||||
private codeReviewer: CodeReviewerSubagent,
|
||||
private mcpBacktestFn: (code: string, ticker: string, timeframe: string) => Promise<Record<string, unknown>>,
|
||||
logger: FastifyBaseLogger
|
||||
) {
|
||||
super(config, logger);
|
||||
}
|
||||
|
||||
buildGraph(): any {
|
||||
const graph = new StateGraph(StrategyValidationState);
|
||||
|
||||
// Create nodes
|
||||
const codeReviewNode = createCodeReviewNode(this.codeReviewer, this.logger);
|
||||
const fixCodeNode = createFixCodeNode(this.model, this.logger);
|
||||
const backtestNode = createBacktestNode(this.mcpBacktestFn, this.logger);
|
||||
const riskAssessmentNode = createRiskAssessmentNode(this.model, this.logger);
|
||||
const humanApprovalNode = createHumanApprovalNode(this.logger);
|
||||
const recommendationNode = createRecommendationNode(this.model, this.logger);
|
||||
|
||||
// Add nodes to graph
|
||||
graph
|
||||
.addNode('code_review', codeReviewNode)
|
||||
.addNode('fix_code', fixCodeNode)
|
||||
.addNode('backtest', backtestNode)
|
||||
.addNode('risk_assessment', riskAssessmentNode)
|
||||
.addNode('human_approval', humanApprovalNode)
|
||||
.addNode('recommendation', recommendationNode);
|
||||
|
||||
// Define edges
|
||||
(graph as any).addEdge('__start__', 'code_review');
|
||||
|
||||
// Conditional: After code review, fix if needed or proceed to backtest
|
||||
(graph as any).addConditionalEdges('code_review', (state: any) => {
|
||||
if (state.needsFixing && state.validationRetryCount < 3) {
|
||||
return 'fix_code';
|
||||
}
|
||||
if (state.needsFixing && state.validationRetryCount >= 3) {
|
||||
return 'recommendation'; // Give up, generate rejection
|
||||
}
|
||||
return 'backtest';
|
||||
});
|
||||
|
||||
// After fixing code, loop back to code review
|
||||
(graph as any).addEdge('fix_code', 'code_review');
|
||||
|
||||
// Conditional: After backtest, fix if failed or proceed to risk assessment
|
||||
(graph as any).addConditionalEdges('backtest', (state: any) => {
|
||||
if (!state.backtestPassed && state.validationRetryCount < 3) {
|
||||
return 'fix_code';
|
||||
}
|
||||
if (!state.backtestPassed && state.validationRetryCount >= 3) {
|
||||
return 'recommendation'; // Give up
|
||||
}
|
||||
return 'risk_assessment';
|
||||
});
|
||||
|
||||
// After risk assessment, go to human approval
|
||||
(graph as any).addEdge('risk_assessment', 'human_approval');
|
||||
|
||||
// Conditional: After human approval, proceed to recommendation or reject
|
||||
(graph as any).addConditionalEdges('human_approval', (state: any) => {
|
||||
return state.humanApproved ? 'recommendation' : '__end__';
|
||||
});
|
||||
|
||||
// Final recommendation is terminal
|
||||
(graph as any).addEdge('recommendation', '__end__');
|
||||
|
||||
return graph;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory function to create and compile workflow
|
||||
*/
|
||||
export async function createStrategyValidationWorkflow(
|
||||
model: BaseChatModel,
|
||||
codeReviewer: CodeReviewerSubagent,
|
||||
mcpBacktestFn: (code: string, ticker: string, timeframe: string) => Promise<Record<string, unknown>>,
|
||||
logger: FastifyBaseLogger,
|
||||
configPath: string
|
||||
): Promise<StrategyValidationWorkflow> {
|
||||
const { readFile } = await import('fs/promises');
|
||||
const yaml = await import('js-yaml');
|
||||
|
||||
// Load config
|
||||
const configContent = await readFile(configPath, 'utf-8');
|
||||
const config = yaml.load(configContent) as WorkflowConfig;
|
||||
|
||||
// Create workflow
|
||||
const workflow = new StrategyValidationWorkflow(
|
||||
config,
|
||||
model,
|
||||
codeReviewer,
|
||||
mcpBacktestFn,
|
||||
logger
|
||||
);
|
||||
|
||||
// Compile graph
|
||||
workflow.compile();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
Reference in New Issue
Block a user