@elizaos/plugin-trajectory-logger 2.0.0-alpha.1
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/node/index.node.js +841 -0
- package/dist/node/index.node.js.map +17 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
// action-interceptor.ts
|
|
2
|
+
import { logger } from "@elizaos/core";
|
|
3
|
+
var trajectoryContexts = new WeakMap;
|
|
4
|
+
function setTrajectoryContext(runtime, trajectoryId, trajectoryLogger) {
|
|
5
|
+
trajectoryContexts.set(runtime, { trajectoryId, logger: trajectoryLogger });
|
|
6
|
+
}
|
|
7
|
+
function getTrajectoryContext(runtime) {
|
|
8
|
+
return trajectoryContexts.get(runtime) || null;
|
|
9
|
+
}
|
|
10
|
+
function clearTrajectoryContext(runtime) {
|
|
11
|
+
trajectoryContexts.delete(runtime);
|
|
12
|
+
}
|
|
13
|
+
function wrapActionWithLogging(action, _trajectoryLogger) {
|
|
14
|
+
const originalHandler = action.handler;
|
|
15
|
+
return {
|
|
16
|
+
...action,
|
|
17
|
+
handler: async (runtime, message, state, options, callback) => {
|
|
18
|
+
const context = getTrajectoryContext(runtime);
|
|
19
|
+
if (!context) {
|
|
20
|
+
const result = await originalHandler(runtime, message, state, options, callback);
|
|
21
|
+
return result ?? undefined;
|
|
22
|
+
}
|
|
23
|
+
const { trajectoryId, logger: loggerService } = context;
|
|
24
|
+
const stepId = loggerService.getCurrentStepId(trajectoryId);
|
|
25
|
+
if (!stepId) {
|
|
26
|
+
logger.warn({ action: action.name, trajectoryId }, "No active step for action execution");
|
|
27
|
+
const result = await originalHandler(runtime, message, state, options, callback);
|
|
28
|
+
return result ?? undefined;
|
|
29
|
+
}
|
|
30
|
+
const successHandler = () => {
|
|
31
|
+
const stateSnapshot = state ? JSON.parse(JSON.stringify(state)) : null;
|
|
32
|
+
loggerService.completeStep(trajectoryId, stepId, {
|
|
33
|
+
actionType: action.name,
|
|
34
|
+
actionName: action.name,
|
|
35
|
+
parameters: {
|
|
36
|
+
message: message.content.text || "",
|
|
37
|
+
state: stateSnapshot
|
|
38
|
+
},
|
|
39
|
+
success: true,
|
|
40
|
+
result: { executed: true },
|
|
41
|
+
reasoning: `Action ${action.name} executed via ${action.description || "handler"}`
|
|
42
|
+
}, { reward: 0.1 });
|
|
43
|
+
};
|
|
44
|
+
const errorHandler = (err) => {
|
|
45
|
+
const error = err instanceof Error ? err.message : typeof err === "string" ? err : err.message || String(err);
|
|
46
|
+
logger.error({ action: action.name, trajectoryId, error }, "Action execution failed");
|
|
47
|
+
const stateSnapshot = state ? JSON.parse(JSON.stringify(state)) : null;
|
|
48
|
+
loggerService.completeStep(trajectoryId, stepId, {
|
|
49
|
+
actionType: action.name,
|
|
50
|
+
actionName: action.name,
|
|
51
|
+
parameters: {
|
|
52
|
+
message: message.content.text || "",
|
|
53
|
+
state: stateSnapshot
|
|
54
|
+
},
|
|
55
|
+
success: false,
|
|
56
|
+
result: { error },
|
|
57
|
+
reasoning: `Action ${action.name} failed: ${error}`
|
|
58
|
+
}, { reward: -0.1 });
|
|
59
|
+
throw err;
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
const result = await originalHandler(runtime, message, state, options, callback);
|
|
63
|
+
successHandler();
|
|
64
|
+
return result ?? undefined;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (err instanceof Error) {
|
|
67
|
+
return errorHandler(err);
|
|
68
|
+
}
|
|
69
|
+
if (typeof err === "string") {
|
|
70
|
+
return errorHandler(err);
|
|
71
|
+
}
|
|
72
|
+
return errorHandler(err);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function wrapPluginActions(plugin, trajectoryLogger) {
|
|
78
|
+
if (!plugin.actions || plugin.actions.length === 0) {
|
|
79
|
+
return plugin;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
...plugin,
|
|
83
|
+
actions: plugin.actions.map((action) => wrapActionWithLogging(action, trajectoryLogger))
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function logLLMCallFromAction(actionContext, trajectoryLogger, trajectoryId) {
|
|
87
|
+
const stepId = trajectoryLogger.getCurrentStepId(trajectoryId);
|
|
88
|
+
if (!stepId) {
|
|
89
|
+
logger.warn({ trajectoryId }, "No active step for LLM call from action");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
trajectoryLogger.logLLMCall(stepId, {
|
|
93
|
+
model: actionContext.model || "unknown",
|
|
94
|
+
systemPrompt: actionContext.systemPrompt || "",
|
|
95
|
+
userPrompt: actionContext.userPrompt || "",
|
|
96
|
+
response: actionContext.response || "",
|
|
97
|
+
reasoning: actionContext.reasoning || undefined,
|
|
98
|
+
temperature: actionContext.temperature || 0.7,
|
|
99
|
+
maxTokens: actionContext.maxTokens || 8192,
|
|
100
|
+
purpose: actionContext.purpose || "action",
|
|
101
|
+
actionType: actionContext.actionType || undefined,
|
|
102
|
+
promptTokens: actionContext.promptTokens || undefined,
|
|
103
|
+
completionTokens: actionContext.completionTokens || undefined,
|
|
104
|
+
latencyMs: actionContext.latencyMs || undefined
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function logProviderFromAction(actionContext, trajectoryLogger, trajectoryId) {
|
|
108
|
+
const stepId = trajectoryLogger.getCurrentStepId(trajectoryId);
|
|
109
|
+
if (!stepId) {
|
|
110
|
+
logger.warn({ trajectoryId }, "No active step for provider access from action");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
trajectoryLogger.logProviderAccess(stepId, {
|
|
114
|
+
providerName: actionContext.providerName || "unknown",
|
|
115
|
+
data: actionContext.data || {},
|
|
116
|
+
purpose: actionContext.purpose || "action",
|
|
117
|
+
query: actionContext.query || undefined
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function wrapProviderWithLogging(provider, _trajectoryLogger) {
|
|
121
|
+
const originalGet = provider.get;
|
|
122
|
+
return {
|
|
123
|
+
...provider,
|
|
124
|
+
get: async (runtime, message, state) => {
|
|
125
|
+
const context = getTrajectoryContext(runtime);
|
|
126
|
+
if (!context) {
|
|
127
|
+
return originalGet?.(runtime, message, state) || { text: "" };
|
|
128
|
+
}
|
|
129
|
+
const { trajectoryId, logger: loggerService } = context;
|
|
130
|
+
const stepId = loggerService.getCurrentStepId(trajectoryId);
|
|
131
|
+
if (!stepId) {
|
|
132
|
+
logger.warn({ provider: provider.name, trajectoryId }, "No active step for provider access");
|
|
133
|
+
return originalGet?.(runtime, message, state) || { text: "" };
|
|
134
|
+
}
|
|
135
|
+
const result = await originalGet?.(runtime, message, state) || { text: "" };
|
|
136
|
+
const stateSnapshot = state ? JSON.parse(JSON.stringify(state)) : null;
|
|
137
|
+
loggerService.logProviderAccess(stepId, {
|
|
138
|
+
providerName: provider.name,
|
|
139
|
+
data: {
|
|
140
|
+
text: result.text || "",
|
|
141
|
+
success: true
|
|
142
|
+
},
|
|
143
|
+
purpose: `Provider ${provider.name} accessed for context`,
|
|
144
|
+
query: {
|
|
145
|
+
message: message.content.text || "",
|
|
146
|
+
state: stateSnapshot
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function wrapPluginProviders(plugin, trajectoryLogger) {
|
|
154
|
+
if (!plugin.providers || plugin.providers.length === 0) {
|
|
155
|
+
return plugin;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
...plugin,
|
|
159
|
+
providers: plugin.providers.map((provider) => wrapProviderWithLogging(provider, trajectoryLogger))
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// art-format.ts
|
|
163
|
+
function toARTMessages(trajectory) {
|
|
164
|
+
const messages = [];
|
|
165
|
+
const systemMessage = buildSystemMessage(trajectory);
|
|
166
|
+
if (systemMessage) {
|
|
167
|
+
messages.push(systemMessage);
|
|
168
|
+
}
|
|
169
|
+
for (const step of trajectory.steps) {
|
|
170
|
+
const userContent = buildUserMessage(step);
|
|
171
|
+
if (userContent) {
|
|
172
|
+
messages.push({ role: "user", content: userContent });
|
|
173
|
+
}
|
|
174
|
+
const assistantContent = buildAssistantMessage(step);
|
|
175
|
+
if (assistantContent) {
|
|
176
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return messages;
|
|
180
|
+
}
|
|
181
|
+
function buildSystemMessage(trajectory) {
|
|
182
|
+
const firstStep = trajectory.steps[0];
|
|
183
|
+
const firstLLMCall = firstStep?.llmCalls?.[0];
|
|
184
|
+
if (firstLLMCall?.systemPrompt) {
|
|
185
|
+
return { role: "system", content: firstLLMCall.systemPrompt };
|
|
186
|
+
}
|
|
187
|
+
const agentName = trajectory.metadata.agentName || "Agent";
|
|
188
|
+
const goal = trajectory.metadata.goalDescription || "make good decisions";
|
|
189
|
+
return {
|
|
190
|
+
role: "system",
|
|
191
|
+
content: `You are ${agentName}, an autonomous agent. Your goal is to ${goal}.`
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function buildUserMessage(step) {
|
|
195
|
+
const llmCall = step.llmCalls.find((call) => call.purpose === "action");
|
|
196
|
+
if (llmCall?.userPrompt) {
|
|
197
|
+
return llmCall.userPrompt;
|
|
198
|
+
}
|
|
199
|
+
const parts = [];
|
|
200
|
+
parts.push("Current state:");
|
|
201
|
+
parts.push(`- Balance: $${step.environmentState.agentBalance}`);
|
|
202
|
+
parts.push(`- P&L: $${step.environmentState.agentPnL}`);
|
|
203
|
+
parts.push(`- Open Positions: ${step.environmentState.openPositions}`);
|
|
204
|
+
for (const provider of step.providerAccesses) {
|
|
205
|
+
parts.push(`
|
|
206
|
+
${provider.providerName} data:`);
|
|
207
|
+
parts.push(JSON.stringify(provider.data, null, 2));
|
|
208
|
+
}
|
|
209
|
+
parts.push(`
|
|
210
|
+
What action should you take?`);
|
|
211
|
+
return parts.join(`
|
|
212
|
+
`);
|
|
213
|
+
}
|
|
214
|
+
function buildAssistantMessage(step) {
|
|
215
|
+
const llmCall = step.llmCalls.find((call) => call.purpose === "action");
|
|
216
|
+
if (llmCall?.response) {
|
|
217
|
+
return llmCall.response;
|
|
218
|
+
}
|
|
219
|
+
const action = step.action;
|
|
220
|
+
const parts = [];
|
|
221
|
+
parts.push(`I will ${action.actionType}.`);
|
|
222
|
+
if (action.reasoning) {
|
|
223
|
+
parts.push(`Reasoning: ${action.reasoning}`);
|
|
224
|
+
}
|
|
225
|
+
parts.push(`Parameters: ${JSON.stringify(action.parameters)}`);
|
|
226
|
+
return parts.join(`
|
|
227
|
+
`);
|
|
228
|
+
}
|
|
229
|
+
function toARTTrajectory(trajectory) {
|
|
230
|
+
return {
|
|
231
|
+
messages: toARTMessages(trajectory),
|
|
232
|
+
reward: trajectory.totalReward,
|
|
233
|
+
metadata: {
|
|
234
|
+
trajectoryId: trajectory.trajectoryId,
|
|
235
|
+
agentId: trajectory.agentId,
|
|
236
|
+
scenarioId: trajectory.scenarioId,
|
|
237
|
+
groupIndex: trajectory.groupIndex,
|
|
238
|
+
environmentContext: {
|
|
239
|
+
initialBalance: trajectory.steps[0]?.environmentState.agentBalance || 0,
|
|
240
|
+
finalBalance: trajectory.metrics.finalBalance || 0,
|
|
241
|
+
initialPnL: trajectory.steps[0]?.environmentState.agentPnL || 0,
|
|
242
|
+
finalPnL: trajectory.metrics.finalPnL || 0,
|
|
243
|
+
actionsTaken: trajectory.steps.map((s) => s.action.actionType),
|
|
244
|
+
errors: trajectory.steps.filter((s) => !s.action.success).map((s) => s.action.error || "Unknown error")
|
|
245
|
+
},
|
|
246
|
+
gameKnowledge: extractGameKnowledge(trajectory),
|
|
247
|
+
metrics: JSON.parse(JSON.stringify(trajectory.metrics))
|
|
248
|
+
},
|
|
249
|
+
metrics: filterNumericMetrics(trajectory.metrics)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function filterNumericMetrics(metrics) {
|
|
253
|
+
const numericMetrics = {};
|
|
254
|
+
for (const [key, value] of Object.entries(metrics)) {
|
|
255
|
+
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
256
|
+
numericMetrics[key] = value;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return numericMetrics;
|
|
260
|
+
}
|
|
261
|
+
function extractGameKnowledge(trajectory) {
|
|
262
|
+
const knowledge = {};
|
|
263
|
+
if (trajectory.metadata.trueProbabilities) {
|
|
264
|
+
knowledge.trueProbabilities = trajectory.metadata.trueProbabilities;
|
|
265
|
+
}
|
|
266
|
+
if (trajectory.metadata.futureOutcomes) {
|
|
267
|
+
knowledge.actualOutcomes = trajectory.metadata.futureOutcomes;
|
|
268
|
+
}
|
|
269
|
+
if (trajectory.metadata.hiddenVariables) {
|
|
270
|
+
knowledge.hiddenVariables = trajectory.metadata.hiddenVariables;
|
|
271
|
+
}
|
|
272
|
+
const gameEvents = trajectory.steps.map((s) => s.metadata?.gameEvent).filter((e) => !!e);
|
|
273
|
+
if (gameEvents.length > 0) {
|
|
274
|
+
knowledge.gameEvents = gameEvents;
|
|
275
|
+
}
|
|
276
|
+
return knowledge;
|
|
277
|
+
}
|
|
278
|
+
function groupTrajectories(trajectories) {
|
|
279
|
+
const groups = new Map;
|
|
280
|
+
for (const traj of trajectories) {
|
|
281
|
+
const scenarioId = traj.scenarioId || "default";
|
|
282
|
+
if (!groups.has(scenarioId)) {
|
|
283
|
+
groups.set(scenarioId, []);
|
|
284
|
+
}
|
|
285
|
+
groups.get(scenarioId)?.push(traj);
|
|
286
|
+
}
|
|
287
|
+
return Array.from(groups.entries()).map(([scenarioId, trajs], idx) => ({
|
|
288
|
+
groupId: `group-${idx}`,
|
|
289
|
+
scenarioId,
|
|
290
|
+
trajectories: trajs,
|
|
291
|
+
sharedPrefix: extractSharedPrefix(trajs),
|
|
292
|
+
createdAt: Date.now()
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
function extractSharedPrefix(trajectories) {
|
|
296
|
+
if (trajectories.length === 0)
|
|
297
|
+
return [];
|
|
298
|
+
const allMessages = trajectories.map((t) => toARTMessages(t));
|
|
299
|
+
if (allMessages.length === 0)
|
|
300
|
+
return [];
|
|
301
|
+
const firstMessages = allMessages[0];
|
|
302
|
+
if (!firstMessages)
|
|
303
|
+
return [];
|
|
304
|
+
const sharedPrefix = [];
|
|
305
|
+
for (let i = 0;i < firstMessages.length; i++) {
|
|
306
|
+
const message = firstMessages[i];
|
|
307
|
+
if (!message)
|
|
308
|
+
break;
|
|
309
|
+
const allMatch = allMessages.every((msgs) => msgs[i] && msgs[i]?.role === message.role && msgs[i]?.content === message.content);
|
|
310
|
+
if (allMatch) {
|
|
311
|
+
sharedPrefix.push(message);
|
|
312
|
+
} else {
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return sharedPrefix;
|
|
317
|
+
}
|
|
318
|
+
function removeSharedPrefix(messages, sharedPrefix) {
|
|
319
|
+
return messages.slice(sharedPrefix.length);
|
|
320
|
+
}
|
|
321
|
+
function prepareForRULER(group) {
|
|
322
|
+
const artTrajs = group.trajectories.map((t) => toARTTrajectory(t));
|
|
323
|
+
const sharedPrefix = group.sharedPrefix || extractSharedPrefix(group.trajectories);
|
|
324
|
+
return {
|
|
325
|
+
sharedPrefix,
|
|
326
|
+
suffixes: artTrajs.map((art) => removeSharedPrefix(art.messages, sharedPrefix)),
|
|
327
|
+
metadata: artTrajs.map((art) => art.metadata)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function toARTJSONL(trajectory) {
|
|
331
|
+
return JSON.stringify(toARTTrajectory(trajectory));
|
|
332
|
+
}
|
|
333
|
+
function validateARTCompatibility(trajectory) {
|
|
334
|
+
const errors = [];
|
|
335
|
+
const warnings = [];
|
|
336
|
+
if (trajectory.steps.length === 0) {
|
|
337
|
+
errors.push("Trajectory has no steps");
|
|
338
|
+
}
|
|
339
|
+
for (const [idx, step] of trajectory.steps.entries()) {
|
|
340
|
+
if (step.llmCalls.length === 0) {
|
|
341
|
+
errors.push(`Step ${idx} has no LLM calls - can't extract messages`);
|
|
342
|
+
}
|
|
343
|
+
for (const llmCall of step.llmCalls) {
|
|
344
|
+
if (!llmCall.userPrompt || llmCall.userPrompt.length < 10) {
|
|
345
|
+
warnings.push(`Step ${idx} has very short user prompt`);
|
|
346
|
+
}
|
|
347
|
+
if (!llmCall.response || llmCall.response.length < 5) {
|
|
348
|
+
warnings.push(`Step ${idx} has very short response`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (trajectory.totalReward === undefined || Number.isNaN(trajectory.totalReward)) {
|
|
353
|
+
errors.push("Trajectory has no valid reward");
|
|
354
|
+
}
|
|
355
|
+
const artTraj = toARTTrajectory(trajectory);
|
|
356
|
+
if (artTraj.messages.length < 2) {
|
|
357
|
+
warnings.push("Trajectory converts to very few messages (< 2)");
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
valid: errors.length === 0,
|
|
361
|
+
errors,
|
|
362
|
+
warnings
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// export.ts
|
|
366
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
367
|
+
import { dirname, join } from "node:path";
|
|
368
|
+
async function exportToHuggingFace(options) {
|
|
369
|
+
return exportForOpenPipeART(options);
|
|
370
|
+
}
|
|
371
|
+
async function exportGroupedByScenario(options) {
|
|
372
|
+
const trajectories = filterTrajectories(options.trajectories || [], options);
|
|
373
|
+
const outPath = resolveOutputPath(options, "grouped-by-scenario.json");
|
|
374
|
+
const grouped = {};
|
|
375
|
+
for (const t of trajectories) {
|
|
376
|
+
const scenarioId = t.scenarioId || "default";
|
|
377
|
+
if (!grouped[scenarioId])
|
|
378
|
+
grouped[scenarioId] = [];
|
|
379
|
+
grouped[scenarioId]?.push(t);
|
|
380
|
+
}
|
|
381
|
+
await writeJson(outPath, grouped);
|
|
382
|
+
return { success: true, trajectoriesExported: trajectories.length, datasetUrl: outPath };
|
|
383
|
+
}
|
|
384
|
+
async function exportForOpenPipeART(options) {
|
|
385
|
+
const trajectories = filterTrajectories(options.trajectories || [], options);
|
|
386
|
+
const outPath = resolveOutputPath(options, "trajectories.art.jsonl");
|
|
387
|
+
const lines = `${trajectories.map((t) => toARTJSONL(t)).join(`
|
|
388
|
+
`)}
|
|
389
|
+
`;
|
|
390
|
+
await writeText(outPath, lines);
|
|
391
|
+
return { success: true, trajectoriesExported: trajectories.length, datasetUrl: outPath };
|
|
392
|
+
}
|
|
393
|
+
async function exportGroupedForGRPO(options) {
|
|
394
|
+
const trajectories = filterTrajectories(options.trajectories || [], options);
|
|
395
|
+
const outPath = resolveOutputPath(options, "trajectories.grpo.groups.json");
|
|
396
|
+
const groups = groupTrajectories(trajectories);
|
|
397
|
+
await writeJson(outPath, groups);
|
|
398
|
+
return { success: true, trajectoriesExported: trajectories.length, datasetUrl: outPath };
|
|
399
|
+
}
|
|
400
|
+
function filterTrajectories(trajectories, options) {
|
|
401
|
+
let out = trajectories.slice();
|
|
402
|
+
if (options.startDate) {
|
|
403
|
+
const startMs = options.startDate.getTime();
|
|
404
|
+
out = out.filter((t) => t.startTime >= startMs);
|
|
405
|
+
}
|
|
406
|
+
if (options.endDate) {
|
|
407
|
+
const endMs = options.endDate.getTime();
|
|
408
|
+
out = out.filter((t) => t.startTime <= endMs);
|
|
409
|
+
}
|
|
410
|
+
if (options.agentIds && options.agentIds.length > 0) {
|
|
411
|
+
const set = new Set(options.agentIds);
|
|
412
|
+
out = out.filter((t) => set.has(t.agentId));
|
|
413
|
+
}
|
|
414
|
+
if (options.scenarioIds && options.scenarioIds.length > 0) {
|
|
415
|
+
const set = new Set(options.scenarioIds);
|
|
416
|
+
out = out.filter((t) => t.scenarioId && set.has(t.scenarioId));
|
|
417
|
+
}
|
|
418
|
+
if (typeof options.minReward === "number") {
|
|
419
|
+
const minReward = options.minReward;
|
|
420
|
+
out = out.filter((t) => t.totalReward >= minReward);
|
|
421
|
+
}
|
|
422
|
+
if (typeof options.maxReward === "number") {
|
|
423
|
+
const maxReward = options.maxReward;
|
|
424
|
+
out = out.filter((t) => t.totalReward <= maxReward);
|
|
425
|
+
}
|
|
426
|
+
const limit = options.maxTrajectories || out.length;
|
|
427
|
+
return out.slice(0, limit);
|
|
428
|
+
}
|
|
429
|
+
function resolveOutputPath(options, fallbackFileName) {
|
|
430
|
+
if (options.outputPath)
|
|
431
|
+
return options.outputPath;
|
|
432
|
+
if (options.outputDir)
|
|
433
|
+
return join(options.outputDir, fallbackFileName);
|
|
434
|
+
const safeName = options.datasetName.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
435
|
+
return join(process.cwd(), `${safeName}.${fallbackFileName}`);
|
|
436
|
+
}
|
|
437
|
+
async function writeText(path, content) {
|
|
438
|
+
await mkdir(dirname(path), { recursive: true });
|
|
439
|
+
await writeFile(path, content, "utf8");
|
|
440
|
+
}
|
|
441
|
+
async function writeJson(path, data) {
|
|
442
|
+
await writeText(path, `${JSON.stringify(data, null, 2)}
|
|
443
|
+
`);
|
|
444
|
+
}
|
|
445
|
+
// game-rewards.ts
|
|
446
|
+
function computeTrajectoryReward(trajectory) {
|
|
447
|
+
return trajectory.totalReward;
|
|
448
|
+
}
|
|
449
|
+
function computeStepReward(step) {
|
|
450
|
+
return step.reward || 0;
|
|
451
|
+
}
|
|
452
|
+
async function buildGameStateFromDB(_trajectoryId) {
|
|
453
|
+
return {};
|
|
454
|
+
}
|
|
455
|
+
async function recomputeTrajectoryRewards(_trajectoryIds) {}
|
|
456
|
+
// integration.ts
|
|
457
|
+
import { logger as logger2 } from "@elizaos/core";
|
|
458
|
+
function startAutonomousTick(trajectoryLogger, context) {
|
|
459
|
+
const trajectoryId = trajectoryLogger.startTrajectory(context.agentId, {
|
|
460
|
+
scenarioId: context.scenarioId,
|
|
461
|
+
episodeId: context.episodeId,
|
|
462
|
+
batchId: context.batchId,
|
|
463
|
+
metadata: context.metadata
|
|
464
|
+
});
|
|
465
|
+
const envState = {
|
|
466
|
+
timestamp: Date.now(),
|
|
467
|
+
agentBalance: 0,
|
|
468
|
+
agentPoints: 0,
|
|
469
|
+
agentPnL: 0,
|
|
470
|
+
openPositions: 0
|
|
471
|
+
};
|
|
472
|
+
trajectoryLogger.startStep(trajectoryId, envState);
|
|
473
|
+
logger2.info({ trajectoryId, agentId: context.agentId }, "Started autonomous tick trajectory");
|
|
474
|
+
return trajectoryId;
|
|
475
|
+
}
|
|
476
|
+
async function endAutonomousTick(trajectoryLogger, trajectoryId, status = "completed", finalMetrics) {
|
|
477
|
+
await trajectoryLogger.endTrajectory(trajectoryId, status, finalMetrics);
|
|
478
|
+
logger2.info({ trajectoryId, status }, "Ended autonomous tick trajectory");
|
|
479
|
+
}
|
|
480
|
+
async function loggedLLMCall(trajectoryLogger, trajectoryId, options, llmCallFn) {
|
|
481
|
+
const stepId = trajectoryLogger.getCurrentStepId(trajectoryId);
|
|
482
|
+
if (!stepId) {
|
|
483
|
+
logger2.warn({ trajectoryId }, "No active step for LLM call");
|
|
484
|
+
const result2 = await llmCallFn();
|
|
485
|
+
return result2.text;
|
|
486
|
+
}
|
|
487
|
+
const startTime = Date.now();
|
|
488
|
+
const result = await llmCallFn();
|
|
489
|
+
const latencyMs = Date.now() - startTime;
|
|
490
|
+
trajectoryLogger.logLLMCall(stepId, {
|
|
491
|
+
model: options.model,
|
|
492
|
+
modelVersion: options.modelVersion,
|
|
493
|
+
systemPrompt: options.systemPrompt,
|
|
494
|
+
userPrompt: options.userPrompt,
|
|
495
|
+
response: result.text,
|
|
496
|
+
reasoning: result.reasoning,
|
|
497
|
+
temperature: options.temperature || 0.7,
|
|
498
|
+
maxTokens: options.maxTokens || 8192,
|
|
499
|
+
purpose: options.purpose || "action",
|
|
500
|
+
actionType: options.actionType,
|
|
501
|
+
promptTokens: result.tokens?.prompt,
|
|
502
|
+
completionTokens: result.tokens?.completion,
|
|
503
|
+
latencyMs: result.latencyMs || latencyMs
|
|
504
|
+
});
|
|
505
|
+
return result.text;
|
|
506
|
+
}
|
|
507
|
+
function logProviderAccess(trajectoryLogger, trajectoryId, access) {
|
|
508
|
+
trajectoryLogger.logProviderAccessByTrajectoryId(trajectoryId, access);
|
|
509
|
+
}
|
|
510
|
+
function withTrajectoryLogging(fn, trajectoryLogger, trajectoryId, context = {}) {
|
|
511
|
+
return async (...args) => {
|
|
512
|
+
const stepId = trajectoryLogger.getCurrentStepId(trajectoryId);
|
|
513
|
+
if (!stepId) {
|
|
514
|
+
return fn(...args);
|
|
515
|
+
}
|
|
516
|
+
const result = await fn(...args);
|
|
517
|
+
trajectoryLogger.completeStep(trajectoryId, stepId, {
|
|
518
|
+
actionType: context.actionType || "function_call",
|
|
519
|
+
actionName: fn.name || "anonymous",
|
|
520
|
+
parameters: { args },
|
|
521
|
+
success: true,
|
|
522
|
+
result: result !== undefined ? { result } : { result: null }
|
|
523
|
+
}, { reward: 0.05 });
|
|
524
|
+
return result;
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
// reward-service.ts
|
|
528
|
+
class RewardService {
|
|
529
|
+
options;
|
|
530
|
+
constructor(options = {}) {
|
|
531
|
+
this.options = options;
|
|
532
|
+
}
|
|
533
|
+
async scoreTrajectory(trajectory) {
|
|
534
|
+
if (this.options.useHeuristics !== false) {
|
|
535
|
+
return this.computeHeuristicReward(trajectory);
|
|
536
|
+
}
|
|
537
|
+
return this.computeHeuristicReward(trajectory);
|
|
538
|
+
}
|
|
539
|
+
async scoreTrajectoryGroup(trajectories) {
|
|
540
|
+
if (trajectories.length === 0) {
|
|
541
|
+
return [];
|
|
542
|
+
}
|
|
543
|
+
if (trajectories.length === 1) {
|
|
544
|
+
const first = trajectories[0];
|
|
545
|
+
if (!first)
|
|
546
|
+
return [];
|
|
547
|
+
const score = await this.scoreTrajectory(first);
|
|
548
|
+
return [this.normalizeScore(score)];
|
|
549
|
+
}
|
|
550
|
+
const rawScores = await Promise.all(trajectories.map((t) => this.scoreTrajectory(t)));
|
|
551
|
+
return this.normalizeScoresForGroup(rawScores);
|
|
552
|
+
}
|
|
553
|
+
computeHeuristicReward(trajectory) {
|
|
554
|
+
const components = trajectory.rewardComponents;
|
|
555
|
+
const metrics = trajectory.metrics;
|
|
556
|
+
let reward = 0;
|
|
557
|
+
let weightSum = 0;
|
|
558
|
+
if (metrics.finalPnL !== undefined) {
|
|
559
|
+
const pnlScore = this.normalizePnL(metrics.finalPnL);
|
|
560
|
+
reward += pnlScore * 0.4;
|
|
561
|
+
weightSum += 0.4;
|
|
562
|
+
}
|
|
563
|
+
if (metrics.successRate !== undefined) {
|
|
564
|
+
const successScore = metrics.successRate * 2 - 1;
|
|
565
|
+
reward += successScore * 0.3;
|
|
566
|
+
weightSum += 0.3;
|
|
567
|
+
}
|
|
568
|
+
const completionScore = metrics.finalStatus === "completed" ? 1 : -0.5;
|
|
569
|
+
reward += completionScore * 0.2;
|
|
570
|
+
weightSum += 0.2;
|
|
571
|
+
if (components.environmentReward !== undefined) {
|
|
572
|
+
const envScore = Math.max(-1, Math.min(1, components.environmentReward));
|
|
573
|
+
reward += envScore * 0.1;
|
|
574
|
+
weightSum += 0.1;
|
|
575
|
+
}
|
|
576
|
+
if (weightSum > 0) {
|
|
577
|
+
reward = reward / weightSum;
|
|
578
|
+
}
|
|
579
|
+
return Math.max(-1, Math.min(1, reward));
|
|
580
|
+
}
|
|
581
|
+
normalizePnL(pnl) {
|
|
582
|
+
return Math.tanh(pnl / 500);
|
|
583
|
+
}
|
|
584
|
+
normalizeScore(score) {
|
|
585
|
+
return (score + 1) / 2;
|
|
586
|
+
}
|
|
587
|
+
normalizeScoresForGroup(scores) {
|
|
588
|
+
const min = Math.min(...scores);
|
|
589
|
+
const max = Math.max(...scores);
|
|
590
|
+
const range = max - min;
|
|
591
|
+
if (range === 0) {
|
|
592
|
+
return scores.map(() => 0.5);
|
|
593
|
+
}
|
|
594
|
+
return scores.map((s) => (s - min) / range);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function createRewardService(options = {}) {
|
|
598
|
+
return new RewardService(options);
|
|
599
|
+
}
|
|
600
|
+
async function scoreTrajectory(trajectory) {
|
|
601
|
+
const service = new RewardService;
|
|
602
|
+
return service.scoreTrajectory(trajectory);
|
|
603
|
+
}
|
|
604
|
+
async function scoreTrajectoryGroup(trajectories) {
|
|
605
|
+
const service = new RewardService;
|
|
606
|
+
return service.scoreTrajectoryGroup(trajectories);
|
|
607
|
+
}
|
|
608
|
+
// TrajectoryLoggerService.ts
|
|
609
|
+
import { asUUID, logger as logger3 } from "@elizaos/core";
|
|
610
|
+
import { v4 as uuidv4 } from "uuid";
|
|
611
|
+
|
|
612
|
+
class TrajectoryLoggerService {
|
|
613
|
+
activeTrajectories = new Map;
|
|
614
|
+
activeStepIds = new Map;
|
|
615
|
+
startTrajectory(agentId, options = {}) {
|
|
616
|
+
const trajectoryId = uuidv4();
|
|
617
|
+
const now = Date.now();
|
|
618
|
+
const trajectory = {
|
|
619
|
+
trajectoryId: asUUID(trajectoryId),
|
|
620
|
+
agentId: asUUID(agentId),
|
|
621
|
+
startTime: now,
|
|
622
|
+
endTime: now,
|
|
623
|
+
durationMs: 0,
|
|
624
|
+
episodeId: options.episodeId,
|
|
625
|
+
scenarioId: options.scenarioId,
|
|
626
|
+
batchId: options.batchId,
|
|
627
|
+
groupIndex: options.groupIndex,
|
|
628
|
+
steps: [],
|
|
629
|
+
totalReward: 0,
|
|
630
|
+
rewardComponents: {
|
|
631
|
+
environmentReward: 0
|
|
632
|
+
},
|
|
633
|
+
metrics: {
|
|
634
|
+
episodeLength: 0,
|
|
635
|
+
finalStatus: "completed"
|
|
636
|
+
},
|
|
637
|
+
metadata: options.metadata || {}
|
|
638
|
+
};
|
|
639
|
+
this.activeTrajectories.set(trajectoryId, trajectory);
|
|
640
|
+
return trajectoryId;
|
|
641
|
+
}
|
|
642
|
+
startStep(trajectoryId, envState) {
|
|
643
|
+
const stepId = uuidv4();
|
|
644
|
+
const trajectory = this.activeTrajectories.get(trajectoryId);
|
|
645
|
+
if (!trajectory) {
|
|
646
|
+
throw new Error(`Trajectory ${trajectoryId} not found`);
|
|
647
|
+
}
|
|
648
|
+
const step = {
|
|
649
|
+
stepId: asUUID(stepId),
|
|
650
|
+
stepNumber: trajectory.steps.length,
|
|
651
|
+
timestamp: envState.timestamp || Date.now(),
|
|
652
|
+
environmentState: envState,
|
|
653
|
+
observation: {},
|
|
654
|
+
llmCalls: [],
|
|
655
|
+
providerAccesses: [],
|
|
656
|
+
action: {
|
|
657
|
+
attemptId: "",
|
|
658
|
+
timestamp: 0,
|
|
659
|
+
actionType: "pending",
|
|
660
|
+
actionName: "pending",
|
|
661
|
+
parameters: {},
|
|
662
|
+
success: false
|
|
663
|
+
},
|
|
664
|
+
reward: 0,
|
|
665
|
+
done: false
|
|
666
|
+
};
|
|
667
|
+
trajectory.steps.push(step);
|
|
668
|
+
this.activeStepIds.set(trajectoryId, stepId);
|
|
669
|
+
return stepId;
|
|
670
|
+
}
|
|
671
|
+
logLLMCall(stepId, llmCall) {
|
|
672
|
+
const trajectory = this.findTrajectoryByStepId(stepId);
|
|
673
|
+
if (!trajectory) {
|
|
674
|
+
logger3.warn({ stepId }, "Trajectory not found for LLM call");
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const step = trajectory.steps.find((s) => s.stepId === stepId);
|
|
678
|
+
if (!step) {
|
|
679
|
+
logger3.warn({ stepId }, "Step not found for LLM call");
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
const fullLLMCall = {
|
|
683
|
+
callId: uuidv4(),
|
|
684
|
+
timestamp: Date.now(),
|
|
685
|
+
...llmCall
|
|
686
|
+
};
|
|
687
|
+
step.llmCalls.push(fullLLMCall);
|
|
688
|
+
}
|
|
689
|
+
logProviderAccess(stepId, access) {
|
|
690
|
+
const trajectory = this.findTrajectoryByStepId(stepId);
|
|
691
|
+
if (!trajectory) {
|
|
692
|
+
logger3.warn({ stepId }, "Trajectory not found for provider access");
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const step = trajectory.steps.find((s) => s.stepId === stepId);
|
|
696
|
+
if (!step) {
|
|
697
|
+
logger3.warn({ stepId }, "Step not found for provider access");
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const fullAccess = {
|
|
701
|
+
providerId: uuidv4(),
|
|
702
|
+
timestamp: Date.now(),
|
|
703
|
+
...access
|
|
704
|
+
};
|
|
705
|
+
step.providerAccesses.push(fullAccess);
|
|
706
|
+
}
|
|
707
|
+
logLLMCallByTrajectoryId(trajectoryId, llmCall) {
|
|
708
|
+
const stepId = this.activeStepIds.get(trajectoryId);
|
|
709
|
+
if (!stepId) {
|
|
710
|
+
logger3.warn({ trajectoryId }, "No active step for trajectory");
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
this.logLLMCall(stepId, llmCall);
|
|
714
|
+
}
|
|
715
|
+
logProviderAccessByTrajectoryId(trajectoryId, access) {
|
|
716
|
+
const stepId = this.activeStepIds.get(trajectoryId);
|
|
717
|
+
if (!stepId) {
|
|
718
|
+
logger3.warn({ trajectoryId }, "No active step for trajectory");
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
this.logProviderAccess(stepId, access);
|
|
722
|
+
}
|
|
723
|
+
getCurrentStepId(trajectoryId) {
|
|
724
|
+
return this.activeStepIds.get(trajectoryId) || null;
|
|
725
|
+
}
|
|
726
|
+
completeStep(trajectoryId, stepId, action, rewardInfo) {
|
|
727
|
+
const trajectory = this.activeTrajectories.get(trajectoryId);
|
|
728
|
+
if (!trajectory) {
|
|
729
|
+
logger3.warn({ trajectoryId }, "Trajectory not found for completeStep");
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const step = trajectory.steps.find((s) => s.stepId === stepId);
|
|
733
|
+
if (!step) {
|
|
734
|
+
logger3.warn({ trajectoryId, stepId }, "Step not found for completeStep");
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
step.action = {
|
|
738
|
+
attemptId: uuidv4(),
|
|
739
|
+
timestamp: Date.now(),
|
|
740
|
+
...action
|
|
741
|
+
};
|
|
742
|
+
if (rewardInfo?.reward !== undefined) {
|
|
743
|
+
step.reward = rewardInfo.reward;
|
|
744
|
+
trajectory.totalReward += rewardInfo.reward;
|
|
745
|
+
}
|
|
746
|
+
if (rewardInfo?.components) {
|
|
747
|
+
trajectory.rewardComponents = {
|
|
748
|
+
...trajectory.rewardComponents,
|
|
749
|
+
...rewardInfo.components
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
this.activeStepIds.delete(trajectoryId);
|
|
753
|
+
}
|
|
754
|
+
completeCurrentStep(trajectoryId, action, rewardInfo) {
|
|
755
|
+
const stepId = this.activeStepIds.get(trajectoryId);
|
|
756
|
+
if (!stepId) {
|
|
757
|
+
logger3.warn({ trajectoryId }, "No active step for trajectory");
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
this.completeStep(trajectoryId, stepId, action, rewardInfo);
|
|
761
|
+
}
|
|
762
|
+
async endTrajectory(trajectoryId, status, finalMetrics) {
|
|
763
|
+
const trajectory = this.activeTrajectories.get(trajectoryId);
|
|
764
|
+
if (!trajectory) {
|
|
765
|
+
logger3.warn({ trajectoryId }, "Trajectory not found for endTrajectory");
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
trajectory.endTime = Date.now();
|
|
769
|
+
trajectory.durationMs = trajectory.endTime - trajectory.startTime;
|
|
770
|
+
trajectory.metrics.finalStatus = status;
|
|
771
|
+
trajectory.metrics.episodeLength = trajectory.steps.length;
|
|
772
|
+
if (finalMetrics) {
|
|
773
|
+
trajectory.metrics = {
|
|
774
|
+
...trajectory.metrics,
|
|
775
|
+
...finalMetrics
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
this.activeStepIds.delete(trajectoryId);
|
|
779
|
+
}
|
|
780
|
+
getActiveTrajectory(trajectoryId) {
|
|
781
|
+
return this.activeTrajectories.get(trajectoryId) || null;
|
|
782
|
+
}
|
|
783
|
+
findTrajectoryByStepId(stepId) {
|
|
784
|
+
for (const trajectory of this.activeTrajectories.values()) {
|
|
785
|
+
if (trajectory.steps.some((s) => s.stepId === stepId)) {
|
|
786
|
+
return trajectory;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// index.ts
|
|
794
|
+
var trajectoryLoggerPlugin = {
|
|
795
|
+
name: "@elizaos/plugin-trajectory-logger",
|
|
796
|
+
description: "Collects complete agent interaction trajectory data for RL training. Records LLM calls, provider access, actions, environment state, and computes rewards.",
|
|
797
|
+
dependencies: [],
|
|
798
|
+
services: []
|
|
799
|
+
};
|
|
800
|
+
var typescript_default = trajectoryLoggerPlugin;
|
|
801
|
+
export {
|
|
802
|
+
wrapProviderWithLogging,
|
|
803
|
+
wrapPluginProviders,
|
|
804
|
+
wrapPluginActions,
|
|
805
|
+
wrapActionWithLogging,
|
|
806
|
+
withTrajectoryLogging,
|
|
807
|
+
validateARTCompatibility,
|
|
808
|
+
trajectoryLoggerPlugin,
|
|
809
|
+
toARTTrajectory,
|
|
810
|
+
toARTMessages,
|
|
811
|
+
toARTJSONL,
|
|
812
|
+
startAutonomousTick,
|
|
813
|
+
setTrajectoryContext,
|
|
814
|
+
scoreTrajectoryGroup,
|
|
815
|
+
scoreTrajectory,
|
|
816
|
+
removeSharedPrefix,
|
|
817
|
+
recomputeTrajectoryRewards,
|
|
818
|
+
prepareForRULER,
|
|
819
|
+
loggedLLMCall,
|
|
820
|
+
logProviderFromAction,
|
|
821
|
+
logProviderAccess,
|
|
822
|
+
logLLMCallFromAction,
|
|
823
|
+
groupTrajectories,
|
|
824
|
+
getTrajectoryContext,
|
|
825
|
+
extractSharedPrefix,
|
|
826
|
+
exportToHuggingFace,
|
|
827
|
+
exportGroupedForGRPO,
|
|
828
|
+
exportGroupedByScenario,
|
|
829
|
+
exportForOpenPipeART,
|
|
830
|
+
endAutonomousTick,
|
|
831
|
+
typescript_default as default,
|
|
832
|
+
createRewardService,
|
|
833
|
+
computeTrajectoryReward,
|
|
834
|
+
computeStepReward,
|
|
835
|
+
clearTrajectoryContext,
|
|
836
|
+
buildGameStateFromDB,
|
|
837
|
+
TrajectoryLoggerService,
|
|
838
|
+
RewardService
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
//# debugId=8E8ACF830258000B64756E2164756E21
|