redesign fully scaffolded and web login works
This commit is contained in:
146
gateway/src/harness/skills/README.md
Normal file
146
gateway/src/harness/skills/README.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Skills
|
||||
|
||||
Skills are individual capabilities that the agent can use to accomplish tasks. Each skill is a self-contained unit with:
|
||||
|
||||
- A markdown definition file (`*.skill.md`)
|
||||
- A TypeScript implementation extending `BaseSkill`
|
||||
- Clear input/output contracts
|
||||
- Parameter validation
|
||||
- Error handling
|
||||
|
||||
## Skill Structure
|
||||
|
||||
```
|
||||
skills/
|
||||
├── base-skill.ts # Base class
|
||||
├── {skill-name}.skill.md # Definition
|
||||
├── {skill-name}.ts # Implementation
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Creating a New Skill
|
||||
|
||||
### 1. Create the Definition File
|
||||
|
||||
Create `{skill-name}.skill.md`:
|
||||
|
||||
```markdown
|
||||
# My Skill
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Author:** Your Name
|
||||
**Tags:** category1, category2
|
||||
|
||||
## Description
|
||||
What does this skill do?
|
||||
|
||||
## Inputs
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| param1 | string | Yes | What it does |
|
||||
|
||||
## Outputs
|
||||
What does it return?
|
||||
|
||||
## Example Usage
|
||||
Show code example
|
||||
```
|
||||
|
||||
### 2. Create the Implementation
|
||||
|
||||
Create `{skill-name}.ts`:
|
||||
|
||||
```typescript
|
||||
import { BaseSkill, SkillInput, SkillResult, SkillMetadata } from './base-skill.js';
|
||||
|
||||
export class MySkill extends BaseSkill {
|
||||
getMetadata(): SkillMetadata {
|
||||
return {
|
||||
name: 'my-skill',
|
||||
description: 'What it does',
|
||||
version: '1.0.0',
|
||||
};
|
||||
}
|
||||
|
||||
getParametersSchema(): Record<string, unknown> {
|
||||
return {
|
||||
type: 'object',
|
||||
required: ['param1'],
|
||||
properties: {
|
||||
param1: { type: 'string' },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
validateInput(parameters: Record<string, unknown>): boolean {
|
||||
return typeof parameters.param1 === 'string';
|
||||
}
|
||||
|
||||
async execute(input: SkillInput): Promise<SkillResult> {
|
||||
this.logStart(input);
|
||||
|
||||
try {
|
||||
// Your implementation here
|
||||
const result = this.success({ data: 'result' });
|
||||
this.logEnd(result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
return this.error(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Register the Skill
|
||||
|
||||
Add to `index.ts`:
|
||||
|
||||
```typescript
|
||||
export { MySkill } from './my-skill.js';
|
||||
```
|
||||
|
||||
## Using Skills in Workflows
|
||||
|
||||
Skills can be used in LangGraph workflows:
|
||||
|
||||
```typescript
|
||||
import { MarketAnalysisSkill } from '../skills/market-analysis.js';
|
||||
|
||||
const analyzeNode = async (state) => {
|
||||
const skill = new MarketAnalysisSkill(logger, model);
|
||||
const result = await skill.execute({
|
||||
context: state.userContext,
|
||||
parameters: {
|
||||
ticker: state.ticker,
|
||||
period: '4h',
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
analysis: result.data,
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Single Responsibility**: Each skill should do one thing well
|
||||
2. **Validation**: Always validate inputs thoroughly
|
||||
3. **Error Handling**: Use try/catch and return meaningful errors
|
||||
4. **Logging**: Use `logStart()` and `logEnd()` helpers
|
||||
5. **Documentation**: Keep the `.skill.md` file up to date
|
||||
6. **Testing**: Write unit tests for skill logic
|
||||
7. **Idempotency**: Skills should be safe to retry
|
||||
|
||||
## Available Skills
|
||||
|
||||
- **market-analysis**: Analyze market conditions and trends
|
||||
- *(Add more as you build them)*
|
||||
|
||||
## Skill Categories
|
||||
|
||||
- **Market Data**: Query and analyze market information
|
||||
- **Trading**: Execute trades, manage positions
|
||||
- **Analysis**: Technical and fundamental analysis
|
||||
- **Risk**: Risk assessment and management
|
||||
- **Utilities**: Helper functions and utilities
|
||||
128
gateway/src/harness/skills/base-skill.ts
Normal file
128
gateway/src/harness/skills/base-skill.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
||||
import type { FastifyBaseLogger } from 'fastify';
|
||||
import type { UserContext } from '../memory/session-context.js';
|
||||
|
||||
/**
|
||||
* Skill metadata
|
||||
*/
|
||||
export interface SkillMetadata {
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
author?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Skill input parameters
|
||||
*/
|
||||
export interface SkillInput {
|
||||
context: UserContext;
|
||||
parameters: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skill execution result
|
||||
*/
|
||||
export interface SkillResult {
|
||||
success: boolean;
|
||||
data?: unknown;
|
||||
error?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base skill interface
|
||||
*
|
||||
* Skills are individual capabilities that the agent can use.
|
||||
* Each skill is defined by:
|
||||
* - A markdown file (*.skill.md) describing purpose, inputs, outputs
|
||||
* - A TypeScript implementation extending BaseSkill
|
||||
*
|
||||
* Skills can use:
|
||||
* - LLM calls for reasoning
|
||||
* - User's MCP server tools
|
||||
* - Platform tools (market data, charts, etc.)
|
||||
*/
|
||||
export abstract class BaseSkill {
|
||||
protected logger: FastifyBaseLogger;
|
||||
protected model?: BaseChatModel;
|
||||
|
||||
constructor(logger: FastifyBaseLogger, model?: BaseChatModel) {
|
||||
this.logger = logger;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get skill metadata
|
||||
*/
|
||||
abstract getMetadata(): SkillMetadata;
|
||||
|
||||
/**
|
||||
* Validate input parameters
|
||||
*/
|
||||
abstract validateInput(parameters: Record<string, unknown>): boolean;
|
||||
|
||||
/**
|
||||
* Execute the skill
|
||||
*/
|
||||
abstract execute(input: SkillInput): Promise<SkillResult>;
|
||||
|
||||
/**
|
||||
* Get required parameters schema (JSON Schema format)
|
||||
*/
|
||||
abstract getParametersSchema(): Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* Helper: Log skill execution start
|
||||
*/
|
||||
protected logStart(input: SkillInput): void {
|
||||
const metadata = this.getMetadata();
|
||||
this.logger.info(
|
||||
{
|
||||
skill: metadata.name,
|
||||
userId: input.context.userId,
|
||||
sessionId: input.context.sessionId,
|
||||
parameters: input.parameters,
|
||||
},
|
||||
'Starting skill execution'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Log skill execution end
|
||||
*/
|
||||
protected logEnd(result: SkillResult): void {
|
||||
const metadata = this.getMetadata();
|
||||
this.logger.info(
|
||||
{
|
||||
skill: metadata.name,
|
||||
success: result.success,
|
||||
error: result.error,
|
||||
},
|
||||
'Skill execution completed'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Create success result
|
||||
*/
|
||||
protected success(data: unknown, metadata?: Record<string, unknown>): SkillResult {
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Create error result
|
||||
*/
|
||||
protected error(error: string | Error, metadata?: Record<string, unknown>): SkillResult {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : error,
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
}
|
||||
10
gateway/src/harness/skills/index.ts
Normal file
10
gateway/src/harness/skills/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
// Skills exports
|
||||
|
||||
export {
|
||||
BaseSkill,
|
||||
type SkillMetadata,
|
||||
type SkillInput,
|
||||
type SkillResult,
|
||||
} from './base-skill.js';
|
||||
|
||||
export { MarketAnalysisSkill } from './market-analysis.js';
|
||||
78
gateway/src/harness/skills/market-analysis.skill.md
Normal file
78
gateway/src/harness/skills/market-analysis.skill.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Market Analysis Skill
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Author:** Dexorder AI Platform
|
||||
**Tags:** market-data, analysis, trading
|
||||
|
||||
## Description
|
||||
|
||||
Analyzes market conditions for a given ticker and timeframe. Provides insights on:
|
||||
- Price trends and patterns
|
||||
- Volume analysis
|
||||
- Support and resistance levels
|
||||
- Market sentiment indicators
|
||||
|
||||
## Inputs
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `ticker` | string | Yes | Market identifier (e.g., "BINANCE:BTC/USDT") |
|
||||
| `period` | string | Yes | Analysis period ("1h", "4h", "1d", "1w") |
|
||||
| `startTime` | number | No | Start timestamp (microseconds), defaults to 7 days ago |
|
||||
| `endTime` | number | No | End timestamp (microseconds), defaults to now |
|
||||
| `indicators` | string[] | No | Additional indicators to include (e.g., ["RSI", "MACD"]) |
|
||||
|
||||
## Outputs
|
||||
|
||||
```typescript
|
||||
{
|
||||
success: true,
|
||||
data: {
|
||||
ticker: string,
|
||||
period: string,
|
||||
timeRange: { start: number, end: number },
|
||||
trend: "bullish" | "bearish" | "neutral",
|
||||
priceChange: number,
|
||||
volumeProfile: {
|
||||
average: number,
|
||||
recent: number,
|
||||
trend: "increasing" | "decreasing" | "stable"
|
||||
},
|
||||
supportLevels: number[],
|
||||
resistanceLevels: number[],
|
||||
indicators: Record<string, unknown>,
|
||||
analysis: string // LLM-generated natural language analysis
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
```typescript
|
||||
const skill = new MarketAnalysisSkill(logger, model);
|
||||
|
||||
const result = await skill.execute({
|
||||
context: userContext,
|
||||
parameters: {
|
||||
ticker: "BINANCE:BTC/USDT",
|
||||
period: "4h",
|
||||
indicators: ["RSI", "MACD"]
|
||||
}
|
||||
});
|
||||
|
||||
console.log(result.data.analysis);
|
||||
// "Bitcoin is showing bullish momentum with RSI at 65 and MACD crossing above signal line..."
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Queries OHLC data from Iceberg warehouse
|
||||
- Uses LLM for natural language analysis
|
||||
- Caches results for 5 minutes to reduce computation
|
||||
- Falls back to reduced analysis if Iceberg unavailable
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Iceberg client (market data)
|
||||
- LLM model (analysis generation)
|
||||
- User's MCP server (optional custom indicators)
|
||||
198
gateway/src/harness/skills/market-analysis.ts
Normal file
198
gateway/src/harness/skills/market-analysis.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import { BaseSkill, type SkillInput, type SkillResult, type SkillMetadata } from './base-skill.js';
|
||||
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
||||
import type { FastifyBaseLogger } from 'fastify';
|
||||
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
||||
|
||||
/**
|
||||
* Market analysis skill implementation
|
||||
*
|
||||
* See market-analysis.skill.md for full documentation
|
||||
*/
|
||||
export class MarketAnalysisSkill extends BaseSkill {
|
||||
constructor(logger: FastifyBaseLogger, model?: BaseChatModel) {
|
||||
super(logger, model);
|
||||
}
|
||||
|
||||
getMetadata(): SkillMetadata {
|
||||
return {
|
||||
name: 'market-analysis',
|
||||
description: 'Analyze market conditions for a given ticker and timeframe',
|
||||
version: '1.0.0',
|
||||
author: 'Dexorder AI Platform',
|
||||
tags: ['market-data', 'analysis', 'trading'],
|
||||
};
|
||||
}
|
||||
|
||||
getParametersSchema(): Record<string, unknown> {
|
||||
return {
|
||||
type: 'object',
|
||||
required: ['ticker', 'period'],
|
||||
properties: {
|
||||
ticker: {
|
||||
type: 'string',
|
||||
description: 'Market identifier (e.g., "BINANCE:BTC/USDT")',
|
||||
},
|
||||
period: {
|
||||
type: 'string',
|
||||
enum: ['1h', '4h', '1d', '1w'],
|
||||
description: 'Analysis period',
|
||||
},
|
||||
startTime: {
|
||||
type: 'number',
|
||||
description: 'Start timestamp in microseconds',
|
||||
},
|
||||
endTime: {
|
||||
type: 'number',
|
||||
description: 'End timestamp in microseconds',
|
||||
},
|
||||
indicators: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Additional indicators to include',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
validateInput(parameters: Record<string, unknown>): boolean {
|
||||
if (!parameters.ticker || typeof parameters.ticker !== 'string') {
|
||||
return false;
|
||||
}
|
||||
if (!parameters.period || typeof parameters.period !== 'string') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async execute(input: SkillInput): Promise<SkillResult> {
|
||||
this.logStart(input);
|
||||
|
||||
if (!this.validateInput(input.parameters)) {
|
||||
return this.error('Invalid parameters: ticker and period are required');
|
||||
}
|
||||
|
||||
try {
|
||||
const ticker = input.parameters.ticker as string;
|
||||
const period = input.parameters.period as string;
|
||||
const indicators = (input.parameters.indicators as string[]) || [];
|
||||
|
||||
// 1. Fetch OHLC data from Iceberg
|
||||
// TODO: Implement Iceberg query
|
||||
// const ohlcData = await this.fetchOHLCData(ticker, period, startTime, endTime);
|
||||
const ohlcData = this.getMockOHLCData(); // Placeholder
|
||||
|
||||
// 2. Calculate technical indicators
|
||||
const analysis = this.calculateAnalysis(ohlcData, indicators);
|
||||
|
||||
// 3. Generate natural language analysis using LLM
|
||||
let narrativeAnalysis = '';
|
||||
if (this.model) {
|
||||
narrativeAnalysis = await this.generateNarrativeAnalysis(
|
||||
ticker,
|
||||
period,
|
||||
analysis
|
||||
);
|
||||
}
|
||||
|
||||
const result = this.success({
|
||||
ticker,
|
||||
period,
|
||||
timeRange: {
|
||||
start: ohlcData.startTime,
|
||||
end: ohlcData.endTime,
|
||||
},
|
||||
trend: analysis.trend,
|
||||
priceChange: analysis.priceChange,
|
||||
volumeProfile: analysis.volumeProfile,
|
||||
supportLevels: analysis.supportLevels,
|
||||
resistanceLevels: analysis.resistanceLevels,
|
||||
indicators: analysis.indicators,
|
||||
analysis: narrativeAnalysis,
|
||||
});
|
||||
|
||||
this.logEnd(result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
const result = this.error(error as Error);
|
||||
this.logEnd(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate technical analysis from OHLC data
|
||||
*/
|
||||
private calculateAnalysis(
|
||||
ohlcData: any,
|
||||
_requestedIndicators: string[]
|
||||
): any {
|
||||
// TODO: Implement proper technical analysis
|
||||
// This is a simplified placeholder
|
||||
|
||||
const priceChange = ((ohlcData.close - ohlcData.open) / ohlcData.open) * 100;
|
||||
const trend = priceChange > 1 ? 'bullish' : priceChange < -1 ? 'bearish' : 'neutral';
|
||||
|
||||
return {
|
||||
trend,
|
||||
priceChange,
|
||||
volumeProfile: {
|
||||
average: ohlcData.avgVolume,
|
||||
recent: ohlcData.currentVolume,
|
||||
trend: ohlcData.currentVolume > ohlcData.avgVolume ? 'increasing' : 'decreasing',
|
||||
},
|
||||
supportLevels: [ohlcData.low * 0.98, ohlcData.low * 0.95],
|
||||
resistanceLevels: [ohlcData.high * 1.02, ohlcData.high * 1.05],
|
||||
indicators: {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate natural language analysis using LLM
|
||||
*/
|
||||
private async generateNarrativeAnalysis(
|
||||
ticker: string,
|
||||
period: string,
|
||||
analysis: any
|
||||
): Promise<string> {
|
||||
if (!this.model) {
|
||||
return 'LLM not available for narrative analysis';
|
||||
}
|
||||
|
||||
const systemPrompt = `You are a professional market analyst.
|
||||
Provide concise, actionable market analysis based on technical data.
|
||||
Focus on key insights and avoid jargon.`;
|
||||
|
||||
const userPrompt = `Analyze the following market data for ${ticker} (${period}):
|
||||
|
||||
Trend: ${analysis.trend}
|
||||
Price Change: ${analysis.priceChange.toFixed(2)}%
|
||||
Volume: ${analysis.volumeProfile.trend}
|
||||
Support Levels: ${analysis.supportLevels.join(', ')}
|
||||
Resistance Levels: ${analysis.resistanceLevels.join(', ')}
|
||||
|
||||
Provide a 2-3 sentence analysis suitable for a trading decision.`;
|
||||
|
||||
const response = await this.model.invoke([
|
||||
new SystemMessage(systemPrompt),
|
||||
new HumanMessage(userPrompt),
|
||||
]);
|
||||
|
||||
return response.content as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock OHLC data (placeholder until Iceberg integration)
|
||||
*/
|
||||
private getMockOHLCData(): any {
|
||||
return {
|
||||
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000,
|
||||
endTime: Date.now(),
|
||||
open: 50000,
|
||||
high: 52000,
|
||||
low: 49000,
|
||||
close: 51500,
|
||||
avgVolume: 1000000,
|
||||
currentVolume: 1200000,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user