@llmbridge/plugin-logging 0.1.2 → 0.1.3
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/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +121 -79
- package/dist/index.mjs +121 -79
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -15,19 +15,21 @@ declare function generateRandomDigits(length: number): string;
|
|
|
15
15
|
declare class LoggingPlugin implements LLMBridge.Plugin<void, string, void, void> {
|
|
16
16
|
private options;
|
|
17
17
|
private logger;
|
|
18
|
+
private runSessions;
|
|
18
19
|
constructor(options: LoggingPluginOptions);
|
|
19
20
|
private log;
|
|
20
21
|
wrapRun(next: () => Promise<LLMBridge.Response>, context: LLMBridge.PluginCompletionContext): Promise<LLMBridge.Response>;
|
|
21
|
-
wrapExec(next: (params: any) => Promise<any>, params: any,
|
|
22
|
+
wrapExec(next: (params: any) => Promise<any>, params: any, context: LLMBridge.PluginCompletionContext): Promise<any>;
|
|
22
23
|
wrapToolExec(next: (tool: LLMBridge.Tool, counter: number, input: any) => Promise<any>, tool: LLMBridge.Tool, counter: number, input: any, context: LLMBridge.PluginCompletionContext): Promise<any>;
|
|
23
24
|
private writeJsonLog;
|
|
24
|
-
private
|
|
25
|
+
private writeSessionXml;
|
|
25
26
|
private logError;
|
|
26
27
|
private getDailyFolder;
|
|
27
|
-
private
|
|
28
|
+
private buildSessionXml;
|
|
28
29
|
private parseArguments;
|
|
29
30
|
private appendCdata;
|
|
30
31
|
private appendArguments;
|
|
32
|
+
private appendToolCalls;
|
|
31
33
|
private appendRawArguments;
|
|
32
34
|
private sanitizeAttribute;
|
|
33
35
|
private cleanXmlString;
|
package/dist/index.d.ts
CHANGED
|
@@ -15,19 +15,21 @@ declare function generateRandomDigits(length: number): string;
|
|
|
15
15
|
declare class LoggingPlugin implements LLMBridge.Plugin<void, string, void, void> {
|
|
16
16
|
private options;
|
|
17
17
|
private logger;
|
|
18
|
+
private runSessions;
|
|
18
19
|
constructor(options: LoggingPluginOptions);
|
|
19
20
|
private log;
|
|
20
21
|
wrapRun(next: () => Promise<LLMBridge.Response>, context: LLMBridge.PluginCompletionContext): Promise<LLMBridge.Response>;
|
|
21
|
-
wrapExec(next: (params: any) => Promise<any>, params: any,
|
|
22
|
+
wrapExec(next: (params: any) => Promise<any>, params: any, context: LLMBridge.PluginCompletionContext): Promise<any>;
|
|
22
23
|
wrapToolExec(next: (tool: LLMBridge.Tool, counter: number, input: any) => Promise<any>, tool: LLMBridge.Tool, counter: number, input: any, context: LLMBridge.PluginCompletionContext): Promise<any>;
|
|
23
24
|
private writeJsonLog;
|
|
24
|
-
private
|
|
25
|
+
private writeSessionXml;
|
|
25
26
|
private logError;
|
|
26
27
|
private getDailyFolder;
|
|
27
|
-
private
|
|
28
|
+
private buildSessionXml;
|
|
28
29
|
private parseArguments;
|
|
29
30
|
private appendCdata;
|
|
30
31
|
private appendArguments;
|
|
32
|
+
private appendToolCalls;
|
|
31
33
|
private appendRawArguments;
|
|
32
34
|
private sanitizeAttribute;
|
|
33
35
|
private cleanXmlString;
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ function generateRandomDigits(length) {
|
|
|
46
46
|
}
|
|
47
47
|
var LoggingPlugin = class {
|
|
48
48
|
constructor(options) {
|
|
49
|
+
this.runSessions = /* @__PURE__ */ new Map();
|
|
49
50
|
this.options = options;
|
|
50
51
|
this.logger = options.logger;
|
|
51
52
|
}
|
|
@@ -60,25 +61,54 @@ var LoggingPlugin = class {
|
|
|
60
61
|
}
|
|
61
62
|
async wrapRun(next, context) {
|
|
62
63
|
this.log(`run ${context.model}`);
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
const session = {
|
|
65
|
+
startTime: (0, import_moment.default)(),
|
|
66
|
+
requestId: generateRandomDigits(4),
|
|
67
|
+
model: context.model,
|
|
68
|
+
turns: []
|
|
69
|
+
};
|
|
70
|
+
this.runSessions.set(context, session);
|
|
71
|
+
try {
|
|
72
|
+
const response = await next();
|
|
73
|
+
const lastResponsePath = import_node_path.default.join(this.options.folder, "last_response.json");
|
|
74
|
+
import_node_fs.default.writeFileSync(lastResponsePath, JSON.stringify(response, null, 2));
|
|
75
|
+
this.writeSessionXml(session);
|
|
76
|
+
return response;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
this.writeSessionXml(session);
|
|
79
|
+
throw error;
|
|
80
|
+
} finally {
|
|
81
|
+
this.runSessions.delete(context);
|
|
82
|
+
}
|
|
67
83
|
}
|
|
68
|
-
async wrapExec(next, params,
|
|
84
|
+
async wrapExec(next, params, context) {
|
|
69
85
|
const requestId = generateRandomDigits(4);
|
|
70
86
|
const requestTime = (0, import_moment.default)();
|
|
87
|
+
const session = this.runSessions.get(context);
|
|
88
|
+
const turn = { request: params, response: void 0, toolExecutions: [] };
|
|
71
89
|
this.writeJsonLog(requestTime, requestId, "request", params);
|
|
72
|
-
|
|
90
|
+
if (session) {
|
|
91
|
+
session.turns.push(turn);
|
|
92
|
+
this.writeSessionXml(session);
|
|
93
|
+
}
|
|
73
94
|
const response = await next(params);
|
|
74
95
|
this.writeJsonLog((0, import_moment.default)(), requestId, "response", response);
|
|
75
|
-
|
|
96
|
+
if (session) {
|
|
97
|
+
turn.response = response;
|
|
98
|
+
this.writeSessionXml(session);
|
|
99
|
+
}
|
|
76
100
|
return response;
|
|
77
101
|
}
|
|
78
102
|
async wrapToolExec(next, tool, counter, input, context) {
|
|
79
103
|
this.log(`run (${counter + 1}/${context.options.tools?.usesLimit}) tool ${tool.name} ${JSON.stringify(input)}`);
|
|
80
104
|
const result = await next(tool, counter, input);
|
|
81
105
|
this.log(`tool response ${JSON.stringify(result)}`);
|
|
106
|
+
const session = this.runSessions.get(context);
|
|
107
|
+
if (session && session.turns.length > 0) {
|
|
108
|
+
const lastTurn = session.turns[session.turns.length - 1];
|
|
109
|
+
lastTurn.toolExecutions.push({ toolName: tool.name, counter, input, result });
|
|
110
|
+
this.writeSessionXml(session);
|
|
111
|
+
}
|
|
82
112
|
return result;
|
|
83
113
|
}
|
|
84
114
|
writeJsonLog(date, requestId, type, data) {
|
|
@@ -87,16 +117,16 @@ var LoggingPlugin = class {
|
|
|
87
117
|
const filePath = import_node_path.default.join(dailyDir, fileName);
|
|
88
118
|
import_node_fs.default.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
89
119
|
}
|
|
90
|
-
|
|
120
|
+
writeSessionXml(session) {
|
|
91
121
|
try {
|
|
92
|
-
const { dailyDir, timeString } = this.getDailyFolder(
|
|
93
|
-
const xmlFileName = `${timeString}_${requestId}.xml`;
|
|
122
|
+
const { dailyDir, timeString } = this.getDailyFolder(session.startTime);
|
|
123
|
+
const xmlFileName = `${timeString}_${session.requestId}.session.xml`;
|
|
94
124
|
const xmlPath = import_node_path.default.join(dailyDir, xmlFileName);
|
|
95
|
-
const xml = this.
|
|
125
|
+
const xml = this.buildSessionXml(session);
|
|
96
126
|
import_node_fs.default.writeFileSync(xmlPath, xml);
|
|
97
127
|
} catch (error) {
|
|
98
128
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
99
|
-
this.logError(`[LLM] Error saving XML log: ${errorMessage}`, error);
|
|
129
|
+
this.logError(`[LLM] Error saving session XML log: ${errorMessage}`, error);
|
|
100
130
|
}
|
|
101
131
|
}
|
|
102
132
|
logError(message, error) {
|
|
@@ -120,81 +150,69 @@ ${error.stack}` : "";
|
|
|
120
150
|
}
|
|
121
151
|
return { dailyDir, timeString };
|
|
122
152
|
}
|
|
123
|
-
|
|
124
|
-
const root = (0, import_xmlbuilder2.create)({ version: "1.0", encoding: "UTF-8" }).ele("
|
|
153
|
+
buildSessionXml(session) {
|
|
154
|
+
const root = (0, import_xmlbuilder2.create)({ version: "1.0", encoding: "UTF-8" }).ele("session", { model: this.sanitizeAttribute(session.model) });
|
|
125
155
|
let toolsUsed = false;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
messageElem.att("tool_call_id", this.sanitizeAttribute(message.tool_call_id));
|
|
135
|
-
}
|
|
136
|
-
const content = message.content;
|
|
137
|
-
if (typeof content === "string") {
|
|
138
|
-
this.appendCdata(messageElem, content);
|
|
139
|
-
} else if (Array.isArray(content)) {
|
|
140
|
-
const textParts = content.filter((part) => part && typeof part === "object" && part.type === "text").map((part) => part.text ?? "").filter((text) => text.length > 0);
|
|
141
|
-
if (textParts.length > 0) {
|
|
142
|
-
this.appendCdata(messageElem, textParts.join("\n"));
|
|
156
|
+
for (let i = 0; i < session.turns.length; i++) {
|
|
157
|
+
const turn = session.turns[i];
|
|
158
|
+
const turnElem = root.ele("turn", { number: String(i + 1) });
|
|
159
|
+
const requestElem = turnElem.ele("request");
|
|
160
|
+
const messages = Array.isArray(turn.request?.messages) ? turn.request.messages : [];
|
|
161
|
+
for (const message of messages) {
|
|
162
|
+
if (!message || typeof message !== "object") {
|
|
163
|
+
continue;
|
|
143
164
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
const functionData = toolCall.function ?? {};
|
|
157
|
-
if (functionData.name) {
|
|
158
|
-
toolElem.att("function_name", this.sanitizeAttribute(functionData.name));
|
|
159
|
-
}
|
|
160
|
-
const args = this.parseArguments(functionData.arguments);
|
|
161
|
-
if (args && typeof args === "object") {
|
|
162
|
-
this.appendArguments(toolElem, args);
|
|
163
|
-
} else if (functionData.arguments !== void 0) {
|
|
164
|
-
this.appendRawArguments(toolElem, functionData.arguments);
|
|
165
|
+
const messageElem = requestElem.ele("message", { role: this.sanitizeAttribute(message.role) });
|
|
166
|
+
if (message.role === "tool" && message.tool_call_id !== void 0) {
|
|
167
|
+
messageElem.att("tool_call_id", this.sanitizeAttribute(message.tool_call_id));
|
|
168
|
+
}
|
|
169
|
+
const content = message.content;
|
|
170
|
+
if (typeof content === "string") {
|
|
171
|
+
this.appendCdata(messageElem, content);
|
|
172
|
+
} else if (Array.isArray(content)) {
|
|
173
|
+
const textParts = content.filter((part) => part && typeof part === "object" && part.type === "text").map((part) => part.text ?? "").filter((text) => text.length > 0);
|
|
174
|
+
if (textParts.length > 0) {
|
|
175
|
+
this.appendCdata(messageElem, textParts.join("\n"));
|
|
165
176
|
}
|
|
166
177
|
}
|
|
178
|
+
if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
179
|
+
toolsUsed = true;
|
|
180
|
+
this.appendToolCalls(messageElem, message.tool_calls);
|
|
181
|
+
}
|
|
167
182
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
if (turn.response) {
|
|
184
|
+
const responseElem = turnElem.ele("response");
|
|
185
|
+
const responseMessageElem = responseElem.ele("message", { role: "assistant" });
|
|
186
|
+
const responseMessage = turn.response?.choices?.[0]?.message;
|
|
187
|
+
const responseContent = responseMessage?.content;
|
|
188
|
+
if (typeof responseContent === "string") {
|
|
189
|
+
this.appendCdata(responseMessageElem, responseContent);
|
|
190
|
+
}
|
|
191
|
+
const responseToolCalls = responseMessage?.tool_calls;
|
|
192
|
+
if (Array.isArray(responseToolCalls) && responseToolCalls.length > 0) {
|
|
193
|
+
toolsUsed = true;
|
|
194
|
+
this.appendToolCalls(responseMessageElem, responseToolCalls);
|
|
195
|
+
}
|
|
176
196
|
}
|
|
177
|
-
|
|
178
|
-
if (Array.isArray(responseToolCalls) && responseToolCalls.length > 0) {
|
|
197
|
+
if (turn.toolExecutions.length > 0) {
|
|
179
198
|
toolsUsed = true;
|
|
180
|
-
const
|
|
181
|
-
for (const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.appendArguments(toolElem, args);
|
|
195
|
-
} else if (functionData?.arguments !== void 0) {
|
|
196
|
-
this.appendRawArguments(toolElem, functionData.arguments);
|
|
199
|
+
const toolExecsElem = turnElem.ele("tool_executions");
|
|
200
|
+
for (const exec of turn.toolExecutions) {
|
|
201
|
+
const execElem = toolExecsElem.ele("tool_execution", {
|
|
202
|
+
name: this.sanitizeAttribute(exec.toolName),
|
|
203
|
+
counter: String(exec.counter)
|
|
204
|
+
});
|
|
205
|
+
const inputElem = execElem.ele("input");
|
|
206
|
+
if (exec.input && typeof exec.input === "object" && !Array.isArray(exec.input)) {
|
|
207
|
+
for (const [key, value] of Object.entries(exec.input)) {
|
|
208
|
+
const argElem = inputElem.ele("arg", { name: this.sanitizeAttribute(key) });
|
|
209
|
+
this.appendCdata(argElem, value);
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
this.appendCdata(inputElem, typeof exec.input === "object" ? JSON.stringify(exec.input) : exec.input);
|
|
197
213
|
}
|
|
214
|
+
const outputElem = execElem.ele("output");
|
|
215
|
+
this.appendCdata(outputElem, typeof exec.result === "object" ? JSON.stringify(exec.result) : exec.result);
|
|
198
216
|
}
|
|
199
217
|
}
|
|
200
218
|
}
|
|
@@ -239,6 +257,30 @@ ${error.stack}` : "";
|
|
|
239
257
|
this.appendCdata(argElem, value);
|
|
240
258
|
}
|
|
241
259
|
}
|
|
260
|
+
appendToolCalls(parentElem, toolCalls) {
|
|
261
|
+
const toolCallsElem = parentElem.ele("tool_calls");
|
|
262
|
+
for (const toolCall of toolCalls) {
|
|
263
|
+
if (!toolCall || typeof toolCall !== "object") {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const functionData = toolCall.function ?? {};
|
|
267
|
+
const functionName = functionData.name;
|
|
268
|
+
if (!functionName || functionName === "null") {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const toolElem = toolCallsElem.ele("tool_call");
|
|
272
|
+
if (toolCall.id) {
|
|
273
|
+
toolElem.att("id", this.sanitizeAttribute(toolCall.id));
|
|
274
|
+
}
|
|
275
|
+
toolElem.att("function_name", this.sanitizeAttribute(functionName));
|
|
276
|
+
const args = this.parseArguments(functionData.arguments);
|
|
277
|
+
if (args && typeof args === "object") {
|
|
278
|
+
this.appendArguments(toolElem, args);
|
|
279
|
+
} else if (functionData.arguments !== void 0) {
|
|
280
|
+
this.appendRawArguments(toolElem, functionData.arguments);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
242
284
|
appendRawArguments(toolElem, argumentsValue) {
|
|
243
285
|
const argsElem = toolElem.ele("arguments");
|
|
244
286
|
this.appendCdata(argsElem, argumentsValue);
|
package/dist/index.mjs
CHANGED
|
@@ -9,6 +9,7 @@ function generateRandomDigits(length) {
|
|
|
9
9
|
}
|
|
10
10
|
var LoggingPlugin = class {
|
|
11
11
|
constructor(options) {
|
|
12
|
+
this.runSessions = /* @__PURE__ */ new Map();
|
|
12
13
|
this.options = options;
|
|
13
14
|
this.logger = options.logger;
|
|
14
15
|
}
|
|
@@ -23,25 +24,54 @@ var LoggingPlugin = class {
|
|
|
23
24
|
}
|
|
24
25
|
async wrapRun(next, context) {
|
|
25
26
|
this.log(`run ${context.model}`);
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const session = {
|
|
28
|
+
startTime: moment(),
|
|
29
|
+
requestId: generateRandomDigits(4),
|
|
30
|
+
model: context.model,
|
|
31
|
+
turns: []
|
|
32
|
+
};
|
|
33
|
+
this.runSessions.set(context, session);
|
|
34
|
+
try {
|
|
35
|
+
const response = await next();
|
|
36
|
+
const lastResponsePath = path.join(this.options.folder, "last_response.json");
|
|
37
|
+
fs.writeFileSync(lastResponsePath, JSON.stringify(response, null, 2));
|
|
38
|
+
this.writeSessionXml(session);
|
|
39
|
+
return response;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
this.writeSessionXml(session);
|
|
42
|
+
throw error;
|
|
43
|
+
} finally {
|
|
44
|
+
this.runSessions.delete(context);
|
|
45
|
+
}
|
|
30
46
|
}
|
|
31
|
-
async wrapExec(next, params,
|
|
47
|
+
async wrapExec(next, params, context) {
|
|
32
48
|
const requestId = generateRandomDigits(4);
|
|
33
49
|
const requestTime = moment();
|
|
50
|
+
const session = this.runSessions.get(context);
|
|
51
|
+
const turn = { request: params, response: void 0, toolExecutions: [] };
|
|
34
52
|
this.writeJsonLog(requestTime, requestId, "request", params);
|
|
35
|
-
|
|
53
|
+
if (session) {
|
|
54
|
+
session.turns.push(turn);
|
|
55
|
+
this.writeSessionXml(session);
|
|
56
|
+
}
|
|
36
57
|
const response = await next(params);
|
|
37
58
|
this.writeJsonLog(moment(), requestId, "response", response);
|
|
38
|
-
|
|
59
|
+
if (session) {
|
|
60
|
+
turn.response = response;
|
|
61
|
+
this.writeSessionXml(session);
|
|
62
|
+
}
|
|
39
63
|
return response;
|
|
40
64
|
}
|
|
41
65
|
async wrapToolExec(next, tool, counter, input, context) {
|
|
42
66
|
this.log(`run (${counter + 1}/${context.options.tools?.usesLimit}) tool ${tool.name} ${JSON.stringify(input)}`);
|
|
43
67
|
const result = await next(tool, counter, input);
|
|
44
68
|
this.log(`tool response ${JSON.stringify(result)}`);
|
|
69
|
+
const session = this.runSessions.get(context);
|
|
70
|
+
if (session && session.turns.length > 0) {
|
|
71
|
+
const lastTurn = session.turns[session.turns.length - 1];
|
|
72
|
+
lastTurn.toolExecutions.push({ toolName: tool.name, counter, input, result });
|
|
73
|
+
this.writeSessionXml(session);
|
|
74
|
+
}
|
|
45
75
|
return result;
|
|
46
76
|
}
|
|
47
77
|
writeJsonLog(date, requestId, type, data) {
|
|
@@ -50,16 +80,16 @@ var LoggingPlugin = class {
|
|
|
50
80
|
const filePath = path.join(dailyDir, fileName);
|
|
51
81
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
52
82
|
}
|
|
53
|
-
|
|
83
|
+
writeSessionXml(session) {
|
|
54
84
|
try {
|
|
55
|
-
const { dailyDir, timeString } = this.getDailyFolder(
|
|
56
|
-
const xmlFileName = `${timeString}_${requestId}.xml`;
|
|
85
|
+
const { dailyDir, timeString } = this.getDailyFolder(session.startTime);
|
|
86
|
+
const xmlFileName = `${timeString}_${session.requestId}.session.xml`;
|
|
57
87
|
const xmlPath = path.join(dailyDir, xmlFileName);
|
|
58
|
-
const xml = this.
|
|
88
|
+
const xml = this.buildSessionXml(session);
|
|
59
89
|
fs.writeFileSync(xmlPath, xml);
|
|
60
90
|
} catch (error) {
|
|
61
91
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
62
|
-
this.logError(`[LLM] Error saving XML log: ${errorMessage}`, error);
|
|
92
|
+
this.logError(`[LLM] Error saving session XML log: ${errorMessage}`, error);
|
|
63
93
|
}
|
|
64
94
|
}
|
|
65
95
|
logError(message, error) {
|
|
@@ -83,81 +113,69 @@ ${error.stack}` : "";
|
|
|
83
113
|
}
|
|
84
114
|
return { dailyDir, timeString };
|
|
85
115
|
}
|
|
86
|
-
|
|
87
|
-
const root = create({ version: "1.0", encoding: "UTF-8" }).ele("
|
|
116
|
+
buildSessionXml(session) {
|
|
117
|
+
const root = create({ version: "1.0", encoding: "UTF-8" }).ele("session", { model: this.sanitizeAttribute(session.model) });
|
|
88
118
|
let toolsUsed = false;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
messageElem.att("tool_call_id", this.sanitizeAttribute(message.tool_call_id));
|
|
98
|
-
}
|
|
99
|
-
const content = message.content;
|
|
100
|
-
if (typeof content === "string") {
|
|
101
|
-
this.appendCdata(messageElem, content);
|
|
102
|
-
} else if (Array.isArray(content)) {
|
|
103
|
-
const textParts = content.filter((part) => part && typeof part === "object" && part.type === "text").map((part) => part.text ?? "").filter((text) => text.length > 0);
|
|
104
|
-
if (textParts.length > 0) {
|
|
105
|
-
this.appendCdata(messageElem, textParts.join("\n"));
|
|
119
|
+
for (let i = 0; i < session.turns.length; i++) {
|
|
120
|
+
const turn = session.turns[i];
|
|
121
|
+
const turnElem = root.ele("turn", { number: String(i + 1) });
|
|
122
|
+
const requestElem = turnElem.ele("request");
|
|
123
|
+
const messages = Array.isArray(turn.request?.messages) ? turn.request.messages : [];
|
|
124
|
+
for (const message of messages) {
|
|
125
|
+
if (!message || typeof message !== "object") {
|
|
126
|
+
continue;
|
|
106
127
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
const functionData = toolCall.function ?? {};
|
|
120
|
-
if (functionData.name) {
|
|
121
|
-
toolElem.att("function_name", this.sanitizeAttribute(functionData.name));
|
|
122
|
-
}
|
|
123
|
-
const args = this.parseArguments(functionData.arguments);
|
|
124
|
-
if (args && typeof args === "object") {
|
|
125
|
-
this.appendArguments(toolElem, args);
|
|
126
|
-
} else if (functionData.arguments !== void 0) {
|
|
127
|
-
this.appendRawArguments(toolElem, functionData.arguments);
|
|
128
|
+
const messageElem = requestElem.ele("message", { role: this.sanitizeAttribute(message.role) });
|
|
129
|
+
if (message.role === "tool" && message.tool_call_id !== void 0) {
|
|
130
|
+
messageElem.att("tool_call_id", this.sanitizeAttribute(message.tool_call_id));
|
|
131
|
+
}
|
|
132
|
+
const content = message.content;
|
|
133
|
+
if (typeof content === "string") {
|
|
134
|
+
this.appendCdata(messageElem, content);
|
|
135
|
+
} else if (Array.isArray(content)) {
|
|
136
|
+
const textParts = content.filter((part) => part && typeof part === "object" && part.type === "text").map((part) => part.text ?? "").filter((text) => text.length > 0);
|
|
137
|
+
if (textParts.length > 0) {
|
|
138
|
+
this.appendCdata(messageElem, textParts.join("\n"));
|
|
128
139
|
}
|
|
129
140
|
}
|
|
141
|
+
if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
142
|
+
toolsUsed = true;
|
|
143
|
+
this.appendToolCalls(messageElem, message.tool_calls);
|
|
144
|
+
}
|
|
130
145
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
if (turn.response) {
|
|
147
|
+
const responseElem = turnElem.ele("response");
|
|
148
|
+
const responseMessageElem = responseElem.ele("message", { role: "assistant" });
|
|
149
|
+
const responseMessage = turn.response?.choices?.[0]?.message;
|
|
150
|
+
const responseContent = responseMessage?.content;
|
|
151
|
+
if (typeof responseContent === "string") {
|
|
152
|
+
this.appendCdata(responseMessageElem, responseContent);
|
|
153
|
+
}
|
|
154
|
+
const responseToolCalls = responseMessage?.tool_calls;
|
|
155
|
+
if (Array.isArray(responseToolCalls) && responseToolCalls.length > 0) {
|
|
156
|
+
toolsUsed = true;
|
|
157
|
+
this.appendToolCalls(responseMessageElem, responseToolCalls);
|
|
158
|
+
}
|
|
139
159
|
}
|
|
140
|
-
|
|
141
|
-
if (Array.isArray(responseToolCalls) && responseToolCalls.length > 0) {
|
|
160
|
+
if (turn.toolExecutions.length > 0) {
|
|
142
161
|
toolsUsed = true;
|
|
143
|
-
const
|
|
144
|
-
for (const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
this.appendArguments(toolElem, args);
|
|
158
|
-
} else if (functionData?.arguments !== void 0) {
|
|
159
|
-
this.appendRawArguments(toolElem, functionData.arguments);
|
|
162
|
+
const toolExecsElem = turnElem.ele("tool_executions");
|
|
163
|
+
for (const exec of turn.toolExecutions) {
|
|
164
|
+
const execElem = toolExecsElem.ele("tool_execution", {
|
|
165
|
+
name: this.sanitizeAttribute(exec.toolName),
|
|
166
|
+
counter: String(exec.counter)
|
|
167
|
+
});
|
|
168
|
+
const inputElem = execElem.ele("input");
|
|
169
|
+
if (exec.input && typeof exec.input === "object" && !Array.isArray(exec.input)) {
|
|
170
|
+
for (const [key, value] of Object.entries(exec.input)) {
|
|
171
|
+
const argElem = inputElem.ele("arg", { name: this.sanitizeAttribute(key) });
|
|
172
|
+
this.appendCdata(argElem, value);
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
this.appendCdata(inputElem, typeof exec.input === "object" ? JSON.stringify(exec.input) : exec.input);
|
|
160
176
|
}
|
|
177
|
+
const outputElem = execElem.ele("output");
|
|
178
|
+
this.appendCdata(outputElem, typeof exec.result === "object" ? JSON.stringify(exec.result) : exec.result);
|
|
161
179
|
}
|
|
162
180
|
}
|
|
163
181
|
}
|
|
@@ -202,6 +220,30 @@ ${error.stack}` : "";
|
|
|
202
220
|
this.appendCdata(argElem, value);
|
|
203
221
|
}
|
|
204
222
|
}
|
|
223
|
+
appendToolCalls(parentElem, toolCalls) {
|
|
224
|
+
const toolCallsElem = parentElem.ele("tool_calls");
|
|
225
|
+
for (const toolCall of toolCalls) {
|
|
226
|
+
if (!toolCall || typeof toolCall !== "object") {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const functionData = toolCall.function ?? {};
|
|
230
|
+
const functionName = functionData.name;
|
|
231
|
+
if (!functionName || functionName === "null") {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const toolElem = toolCallsElem.ele("tool_call");
|
|
235
|
+
if (toolCall.id) {
|
|
236
|
+
toolElem.att("id", this.sanitizeAttribute(toolCall.id));
|
|
237
|
+
}
|
|
238
|
+
toolElem.att("function_name", this.sanitizeAttribute(functionName));
|
|
239
|
+
const args = this.parseArguments(functionData.arguments);
|
|
240
|
+
if (args && typeof args === "object") {
|
|
241
|
+
this.appendArguments(toolElem, args);
|
|
242
|
+
} else if (functionData.arguments !== void 0) {
|
|
243
|
+
this.appendRawArguments(toolElem, functionData.arguments);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
205
247
|
appendRawArguments(toolElem, argumentsValue) {
|
|
206
248
|
const argsElem = toolElem.ele("arguments");
|
|
207
249
|
this.appendCdata(argsElem, argumentsValue);
|