praisonai 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/index.d.ts +78 -0
- package/dist/cache/index.js +235 -0
- package/dist/db/postgres.d.ts +84 -0
- package/dist/db/postgres.js +266 -0
- package/dist/db/redis.d.ts +109 -0
- package/dist/db/redis.js +307 -0
- package/dist/db/sqlite.d.ts +66 -0
- package/dist/db/sqlite.js +339 -0
- package/dist/events/index.d.ts +84 -0
- package/dist/events/index.js +153 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +84 -1
- package/dist/integrations/index.d.ts +7 -0
- package/dist/integrations/index.js +26 -0
- package/dist/integrations/observability/base.d.ts +123 -0
- package/dist/integrations/observability/base.js +183 -0
- package/dist/integrations/observability/index.d.ts +8 -0
- package/dist/integrations/observability/index.js +29 -0
- package/dist/integrations/observability/langfuse.d.ts +32 -0
- package/dist/integrations/observability/langfuse.js +174 -0
- package/dist/integrations/vector/base.d.ts +110 -0
- package/dist/integrations/vector/base.js +158 -0
- package/dist/integrations/vector/chroma.d.ts +25 -0
- package/dist/integrations/vector/chroma.js +143 -0
- package/dist/integrations/vector/index.d.ts +14 -0
- package/dist/integrations/vector/index.js +37 -0
- package/dist/integrations/vector/pinecone.d.ts +28 -0
- package/dist/integrations/vector/pinecone.js +172 -0
- package/dist/integrations/vector/qdrant.d.ts +25 -0
- package/dist/integrations/vector/qdrant.js +146 -0
- package/dist/integrations/vector/weaviate.d.ts +30 -0
- package/dist/integrations/vector/weaviate.js +206 -0
- package/dist/integrations/voice/base.d.ts +76 -0
- package/dist/integrations/voice/base.js +168 -0
- package/dist/integrations/voice/index.d.ts +6 -0
- package/dist/integrations/voice/index.js +26 -0
- package/dist/knowledge/graph-rag.d.ts +125 -0
- package/dist/knowledge/graph-rag.js +289 -0
- package/dist/knowledge/index.d.ts +2 -0
- package/dist/knowledge/index.js +18 -0
- package/dist/knowledge/reranker.d.ts +86 -0
- package/dist/knowledge/reranker.js +196 -0
- package/dist/workflows/yaml-parser.d.ts +48 -0
- package/dist/workflows/yaml-parser.js +304 -0
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reranker - Rerank search results for improved relevance
|
|
3
|
+
* Inspired by mastra's rerank module
|
|
4
|
+
*/
|
|
5
|
+
export interface RerankResult {
|
|
6
|
+
id: string;
|
|
7
|
+
score: number;
|
|
8
|
+
content: string;
|
|
9
|
+
metadata?: Record<string, any>;
|
|
10
|
+
originalRank: number;
|
|
11
|
+
newRank: number;
|
|
12
|
+
}
|
|
13
|
+
export interface RerankConfig {
|
|
14
|
+
model?: string;
|
|
15
|
+
topK?: number;
|
|
16
|
+
threshold?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Abstract base class for rerankers
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class BaseReranker {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
constructor(name: string);
|
|
24
|
+
abstract rerank(query: string, documents: Array<{
|
|
25
|
+
id: string;
|
|
26
|
+
content: string;
|
|
27
|
+
metadata?: Record<string, any>;
|
|
28
|
+
}>, config?: RerankConfig): Promise<RerankResult[]>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Cohere Reranker - Uses Cohere's rerank API
|
|
32
|
+
*/
|
|
33
|
+
export declare class CohereReranker extends BaseReranker {
|
|
34
|
+
private apiKey;
|
|
35
|
+
private model;
|
|
36
|
+
constructor(config?: {
|
|
37
|
+
apiKey?: string;
|
|
38
|
+
model?: string;
|
|
39
|
+
});
|
|
40
|
+
rerank(query: string, documents: Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
content: string;
|
|
43
|
+
metadata?: Record<string, any>;
|
|
44
|
+
}>, config?: RerankConfig): Promise<RerankResult[]>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Cross-Encoder Reranker - Uses sentence similarity for reranking
|
|
48
|
+
* This is a simple implementation using cosine similarity
|
|
49
|
+
*/
|
|
50
|
+
export declare class CrossEncoderReranker extends BaseReranker {
|
|
51
|
+
private embedFn?;
|
|
52
|
+
constructor(config?: {
|
|
53
|
+
embedFn?: (texts: string[]) => Promise<number[][]>;
|
|
54
|
+
});
|
|
55
|
+
rerank(query: string, documents: Array<{
|
|
56
|
+
id: string;
|
|
57
|
+
content: string;
|
|
58
|
+
metadata?: Record<string, any>;
|
|
59
|
+
}>, config?: RerankConfig): Promise<RerankResult[]>;
|
|
60
|
+
private keywordRerank;
|
|
61
|
+
private cosineSimilarity;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* LLM Reranker - Uses an LLM to score relevance
|
|
65
|
+
*/
|
|
66
|
+
export declare class LLMReranker extends BaseReranker {
|
|
67
|
+
private generateFn;
|
|
68
|
+
constructor(config: {
|
|
69
|
+
generateFn: (prompt: string) => Promise<string>;
|
|
70
|
+
});
|
|
71
|
+
rerank(query: string, documents: Array<{
|
|
72
|
+
id: string;
|
|
73
|
+
content: string;
|
|
74
|
+
metadata?: Record<string, any>;
|
|
75
|
+
}>, config?: RerankConfig): Promise<RerankResult[]>;
|
|
76
|
+
}
|
|
77
|
+
export declare function createCohereReranker(config?: {
|
|
78
|
+
apiKey?: string;
|
|
79
|
+
model?: string;
|
|
80
|
+
}): CohereReranker;
|
|
81
|
+
export declare function createCrossEncoderReranker(config?: {
|
|
82
|
+
embedFn?: (texts: string[]) => Promise<number[][]>;
|
|
83
|
+
}): CrossEncoderReranker;
|
|
84
|
+
export declare function createLLMReranker(config: {
|
|
85
|
+
generateFn: (prompt: string) => Promise<string>;
|
|
86
|
+
}): LLMReranker;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Reranker - Rerank search results for improved relevance
|
|
4
|
+
* Inspired by mastra's rerank module
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.LLMReranker = exports.CrossEncoderReranker = exports.CohereReranker = exports.BaseReranker = void 0;
|
|
8
|
+
exports.createCohereReranker = createCohereReranker;
|
|
9
|
+
exports.createCrossEncoderReranker = createCrossEncoderReranker;
|
|
10
|
+
exports.createLLMReranker = createLLMReranker;
|
|
11
|
+
/**
|
|
12
|
+
* Abstract base class for rerankers
|
|
13
|
+
*/
|
|
14
|
+
class BaseReranker {
|
|
15
|
+
constructor(name) {
|
|
16
|
+
this.name = name;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.BaseReranker = BaseReranker;
|
|
20
|
+
/**
|
|
21
|
+
* Cohere Reranker - Uses Cohere's rerank API
|
|
22
|
+
*/
|
|
23
|
+
class CohereReranker extends BaseReranker {
|
|
24
|
+
constructor(config = {}) {
|
|
25
|
+
super('CohereReranker');
|
|
26
|
+
this.apiKey = config.apiKey || process.env.COHERE_API_KEY || '';
|
|
27
|
+
this.model = config.model || 'rerank-english-v3.0';
|
|
28
|
+
}
|
|
29
|
+
async rerank(query, documents, config) {
|
|
30
|
+
if (!this.apiKey) {
|
|
31
|
+
throw new Error('Cohere API key required for reranking');
|
|
32
|
+
}
|
|
33
|
+
const response = await fetch('https://api.cohere.ai/v1/rerank', {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: {
|
|
36
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
37
|
+
'Content-Type': 'application/json'
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
model: config?.model || this.model,
|
|
41
|
+
query,
|
|
42
|
+
documents: documents.map(d => d.content),
|
|
43
|
+
top_n: config?.topK || documents.length,
|
|
44
|
+
return_documents: true
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const error = await response.text();
|
|
49
|
+
throw new Error(`Cohere rerank error: ${response.status} - ${error}`);
|
|
50
|
+
}
|
|
51
|
+
const data = await response.json();
|
|
52
|
+
const threshold = config?.threshold || 0;
|
|
53
|
+
return (data.results || [])
|
|
54
|
+
.filter((r) => r.relevance_score >= threshold)
|
|
55
|
+
.map((r, newRank) => ({
|
|
56
|
+
id: documents[r.index].id,
|
|
57
|
+
score: r.relevance_score,
|
|
58
|
+
content: documents[r.index].content,
|
|
59
|
+
metadata: documents[r.index].metadata,
|
|
60
|
+
originalRank: r.index,
|
|
61
|
+
newRank
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.CohereReranker = CohereReranker;
|
|
66
|
+
/**
|
|
67
|
+
* Cross-Encoder Reranker - Uses sentence similarity for reranking
|
|
68
|
+
* This is a simple implementation using cosine similarity
|
|
69
|
+
*/
|
|
70
|
+
class CrossEncoderReranker extends BaseReranker {
|
|
71
|
+
constructor(config = {}) {
|
|
72
|
+
super('CrossEncoderReranker');
|
|
73
|
+
this.embedFn = config.embedFn;
|
|
74
|
+
}
|
|
75
|
+
async rerank(query, documents, config) {
|
|
76
|
+
if (!this.embedFn) {
|
|
77
|
+
// Fallback to simple keyword matching
|
|
78
|
+
return this.keywordRerank(query, documents, config);
|
|
79
|
+
}
|
|
80
|
+
// Get embeddings for query and all documents
|
|
81
|
+
const texts = [query, ...documents.map(d => d.content)];
|
|
82
|
+
const embeddings = await this.embedFn(texts);
|
|
83
|
+
const queryEmbedding = embeddings[0];
|
|
84
|
+
const docEmbeddings = embeddings.slice(1);
|
|
85
|
+
// Calculate similarity scores
|
|
86
|
+
const scores = docEmbeddings.map((docEmb, i) => ({
|
|
87
|
+
index: i,
|
|
88
|
+
score: this.cosineSimilarity(queryEmbedding, docEmb)
|
|
89
|
+
}));
|
|
90
|
+
// Sort by score descending
|
|
91
|
+
scores.sort((a, b) => b.score - a.score);
|
|
92
|
+
const threshold = config?.threshold || 0;
|
|
93
|
+
const topK = config?.topK || documents.length;
|
|
94
|
+
return scores
|
|
95
|
+
.filter(s => s.score >= threshold)
|
|
96
|
+
.slice(0, topK)
|
|
97
|
+
.map((s, newRank) => ({
|
|
98
|
+
id: documents[s.index].id,
|
|
99
|
+
score: s.score,
|
|
100
|
+
content: documents[s.index].content,
|
|
101
|
+
metadata: documents[s.index].metadata,
|
|
102
|
+
originalRank: s.index,
|
|
103
|
+
newRank
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
keywordRerank(query, documents, config) {
|
|
107
|
+
const queryTerms = query.toLowerCase().split(/\s+/);
|
|
108
|
+
const scores = documents.map((doc, i) => {
|
|
109
|
+
const content = doc.content.toLowerCase();
|
|
110
|
+
let score = 0;
|
|
111
|
+
for (const term of queryTerms) {
|
|
112
|
+
if (content.includes(term)) {
|
|
113
|
+
score += 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Normalize by query length
|
|
117
|
+
score = score / queryTerms.length;
|
|
118
|
+
return { index: i, score };
|
|
119
|
+
});
|
|
120
|
+
scores.sort((a, b) => b.score - a.score);
|
|
121
|
+
const threshold = config?.threshold || 0;
|
|
122
|
+
const topK = config?.topK || documents.length;
|
|
123
|
+
return scores
|
|
124
|
+
.filter(s => s.score >= threshold)
|
|
125
|
+
.slice(0, topK)
|
|
126
|
+
.map((s, newRank) => ({
|
|
127
|
+
id: documents[s.index].id,
|
|
128
|
+
score: s.score,
|
|
129
|
+
content: documents[s.index].content,
|
|
130
|
+
metadata: documents[s.index].metadata,
|
|
131
|
+
originalRank: s.index,
|
|
132
|
+
newRank
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
cosineSimilarity(a, b) {
|
|
136
|
+
const dot = a.reduce((sum, x, i) => sum + x * b[i], 0);
|
|
137
|
+
const normA = Math.sqrt(a.reduce((sum, x) => sum + x * x, 0));
|
|
138
|
+
const normB = Math.sqrt(b.reduce((sum, x) => sum + x * x, 0));
|
|
139
|
+
return dot / (normA * normB);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.CrossEncoderReranker = CrossEncoderReranker;
|
|
143
|
+
/**
|
|
144
|
+
* LLM Reranker - Uses an LLM to score relevance
|
|
145
|
+
*/
|
|
146
|
+
class LLMReranker extends BaseReranker {
|
|
147
|
+
constructor(config) {
|
|
148
|
+
super('LLMReranker');
|
|
149
|
+
this.generateFn = config.generateFn;
|
|
150
|
+
}
|
|
151
|
+
async rerank(query, documents, config) {
|
|
152
|
+
const scores = [];
|
|
153
|
+
// Score each document
|
|
154
|
+
for (let i = 0; i < documents.length; i++) {
|
|
155
|
+
const doc = documents[i];
|
|
156
|
+
const prompt = `Rate the relevance of the following document to the query on a scale of 0 to 10.
|
|
157
|
+
Query: ${query}
|
|
158
|
+
Document: ${doc.content.slice(0, 500)}
|
|
159
|
+
|
|
160
|
+
Respond with only a number between 0 and 10.`;
|
|
161
|
+
try {
|
|
162
|
+
const response = await this.generateFn(prompt);
|
|
163
|
+
const score = parseFloat(response.trim()) / 10;
|
|
164
|
+
scores.push({ index: i, score: isNaN(score) ? 0 : Math.min(1, Math.max(0, score)) });
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
scores.push({ index: i, score: 0 });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
scores.sort((a, b) => b.score - a.score);
|
|
171
|
+
const threshold = config?.threshold || 0;
|
|
172
|
+
const topK = config?.topK || documents.length;
|
|
173
|
+
return scores
|
|
174
|
+
.filter(s => s.score >= threshold)
|
|
175
|
+
.slice(0, topK)
|
|
176
|
+
.map((s, newRank) => ({
|
|
177
|
+
id: documents[s.index].id,
|
|
178
|
+
score: s.score,
|
|
179
|
+
content: documents[s.index].content,
|
|
180
|
+
metadata: documents[s.index].metadata,
|
|
181
|
+
originalRank: s.index,
|
|
182
|
+
newRank
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.LLMReranker = LLMReranker;
|
|
187
|
+
// Factory functions
|
|
188
|
+
function createCohereReranker(config) {
|
|
189
|
+
return new CohereReranker(config);
|
|
190
|
+
}
|
|
191
|
+
function createCrossEncoderReranker(config) {
|
|
192
|
+
return new CrossEncoderReranker(config);
|
|
193
|
+
}
|
|
194
|
+
function createLLMReranker(config) {
|
|
195
|
+
return new LLMReranker(config);
|
|
196
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML Workflow Parser
|
|
3
|
+
* Parse YAML workflow definitions into executable workflows
|
|
4
|
+
*/
|
|
5
|
+
import { Workflow } from './index';
|
|
6
|
+
export interface YAMLWorkflowDefinition {
|
|
7
|
+
name: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
version?: string;
|
|
10
|
+
steps: YAMLStepDefinition[];
|
|
11
|
+
metadata?: Record<string, any>;
|
|
12
|
+
}
|
|
13
|
+
export interface YAMLStepDefinition {
|
|
14
|
+
name: string;
|
|
15
|
+
type: 'agent' | 'tool' | 'condition' | 'parallel' | 'loop';
|
|
16
|
+
agent?: string;
|
|
17
|
+
tool?: string;
|
|
18
|
+
input?: string | Record<string, any>;
|
|
19
|
+
output?: string;
|
|
20
|
+
condition?: string;
|
|
21
|
+
onError?: 'fail' | 'skip' | 'retry';
|
|
22
|
+
maxRetries?: number;
|
|
23
|
+
timeout?: number;
|
|
24
|
+
steps?: YAMLStepDefinition[];
|
|
25
|
+
loopCondition?: string;
|
|
26
|
+
maxIterations?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface ParsedWorkflow {
|
|
29
|
+
workflow: Workflow;
|
|
30
|
+
definition: YAMLWorkflowDefinition;
|
|
31
|
+
errors: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse YAML string into workflow definition
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseYAMLWorkflow(yamlContent: string): YAMLWorkflowDefinition;
|
|
37
|
+
/**
|
|
38
|
+
* Create executable workflow from YAML definition
|
|
39
|
+
*/
|
|
40
|
+
export declare function createWorkflowFromYAML(definition: YAMLWorkflowDefinition, agents?: Record<string, any>, tools?: Record<string, any>): ParsedWorkflow;
|
|
41
|
+
/**
|
|
42
|
+
* Load workflow from YAML file
|
|
43
|
+
*/
|
|
44
|
+
export declare function loadWorkflowFromFile(filePath: string, agents?: Record<string, any>, tools?: Record<string, any>): Promise<ParsedWorkflow>;
|
|
45
|
+
/**
|
|
46
|
+
* Validate YAML workflow definition
|
|
47
|
+
*/
|
|
48
|
+
export declare function validateWorkflowDefinition(definition: YAMLWorkflowDefinition): string[];
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* YAML Workflow Parser
|
|
4
|
+
* Parse YAML workflow definitions into executable workflows
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.parseYAMLWorkflow = parseYAMLWorkflow;
|
|
41
|
+
exports.createWorkflowFromYAML = createWorkflowFromYAML;
|
|
42
|
+
exports.loadWorkflowFromFile = loadWorkflowFromFile;
|
|
43
|
+
exports.validateWorkflowDefinition = validateWorkflowDefinition;
|
|
44
|
+
const index_1 = require("./index");
|
|
45
|
+
/**
|
|
46
|
+
* Parse YAML string into workflow definition
|
|
47
|
+
*/
|
|
48
|
+
function parseYAMLWorkflow(yamlContent) {
|
|
49
|
+
// Simple YAML parser for workflow definitions
|
|
50
|
+
// For production, use js-yaml package
|
|
51
|
+
const lines = yamlContent.split('\n');
|
|
52
|
+
const result = {
|
|
53
|
+
name: '',
|
|
54
|
+
steps: []
|
|
55
|
+
};
|
|
56
|
+
let currentStep = null;
|
|
57
|
+
let indent = 0;
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
61
|
+
continue;
|
|
62
|
+
const currentIndent = line.search(/\S/);
|
|
63
|
+
// Parse key-value pairs
|
|
64
|
+
const colonIndex = trimmed.indexOf(':');
|
|
65
|
+
if (colonIndex === -1)
|
|
66
|
+
continue;
|
|
67
|
+
const key = trimmed.substring(0, colonIndex).trim();
|
|
68
|
+
const value = trimmed.substring(colonIndex + 1).trim();
|
|
69
|
+
if (currentIndent === 0) {
|
|
70
|
+
// Top-level keys
|
|
71
|
+
if (key === 'name')
|
|
72
|
+
result.name = value;
|
|
73
|
+
else if (key === 'description')
|
|
74
|
+
result.description = value;
|
|
75
|
+
else if (key === 'version')
|
|
76
|
+
result.version = value;
|
|
77
|
+
else if (key === 'steps') {
|
|
78
|
+
// Steps array starts
|
|
79
|
+
indent = currentIndent;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (trimmed.startsWith('- name:')) {
|
|
83
|
+
// New step
|
|
84
|
+
if (currentStep && currentStep.name) {
|
|
85
|
+
result.steps.push(currentStep);
|
|
86
|
+
}
|
|
87
|
+
currentStep = {
|
|
88
|
+
name: value,
|
|
89
|
+
type: 'agent'
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
else if (currentStep) {
|
|
93
|
+
// Step properties
|
|
94
|
+
if (key === 'type')
|
|
95
|
+
currentStep.type = value;
|
|
96
|
+
else if (key === 'agent')
|
|
97
|
+
currentStep.agent = value;
|
|
98
|
+
else if (key === 'tool')
|
|
99
|
+
currentStep.tool = value;
|
|
100
|
+
else if (key === 'input')
|
|
101
|
+
currentStep.input = value;
|
|
102
|
+
else if (key === 'output')
|
|
103
|
+
currentStep.output = value;
|
|
104
|
+
else if (key === 'condition')
|
|
105
|
+
currentStep.condition = value;
|
|
106
|
+
else if (key === 'onError')
|
|
107
|
+
currentStep.onError = value;
|
|
108
|
+
else if (key === 'maxRetries')
|
|
109
|
+
currentStep.maxRetries = parseInt(value);
|
|
110
|
+
else if (key === 'timeout')
|
|
111
|
+
currentStep.timeout = parseInt(value);
|
|
112
|
+
else if (key === 'loopCondition')
|
|
113
|
+
currentStep.loopCondition = value;
|
|
114
|
+
else if (key === 'maxIterations')
|
|
115
|
+
currentStep.maxIterations = parseInt(value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Add last step
|
|
119
|
+
if (currentStep && currentStep.name) {
|
|
120
|
+
result.steps.push(currentStep);
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create executable workflow from YAML definition
|
|
126
|
+
*/
|
|
127
|
+
function createWorkflowFromYAML(definition, agents = {}, tools = {}) {
|
|
128
|
+
const errors = [];
|
|
129
|
+
const workflow = new index_1.Workflow(definition.name);
|
|
130
|
+
for (const stepDef of definition.steps) {
|
|
131
|
+
try {
|
|
132
|
+
const stepConfig = createStepConfig(stepDef, agents, tools, errors);
|
|
133
|
+
if (stepConfig) {
|
|
134
|
+
workflow.addStep(stepConfig);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
errors.push(`Error creating step ${stepDef.name}: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { workflow, definition, errors };
|
|
142
|
+
}
|
|
143
|
+
function createStepConfig(stepDef, agents, tools, errors) {
|
|
144
|
+
const { name, type, agent, tool, onError, maxRetries, timeout, condition } = stepDef;
|
|
145
|
+
let execute;
|
|
146
|
+
switch (type) {
|
|
147
|
+
case 'agent':
|
|
148
|
+
if (!agent) {
|
|
149
|
+
errors.push(`Step ${name}: agent type requires 'agent' field`);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const agentInstance = agents[agent];
|
|
153
|
+
if (!agentInstance) {
|
|
154
|
+
errors.push(`Step ${name}: agent '${agent}' not found`);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
execute = async (input) => {
|
|
158
|
+
if (typeof agentInstance.chat === 'function') {
|
|
159
|
+
return agentInstance.chat(typeof input === 'string' ? input : JSON.stringify(input));
|
|
160
|
+
}
|
|
161
|
+
return agentInstance(input);
|
|
162
|
+
};
|
|
163
|
+
break;
|
|
164
|
+
case 'tool':
|
|
165
|
+
if (!tool) {
|
|
166
|
+
errors.push(`Step ${name}: tool type requires 'tool' field`);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const toolInstance = tools[tool];
|
|
170
|
+
if (!toolInstance) {
|
|
171
|
+
errors.push(`Step ${name}: tool '${tool}' not found`);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
execute = async (input) => {
|
|
175
|
+
if (typeof toolInstance.execute === 'function') {
|
|
176
|
+
return toolInstance.execute(input);
|
|
177
|
+
}
|
|
178
|
+
if (typeof toolInstance.run === 'function') {
|
|
179
|
+
return toolInstance.run(input);
|
|
180
|
+
}
|
|
181
|
+
return toolInstance(input);
|
|
182
|
+
};
|
|
183
|
+
break;
|
|
184
|
+
case 'condition':
|
|
185
|
+
execute = async (input, context) => {
|
|
186
|
+
// Evaluate condition and return input or skip
|
|
187
|
+
if (condition) {
|
|
188
|
+
// Simple condition evaluation
|
|
189
|
+
const result = evaluateCondition(condition, input, context);
|
|
190
|
+
return result ? input : null;
|
|
191
|
+
}
|
|
192
|
+
return input;
|
|
193
|
+
};
|
|
194
|
+
break;
|
|
195
|
+
default:
|
|
196
|
+
execute = async (input) => input;
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
name,
|
|
200
|
+
execute,
|
|
201
|
+
onError,
|
|
202
|
+
maxRetries,
|
|
203
|
+
timeout,
|
|
204
|
+
condition: condition ? (context) => evaluateCondition(condition, null, context) : undefined
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function evaluateCondition(condition, input, context) {
|
|
208
|
+
// Simple condition evaluation
|
|
209
|
+
// Supports: "result.success", "input.length > 0", etc.
|
|
210
|
+
try {
|
|
211
|
+
// Create a safe evaluation context
|
|
212
|
+
const evalContext = {
|
|
213
|
+
input,
|
|
214
|
+
context,
|
|
215
|
+
result: context?.get?.('lastResult'),
|
|
216
|
+
...context?.metadata
|
|
217
|
+
};
|
|
218
|
+
// Simple expression evaluation
|
|
219
|
+
if (condition.includes('===')) {
|
|
220
|
+
const [left, right] = condition.split('===').map(s => s.trim());
|
|
221
|
+
return getNestedValue(evalContext, left) === parseValue(right);
|
|
222
|
+
}
|
|
223
|
+
if (condition.includes('!==')) {
|
|
224
|
+
const [left, right] = condition.split('!==').map(s => s.trim());
|
|
225
|
+
return getNestedValue(evalContext, left) !== parseValue(right);
|
|
226
|
+
}
|
|
227
|
+
if (condition.includes('>')) {
|
|
228
|
+
const [left, right] = condition.split('>').map(s => s.trim());
|
|
229
|
+
return getNestedValue(evalContext, left) > parseValue(right);
|
|
230
|
+
}
|
|
231
|
+
if (condition.includes('<')) {
|
|
232
|
+
const [left, right] = condition.split('<').map(s => s.trim());
|
|
233
|
+
return getNestedValue(evalContext, left) < parseValue(right);
|
|
234
|
+
}
|
|
235
|
+
// Boolean check
|
|
236
|
+
return !!getNestedValue(evalContext, condition);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function getNestedValue(obj, path) {
|
|
243
|
+
return path.split('.').reduce((current, key) => current?.[key], obj);
|
|
244
|
+
}
|
|
245
|
+
function parseValue(value) {
|
|
246
|
+
value = value.trim();
|
|
247
|
+
if (value === 'true')
|
|
248
|
+
return true;
|
|
249
|
+
if (value === 'false')
|
|
250
|
+
return false;
|
|
251
|
+
if (value === 'null')
|
|
252
|
+
return null;
|
|
253
|
+
if (value.startsWith('"') && value.endsWith('"'))
|
|
254
|
+
return value.slice(1, -1);
|
|
255
|
+
if (value.startsWith("'") && value.endsWith("'"))
|
|
256
|
+
return value.slice(1, -1);
|
|
257
|
+
const num = Number(value);
|
|
258
|
+
if (!isNaN(num))
|
|
259
|
+
return num;
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Load workflow from YAML file
|
|
264
|
+
*/
|
|
265
|
+
async function loadWorkflowFromFile(filePath, agents = {}, tools = {}) {
|
|
266
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
267
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
268
|
+
const definition = parseYAMLWorkflow(content);
|
|
269
|
+
return createWorkflowFromYAML(definition, agents, tools);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Validate YAML workflow definition
|
|
273
|
+
*/
|
|
274
|
+
function validateWorkflowDefinition(definition) {
|
|
275
|
+
const errors = [];
|
|
276
|
+
if (!definition.name) {
|
|
277
|
+
errors.push('Workflow must have a name');
|
|
278
|
+
}
|
|
279
|
+
if (!definition.steps || definition.steps.length === 0) {
|
|
280
|
+
errors.push('Workflow must have at least one step');
|
|
281
|
+
}
|
|
282
|
+
const stepNames = new Set();
|
|
283
|
+
for (const step of definition.steps) {
|
|
284
|
+
if (!step.name) {
|
|
285
|
+
errors.push('Each step must have a name');
|
|
286
|
+
}
|
|
287
|
+
else if (stepNames.has(step.name)) {
|
|
288
|
+
errors.push(`Duplicate step name: ${step.name}`);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
stepNames.add(step.name);
|
|
292
|
+
}
|
|
293
|
+
if (!step.type) {
|
|
294
|
+
errors.push(`Step ${step.name}: must have a type`);
|
|
295
|
+
}
|
|
296
|
+
if (step.type === 'agent' && !step.agent) {
|
|
297
|
+
errors.push(`Step ${step.name}: agent type requires 'agent' field`);
|
|
298
|
+
}
|
|
299
|
+
if (step.type === 'tool' && !step.tool) {
|
|
300
|
+
errors.push(`Step ${step.name}: tool type requires 'tool' field`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return errors;
|
|
304
|
+
}
|