redesign fully scaffolded and web login works

This commit is contained in:
2026-03-17 20:10:47 -04:00
parent b9cc397e05
commit f6bd22a8ef
143 changed files with 17317 additions and 693 deletions

View File

@@ -0,0 +1,461 @@
# Workflows
LangGraph-based workflows for multi-step agent orchestration.
## What are Workflows?
Workflows are state machines that orchestrate complex multi-step tasks with:
- **State Management**: Typed state with annotations
- **Conditional Routing**: Different paths based on state
- **Validation Loops**: Retry with fixes
- **Human-in-the-Loop**: Approval gates and interrupts
- **Error Recovery**: Graceful handling of failures
Built on [LangGraph.js](https://langchain-ai.github.io/langgraphjs/).
## Directory Structure
```
workflows/
├── base-workflow.ts # Base class and utilities
├── {workflow-name}/
│ ├── config.yaml # Workflow configuration
│ ├── state.ts # State schema (Annotations)
│ ├── nodes.ts # Node implementations
│ └── graph.ts # StateGraph definition
└── README.md # This file
```
## Workflow Components
### State (state.ts)
Defines what data flows through the workflow:
```typescript
import { Annotation } from '@langchain/langgraph';
import { BaseWorkflowState } from '../base-workflow.js';
export const MyWorkflowState = Annotation.Root({
...BaseWorkflowState.spec, // Inherit base fields
// Your custom fields
input: Annotation<string>(),
result: Annotation<string | null>({ default: () => null }),
errorCount: Annotation<number>({ default: () => 0 }),
});
export type MyWorkflowStateType = typeof MyWorkflowState.State;
```
### Nodes (nodes.ts)
Functions that transform state:
```typescript
export function createMyNode(deps: Dependencies) {
return async (state: MyWorkflowStateType): Promise<Partial<MyWorkflowStateType>> => {
// Do work
const result = await doSomething(state.input);
// Return partial state update
return { result };
};
}
```
### Graph (graph.ts)
Connects nodes with edges:
```typescript
import { StateGraph } from '@langchain/langgraph';
import { BaseWorkflow } from '../base-workflow.js';
export class MyWorkflow extends BaseWorkflow<MyWorkflowStateType> {
buildGraph(): StateGraph<MyWorkflowStateType> {
const graph = new StateGraph(MyWorkflowState);
// Add nodes
graph
.addNode('step1', createStep1Node())
.addNode('step2', createStep2Node());
// Add edges
graph
.addEdge('__start__', 'step1')
.addEdge('step1', 'step2')
.addEdge('step2', '__end__');
return graph;
}
}
```
### Config (config.yaml)
Workflow settings:
```yaml
name: my-workflow
description: What it does
timeout: 300000 # 5 minutes
maxRetries: 3
requiresApproval: true
approvalNodes:
- human_approval
# Custom settings
myCustomSetting: value
```
## Common Patterns
### 1. Validation Loop (Retry with Fixes)
```typescript
graph
.addNode('validate', validateNode)
.addNode('fix', fixNode)
.addConditionalEdges('validate', (state) => {
if (state.isValid) return 'next_step';
if (state.retryCount >= 3) return '__end__'; // Give up
return 'fix'; // Try to fix
})
.addEdge('fix', 'validate'); // Loop back
```
### 2. Human-in-the-Loop (Approval)
```typescript
const approvalNode = async (state) => {
// Send approval request to user's channel
await sendToChannel(state.userContext.activeChannel, {
type: 'approval_request',
data: {
action: 'execute_trade',
details: state.tradeDetails,
}
});
// Mark as waiting for approval
return { approvalRequested: true, userApproved: false };
};
graph.addConditionalEdges('approval', (state) => {
return state.userApproved ? 'execute' : '__end__';
});
// To resume after user input:
// const updated = await workflow.execute({ ...state, userApproved: true });
```
### 3. Parallel Execution
```typescript
import { Branch } from '@langchain/langgraph';
graph
.addNode('parallel_start', startNode)
.addNode('task_a', taskANode)
.addNode('task_b', taskBNode)
.addNode('merge', mergeNode);
// Branch to parallel tasks
graph.addEdge('parallel_start', Branch.parallel(['task_a', 'task_b']));
// Merge results
graph
.addEdge('task_a', 'merge')
.addEdge('task_b', 'merge');
```
### 4. Error Recovery
```typescript
const resilientNode = async (state) => {
try {
const result = await riskyOperation();
return { result, error: null };
} catch (error) {
logger.error({ error }, 'Operation failed');
return {
error: error.message,
fallbackUsed: true,
result: await fallbackOperation()
};
}
};
```
### 5. Conditional Routing
```typescript
graph.addConditionalEdges('decision', (state) => {
if (state.score > 0.8) return 'high_confidence';
if (state.score > 0.5) return 'medium_confidence';
return 'low_confidence';
});
graph
.addNode('high_confidence', autoApproveNode)
.addNode('medium_confidence', humanReviewNode)
.addNode('low_confidence', rejectNode);
```
## Available Workflows
### strategy-validation
Validates trading strategies with multiple steps and a validation loop.
**Flow:**
1. Code Review (using CodeReviewerSubagent)
2. If issues → Fix Code → loop back
3. Backtest (via MCP)
4. If failed → Fix Code → loop back
5. Risk Assessment
6. Human Approval
7. Final Recommendation
**Features:**
- Max 3 retry attempts
- Multi-file memory from subagent
- Risk-based auto-approval
- Comprehensive state tracking
### trading-request
Human-in-the-loop workflow for trade execution.
**Flow:**
1. Analyze market conditions
2. Calculate risk and position size
3. Request human approval (PAUSE)
4. If approved → Execute trade
5. Generate summary
**Features:**
- Interrupt at approval node
- Channel-aware approval UI
- Risk validation
- Execution confirmation
## Creating a New Workflow
### 1. Create Directory
```bash
mkdir -p workflows/my-workflow
```
### 2. Define State
```typescript
// state.ts
import { Annotation } from '@langchain/langgraph';
import { BaseWorkflowState } from '../base-workflow.js';
export const MyWorkflowState = Annotation.Root({
...BaseWorkflowState.spec,
// Add your fields
input: Annotation<string>(),
step1Result: Annotation<string | null>({ default: () => null }),
step2Result: Annotation<string | null>({ default: () => null }),
});
export type MyWorkflowStateType = typeof MyWorkflowState.State;
```
### 3. Create Nodes
```typescript
// nodes.ts
import { MyWorkflowStateType } from './state.js';
export function createStep1Node(deps: any) {
return async (state: MyWorkflowStateType) => {
const result = await doStep1(state.input);
return { step1Result: result };
};
}
export function createStep2Node(deps: any) {
return async (state: MyWorkflowStateType) => {
const result = await doStep2(state.step1Result);
return { step2Result: result, output: result };
};
}
```
### 4. Build Graph
```typescript
// graph.ts
import { StateGraph } from '@langchain/langgraph';
import { BaseWorkflow, WorkflowConfig } from '../base-workflow.js';
import { MyWorkflowState, MyWorkflowStateType } from './state.js';
import { createStep1Node, createStep2Node } from './nodes.js';
export class MyWorkflow extends BaseWorkflow<MyWorkflowStateType> {
constructor(config: WorkflowConfig, private deps: any, logger: Logger) {
super(config, logger);
}
buildGraph(): StateGraph<MyWorkflowStateType> {
const graph = new StateGraph(MyWorkflowState);
const step1 = createStep1Node(this.deps);
const step2 = createStep2Node(this.deps);
graph
.addNode('step1', step1)
.addNode('step2', step2)
.addEdge('__start__', 'step1')
.addEdge('step1', 'step2')
.addEdge('step2', '__end__');
return graph;
}
}
```
### 5. Create Config
```yaml
# config.yaml
name: my-workflow
description: My workflow description
timeout: 60000
maxRetries: 3
requiresApproval: false
model: claude-3-5-sonnet-20241022
```
### 6. Add Factory Function
```typescript
// graph.ts (continued)
export async function createMyWorkflow(
deps: any,
logger: Logger,
configPath: string
): Promise<MyWorkflow> {
const config = await loadYAML(configPath);
const workflow = new MyWorkflow(config, deps, logger);
workflow.compile();
return workflow;
}
```
## Usage
### Execute Workflow
```typescript
import { createMyWorkflow } from './harness/workflows';
const workflow = await createMyWorkflow(deps, logger, configPath);
const result = await workflow.execute({
userContext,
input: 'my input'
});
console.log(result.output);
```
### Stream Workflow
```typescript
for await (const state of workflow.stream({ userContext, input })) {
console.log('Current state:', state);
}
```
### With Interrupts (Human-in-the-Loop)
```typescript
// Initial execution (pauses at interrupt)
const pausedState = await workflow.execute(initialState);
// User provides input
const userInput = await getUserApproval();
// Resume from paused state
const finalState = await workflow.execute({
...pausedState,
userApproved: userInput.approved
});
```
## Best Practices
### State Design
- **Immutable Updates**: Return partial state, don't mutate
- **Type Safety**: Use TypeScript annotations
- **Defaults**: Provide sensible defaults
- **Nullable Fields**: Use `| null` with `default: () => null`
### Node Implementation
- **Pure Functions**: Avoid side effects in state logic
- **Error Handling**: Catch errors, return error state
- **Logging**: Log entry/exit of nodes
- **Partial Updates**: Only return fields that changed
### Graph Design
- **Single Responsibility**: Each node does one thing
- **Clear Flow**: Easy to visualize the graph
- **Error Paths**: Handle failures gracefully
- **Idempotency**: Safe to retry nodes
### Configuration
- **Timeouts**: Set reasonable limits
- **Retries**: Don't retry forever
- **Approvals**: Mark approval nodes explicitly
- **Documentation**: Explain complex config values
## Debugging
### View Graph
```typescript
// Get graph structure
const compiled = workflow.compile();
console.log(compiled.getGraph());
```
### Log State
```typescript
const debugNode = async (state) => {
logger.debug({ state }, 'Current state');
return {}; // No changes
};
graph.addNode('debug', debugNode);
```
### Test Nodes in Isolation
```typescript
const step1 = createStep1Node(deps);
const result = await step1({ input: 'test', /* ... */ });
expect(result.step1Result).toBe('expected');
```
## References
- [LangGraph.js Docs](https://langchain-ai.github.io/langgraphjs/)
- [LangChain.js Docs](https://js.langchain.com/)
- [Example: strategy-validation](./strategy-validation/graph.ts)
- [Example: trading-request](./trading-request/graph.ts)