@fastino-ai/pioneer-cli 0.1.0 → 0.2.0
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/README.md +161 -22
- package/bun.lock +82 -0
- package/cache/cache.db +0 -0
- package/cache/cache.db-shm +0 -0
- package/cache/cache.db-wal +0 -0
- package/fastino-ai-pioneer-cli-0.2.0.tgz +0 -0
- package/package.json +6 -3
- package/src/agent/Agent.ts +342 -0
- package/src/agent/BudgetManager.ts +167 -0
- package/src/agent/FileResolver.ts +321 -0
- package/src/agent/LLMClient.ts +435 -0
- package/src/agent/ToolRegistry.ts +97 -0
- package/src/agent/index.ts +15 -0
- package/src/agent/types.ts +84 -0
- package/src/chat/ChatApp.tsx +701 -0
- package/src/chat/index.ts +7 -0
- package/src/config.ts +185 -3
- package/src/evolution/EvalRunner.ts +301 -0
- package/src/evolution/EvolutionEngine.ts +319 -0
- package/src/evolution/FeedbackCollector.ts +197 -0
- package/src/evolution/ModelTrainer.ts +371 -0
- package/src/evolution/index.ts +18 -0
- package/src/evolution/types.ts +110 -0
- package/src/index.tsx +101 -2
- package/src/tools/bash.ts +184 -0
- package/src/tools/filesystem.ts +444 -0
- package/src/tools/index.ts +29 -0
- package/src/tools/modal.ts +269 -0
- package/src/tools/sandbox.ts +310 -0
- package/src/tools/training.ts +443 -0
- package/src/tools/wandb.ts +348 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EvolutionEngine - Core self-improvement loop
|
|
3
|
+
* Iteratively improves the agent based on evaluations and feedback
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import * as os from "os";
|
|
9
|
+
import type { Agent } from "../agent/Agent.js";
|
|
10
|
+
import { FeedbackCollector } from "./FeedbackCollector.js";
|
|
11
|
+
import { EvalRunner, DEFAULT_EVAL_CASES } from "./EvalRunner.js";
|
|
12
|
+
import { ModelTrainer, type TrainingResult } from "./ModelTrainer.js";
|
|
13
|
+
import type {
|
|
14
|
+
EvolutionConfig,
|
|
15
|
+
EvolutionState,
|
|
16
|
+
EvolutionHistory,
|
|
17
|
+
EvalCase,
|
|
18
|
+
EvalRunSummary,
|
|
19
|
+
} from "./types.js";
|
|
20
|
+
|
|
21
|
+
export interface EvolutionEngineConfig {
|
|
22
|
+
storagePath?: string;
|
|
23
|
+
evalCases?: EvalCase[];
|
|
24
|
+
targetScore?: number;
|
|
25
|
+
maxIterations?: number;
|
|
26
|
+
budgetPerIteration?: {
|
|
27
|
+
maxTokens?: number;
|
|
28
|
+
maxCost?: number;
|
|
29
|
+
maxTime?: number;
|
|
30
|
+
};
|
|
31
|
+
trainingConfig?: {
|
|
32
|
+
provider: "openai" | "modal" | "local";
|
|
33
|
+
baseModel: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface EvolutionEvents {
|
|
38
|
+
onIterationStart?: (iteration: number) => void;
|
|
39
|
+
onIterationEnd?: (iteration: number, score: number) => void;
|
|
40
|
+
onEvalComplete?: (summary: EvalRunSummary) => void;
|
|
41
|
+
onTrainingComplete?: (result: TrainingResult) => void;
|
|
42
|
+
onBudgetWarning?: (message: string) => void;
|
|
43
|
+
onComplete?: (state: EvolutionState) => void;
|
|
44
|
+
onError?: (error: Error) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class EvolutionEngine {
|
|
48
|
+
private config: EvolutionEngineConfig;
|
|
49
|
+
private storagePath: string;
|
|
50
|
+
private feedbackCollector: FeedbackCollector;
|
|
51
|
+
private evalRunner: EvalRunner;
|
|
52
|
+
private modelTrainer: ModelTrainer | null = null;
|
|
53
|
+
private state: EvolutionState;
|
|
54
|
+
private events: EvolutionEvents;
|
|
55
|
+
|
|
56
|
+
constructor(config: EvolutionEngineConfig, events: EvolutionEvents = {}) {
|
|
57
|
+
this.config = config;
|
|
58
|
+
this.events = events;
|
|
59
|
+
this.storagePath =
|
|
60
|
+
config.storagePath || path.join(os.homedir(), ".pioneer", "evolution");
|
|
61
|
+
this.ensureStoragePath();
|
|
62
|
+
|
|
63
|
+
this.feedbackCollector = new FeedbackCollector({
|
|
64
|
+
storagePath: path.join(this.storagePath, "feedback"),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.evalRunner = new EvalRunner();
|
|
68
|
+
|
|
69
|
+
if (config.trainingConfig) {
|
|
70
|
+
this.modelTrainer = new ModelTrainer({
|
|
71
|
+
provider: config.trainingConfig.provider,
|
|
72
|
+
baseModel: config.trainingConfig.baseModel,
|
|
73
|
+
outputDir: path.join(this.storagePath, "models"),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.state = this.loadState() || this.createInitialState();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private ensureStoragePath(): void {
|
|
81
|
+
if (!fs.existsSync(this.storagePath)) {
|
|
82
|
+
fs.mkdirSync(this.storagePath, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private createInitialState(): EvolutionState {
|
|
87
|
+
return {
|
|
88
|
+
iteration: 0,
|
|
89
|
+
currentScore: 0,
|
|
90
|
+
bestScore: 0,
|
|
91
|
+
bestPrompt: "",
|
|
92
|
+
history: [],
|
|
93
|
+
totalTokensUsed: 0,
|
|
94
|
+
totalCostUsed: 0,
|
|
95
|
+
totalTimeUsed: 0,
|
|
96
|
+
startTime: new Date(),
|
|
97
|
+
status: "running",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private loadState(): EvolutionState | null {
|
|
102
|
+
const statePath = path.join(this.storagePath, "state.json");
|
|
103
|
+
try {
|
|
104
|
+
if (fs.existsSync(statePath)) {
|
|
105
|
+
const data = fs.readFileSync(statePath, "utf-8");
|
|
106
|
+
return JSON.parse(data);
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
// Ignore errors
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private saveState(): void {
|
|
115
|
+
const statePath = path.join(this.storagePath, "state.json");
|
|
116
|
+
fs.writeFileSync(statePath, JSON.stringify(this.state, null, 2));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async evolve(agent: Agent): Promise<EvolutionState> {
|
|
120
|
+
const evalCases = this.config.evalCases || DEFAULT_EVAL_CASES;
|
|
121
|
+
const targetScore = this.config.targetScore || 0.9;
|
|
122
|
+
const maxIterations = this.config.maxIterations || 10;
|
|
123
|
+
|
|
124
|
+
this.state.status = "running";
|
|
125
|
+
this.state.startTime = new Date();
|
|
126
|
+
|
|
127
|
+
while (
|
|
128
|
+
this.state.iteration < maxIterations &&
|
|
129
|
+
this.state.status === "running"
|
|
130
|
+
) {
|
|
131
|
+
// Check budget
|
|
132
|
+
if (!this.checkBudget()) {
|
|
133
|
+
this.state.status = "budget_exhausted";
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.state.iteration++;
|
|
138
|
+
this.events.onIterationStart?.(this.state.iteration);
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
// Run evaluation
|
|
142
|
+
const evalSummary = await this.evalRunner.runEvalSuite(agent, evalCases);
|
|
143
|
+
this.events.onEvalComplete?.(evalSummary);
|
|
144
|
+
|
|
145
|
+
// Update state
|
|
146
|
+
this.state.currentScore = evalSummary.averageScore;
|
|
147
|
+
this.state.totalTokensUsed += evalSummary.totalTokens;
|
|
148
|
+
this.state.totalTimeUsed += evalSummary.totalDuration / 1000;
|
|
149
|
+
|
|
150
|
+
// Check if target reached
|
|
151
|
+
if (this.state.currentScore >= targetScore) {
|
|
152
|
+
this.state.status = "completed";
|
|
153
|
+
this.events.onIterationEnd?.(this.state.iteration, this.state.currentScore);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Update best if improved
|
|
158
|
+
if (this.state.currentScore > this.state.bestScore) {
|
|
159
|
+
this.state.bestScore = this.state.currentScore;
|
|
160
|
+
// Save the current configuration as best
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Attempt improvement
|
|
164
|
+
await this.attemptImprovement(agent, evalSummary);
|
|
165
|
+
|
|
166
|
+
// Record history
|
|
167
|
+
this.state.history.push({
|
|
168
|
+
iteration: this.state.iteration,
|
|
169
|
+
prompt: "", // Would store the current prompt
|
|
170
|
+
evalScore: this.state.currentScore,
|
|
171
|
+
changes: "Prompt/model adjustment",
|
|
172
|
+
timestamp: new Date(),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.events.onIterationEnd?.(this.state.iteration, this.state.currentScore);
|
|
176
|
+
this.saveState();
|
|
177
|
+
} catch (error) {
|
|
178
|
+
this.events.onError?.(
|
|
179
|
+
error instanceof Error ? error : new Error(String(error))
|
|
180
|
+
);
|
|
181
|
+
this.state.status = "failed";
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.state.endTime = new Date();
|
|
187
|
+
this.saveState();
|
|
188
|
+
this.events.onComplete?.(this.state);
|
|
189
|
+
|
|
190
|
+
return this.state;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private checkBudget(): boolean {
|
|
194
|
+
const budget = this.config.budgetPerIteration;
|
|
195
|
+
if (!budget) return true;
|
|
196
|
+
|
|
197
|
+
if (budget.maxTokens && this.state.totalTokensUsed >= budget.maxTokens) {
|
|
198
|
+
this.events.onBudgetWarning?.("Token budget exhausted");
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (budget.maxCost && this.state.totalCostUsed >= budget.maxCost) {
|
|
203
|
+
this.events.onBudgetWarning?.("Cost budget exhausted");
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (budget.maxTime && this.state.totalTimeUsed >= budget.maxTime) {
|
|
208
|
+
this.events.onBudgetWarning?.("Time budget exhausted");
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private async attemptImprovement(
|
|
216
|
+
agent: Agent,
|
|
217
|
+
evalSummary: EvalRunSummary
|
|
218
|
+
): Promise<void> {
|
|
219
|
+
// Get feedback for training
|
|
220
|
+
const trainingData = this.feedbackCollector.toTrainingData();
|
|
221
|
+
|
|
222
|
+
// If we have enough training data, attempt fine-tuning
|
|
223
|
+
if (trainingData.length >= 50 && this.modelTrainer) {
|
|
224
|
+
try {
|
|
225
|
+
const result = await this.modelTrainer.train(trainingData);
|
|
226
|
+
this.events.onTrainingComplete?.(result);
|
|
227
|
+
|
|
228
|
+
if (result.success && result.modelId) {
|
|
229
|
+
// Would update agent to use the new model
|
|
230
|
+
console.log(`New model trained: ${result.modelId}`);
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
this.events.onError?.(
|
|
234
|
+
error instanceof Error ? error : new Error(String(error))
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Analyze failed cases and suggest improvements
|
|
240
|
+
const failedCases = evalSummary.results.filter((r) => !r.passed);
|
|
241
|
+
if (failedCases.length > 0) {
|
|
242
|
+
// Could use the LLM to analyze failures and suggest prompt improvements
|
|
243
|
+
console.log(
|
|
244
|
+
`Analyzing ${failedCases.length} failed cases for improvement...`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Record interaction feedback
|
|
250
|
+
recordInteraction(params: {
|
|
251
|
+
sessionId: string;
|
|
252
|
+
userMessage: string;
|
|
253
|
+
agentResponse: string;
|
|
254
|
+
toolCalls: string[];
|
|
255
|
+
wasSuccessful: boolean;
|
|
256
|
+
metadata?: Record<string, unknown>;
|
|
257
|
+
}): string {
|
|
258
|
+
return this.feedbackCollector.recordInteraction(params);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Add user rating to feedback
|
|
262
|
+
rateFeedback(feedbackId: string, rating: number, corrections?: string): void {
|
|
263
|
+
this.feedbackCollector.addRating(feedbackId, rating, corrections);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Get evolution statistics
|
|
267
|
+
getStats(): {
|
|
268
|
+
state: EvolutionState;
|
|
269
|
+
feedbackStats: ReturnType<FeedbackCollector["getStats"]>;
|
|
270
|
+
} {
|
|
271
|
+
return {
|
|
272
|
+
state: this.state,
|
|
273
|
+
feedbackStats: this.feedbackCollector.getStats(),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Reset evolution state
|
|
278
|
+
reset(): void {
|
|
279
|
+
this.state = this.createInitialState();
|
|
280
|
+
this.saveState();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Export feedback for external training
|
|
284
|
+
exportFeedback(format: "jsonl" | "openai", outputPath: string): void {
|
|
285
|
+
if (format === "openai") {
|
|
286
|
+
this.feedbackCollector.exportAsOpenAIFormat(outputPath);
|
|
287
|
+
} else {
|
|
288
|
+
this.feedbackCollector.exportAsJsonl(outputPath);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Run a single evaluation cycle
|
|
293
|
+
async runEvaluation(agent: Agent): Promise<EvalRunSummary> {
|
|
294
|
+
const evalCases = this.config.evalCases || DEFAULT_EVAL_CASES;
|
|
295
|
+
return this.evalRunner.runEvalSuite(agent, evalCases);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Get formatted results
|
|
299
|
+
formatResults(): string {
|
|
300
|
+
let output = "\nEvolution Engine Status\n";
|
|
301
|
+
output += "=".repeat(50) + "\n\n";
|
|
302
|
+
output += `Status: ${this.state.status}\n`;
|
|
303
|
+
output += `Iteration: ${this.state.iteration}\n`;
|
|
304
|
+
output += `Current Score: ${(this.state.currentScore * 100).toFixed(1)}%\n`;
|
|
305
|
+
output += `Best Score: ${(this.state.bestScore * 100).toFixed(1)}%\n`;
|
|
306
|
+
output += `Tokens Used: ${this.state.totalTokensUsed.toLocaleString()}\n`;
|
|
307
|
+
output += `Time Used: ${this.state.totalTimeUsed.toFixed(1)}s\n\n`;
|
|
308
|
+
|
|
309
|
+
const feedbackStats = this.feedbackCollector.getStats();
|
|
310
|
+
output += "Feedback Statistics:\n";
|
|
311
|
+
output += ` Total: ${feedbackStats.total}\n`;
|
|
312
|
+
output += ` Rated: ${feedbackStats.rated}\n`;
|
|
313
|
+
output += ` Avg Rating: ${feedbackStats.avgRating}/5\n`;
|
|
314
|
+
output += ` Success Rate: ${feedbackStats.successRate}%\n`;
|
|
315
|
+
|
|
316
|
+
return output;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FeedbackCollector - Collects and stores training feedback from interactions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
import type { Feedback, TrainingData } from "./types.js";
|
|
9
|
+
|
|
10
|
+
export interface FeedbackCollectorConfig {
|
|
11
|
+
storagePath?: string;
|
|
12
|
+
maxFeedbackItems?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class FeedbackCollector {
|
|
16
|
+
private storagePath: string;
|
|
17
|
+
private maxItems: number;
|
|
18
|
+
private feedback: Feedback[] = [];
|
|
19
|
+
|
|
20
|
+
constructor(config: FeedbackCollectorConfig = {}) {
|
|
21
|
+
this.storagePath = config.storagePath || path.join(os.homedir(), ".pioneer", "feedback");
|
|
22
|
+
this.maxItems = config.maxFeedbackItems || 1000;
|
|
23
|
+
this.ensureStorageDir();
|
|
24
|
+
this.loadFeedback();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private ensureStorageDir(): void {
|
|
28
|
+
if (!fs.existsSync(this.storagePath)) {
|
|
29
|
+
fs.mkdirSync(this.storagePath, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private loadFeedback(): void {
|
|
34
|
+
const feedbackFile = path.join(this.storagePath, "feedback.json");
|
|
35
|
+
try {
|
|
36
|
+
if (fs.existsSync(feedbackFile)) {
|
|
37
|
+
const data = fs.readFileSync(feedbackFile, "utf-8");
|
|
38
|
+
this.feedback = JSON.parse(data);
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
this.feedback = [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private saveFeedback(): void {
|
|
46
|
+
const feedbackFile = path.join(this.storagePath, "feedback.json");
|
|
47
|
+
fs.writeFileSync(feedbackFile, JSON.stringify(this.feedback, null, 2));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
recordInteraction(params: {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
userMessage: string;
|
|
53
|
+
agentResponse: string;
|
|
54
|
+
toolCalls: string[];
|
|
55
|
+
wasSuccessful: boolean;
|
|
56
|
+
metadata?: Record<string, unknown>;
|
|
57
|
+
}): string {
|
|
58
|
+
const id = `fb_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
59
|
+
|
|
60
|
+
const feedback: Feedback = {
|
|
61
|
+
id,
|
|
62
|
+
sessionId: params.sessionId,
|
|
63
|
+
timestamp: new Date(),
|
|
64
|
+
userMessage: params.userMessage,
|
|
65
|
+
agentResponse: params.agentResponse,
|
|
66
|
+
toolCalls: params.toolCalls,
|
|
67
|
+
wasSuccessful: params.wasSuccessful,
|
|
68
|
+
metadata: params.metadata,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
this.feedback.push(feedback);
|
|
72
|
+
|
|
73
|
+
// Trim to max items
|
|
74
|
+
if (this.feedback.length > this.maxItems) {
|
|
75
|
+
this.feedback = this.feedback.slice(-this.maxItems);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.saveFeedback();
|
|
79
|
+
return id;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
addRating(feedbackId: string, rating: number, corrections?: string): void {
|
|
83
|
+
const item = this.feedback.find((f) => f.id === feedbackId);
|
|
84
|
+
if (item) {
|
|
85
|
+
item.rating = Math.max(1, Math.min(5, rating));
|
|
86
|
+
if (corrections) {
|
|
87
|
+
item.corrections = corrections;
|
|
88
|
+
}
|
|
89
|
+
this.saveFeedback();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getRecentFeedback(limit = 100): Feedback[] {
|
|
94
|
+
return this.feedback.slice(-limit);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getPositiveFeedback(minRating = 4): Feedback[] {
|
|
98
|
+
return this.feedback.filter(
|
|
99
|
+
(f) => f.rating !== undefined && f.rating >= minRating
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getNegativeFeedback(maxRating = 2): Feedback[] {
|
|
104
|
+
return this.feedback.filter(
|
|
105
|
+
(f) => f.rating !== undefined && f.rating <= maxRating
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getSuccessfulInteractions(): Feedback[] {
|
|
110
|
+
return this.feedback.filter((f) => f.wasSuccessful);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getCorrectedInteractions(): Feedback[] {
|
|
114
|
+
return this.feedback.filter((f) => f.corrections !== undefined);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Convert feedback to training data format
|
|
118
|
+
toTrainingData(): TrainingData[] {
|
|
119
|
+
const trainingData: TrainingData[] = [];
|
|
120
|
+
|
|
121
|
+
for (const fb of this.feedback) {
|
|
122
|
+
// Only use successful or highly-rated interactions
|
|
123
|
+
if (!fb.wasSuccessful && (fb.rating === undefined || fb.rating < 4)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Use corrections if available
|
|
128
|
+
const assistantContent = fb.corrections || fb.agentResponse;
|
|
129
|
+
|
|
130
|
+
trainingData.push({
|
|
131
|
+
id: fb.id,
|
|
132
|
+
messages: [
|
|
133
|
+
{ role: "user", content: fb.userMessage },
|
|
134
|
+
{ role: "assistant", content: assistantContent },
|
|
135
|
+
],
|
|
136
|
+
toolCalls: fb.toolCalls.map((name) => ({
|
|
137
|
+
name,
|
|
138
|
+
arguments: {},
|
|
139
|
+
result: "",
|
|
140
|
+
})),
|
|
141
|
+
metadata: fb.metadata,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return trainingData;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Export training data in various formats
|
|
149
|
+
exportAsJsonl(outputPath: string): void {
|
|
150
|
+
const data = this.toTrainingData();
|
|
151
|
+
const lines = data.map((d) => JSON.stringify(d));
|
|
152
|
+
fs.writeFileSync(outputPath, lines.join("\n"));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
exportAsOpenAIFormat(outputPath: string): void {
|
|
156
|
+
const data = this.toTrainingData();
|
|
157
|
+
const formatted = data.map((d) => ({
|
|
158
|
+
messages: d.messages,
|
|
159
|
+
}));
|
|
160
|
+
const lines = formatted.map((d) => JSON.stringify(d));
|
|
161
|
+
fs.writeFileSync(outputPath, lines.join("\n"));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Get statistics
|
|
165
|
+
getStats(): {
|
|
166
|
+
total: number;
|
|
167
|
+
rated: number;
|
|
168
|
+
avgRating: number;
|
|
169
|
+
successRate: number;
|
|
170
|
+
corrected: number;
|
|
171
|
+
} {
|
|
172
|
+
const rated = this.feedback.filter((f) => f.rating !== undefined);
|
|
173
|
+
const avgRating =
|
|
174
|
+
rated.length > 0
|
|
175
|
+
? rated.reduce((sum, f) => sum + (f.rating || 0), 0) / rated.length
|
|
176
|
+
: 0;
|
|
177
|
+
const successful = this.feedback.filter((f) => f.wasSuccessful);
|
|
178
|
+
const corrected = this.feedback.filter((f) => f.corrections !== undefined);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
total: this.feedback.length,
|
|
182
|
+
rated: rated.length,
|
|
183
|
+
avgRating: Math.round(avgRating * 100) / 100,
|
|
184
|
+
successRate:
|
|
185
|
+
this.feedback.length > 0
|
|
186
|
+
? Math.round((successful.length / this.feedback.length) * 100)
|
|
187
|
+
: 0,
|
|
188
|
+
corrected: corrected.length,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
clear(): void {
|
|
193
|
+
this.feedback = [];
|
|
194
|
+
this.saveFeedback();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|