@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 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, _context: LLMBridge.PluginCompletionContext): Promise<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 writeXmlLog;
25
+ private writeSessionXml;
25
26
  private logError;
26
27
  private getDailyFolder;
27
- private buildXml;
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, _context: LLMBridge.PluginCompletionContext): Promise<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 writeXmlLog;
25
+ private writeSessionXml;
25
26
  private logError;
26
27
  private getDailyFolder;
27
- private buildXml;
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 response = await next();
64
- const lastResponsePath = import_node_path.default.join(this.options.folder, "last_response.json");
65
- import_node_fs.default.writeFileSync(lastResponsePath, JSON.stringify(response, null, 2));
66
- return response;
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, _context) {
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
- this.writeXmlLog(requestTime, requestId, params, void 0);
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
- this.writeXmlLog(requestTime, requestId, params, response);
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
- writeXmlLog(date, requestId, params, response) {
120
+ writeSessionXml(session) {
91
121
  try {
92
- const { dailyDir, timeString } = this.getDailyFolder(date);
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.buildXml(params, response);
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
- buildXml(params, response) {
124
- const root = (0, import_xmlbuilder2.create)({ version: "1.0", encoding: "UTF-8" }).ele("root");
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
- const requestElem = root.ele("request");
127
- const messages = Array.isArray(params?.messages) ? params.messages : [];
128
- for (const message of messages) {
129
- if (!message || typeof message !== "object") {
130
- continue;
131
- }
132
- const messageElem = requestElem.ele("message", { role: this.sanitizeAttribute(message.role) });
133
- if (message.role === "tool" && message.tool_call_id !== void 0) {
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
- if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
146
- toolsUsed = true;
147
- const toolCallsElem = messageElem.ele("tool_calls");
148
- for (const toolCall of message.tool_calls) {
149
- if (!toolCall || typeof toolCall !== "object") {
150
- continue;
151
- }
152
- const toolElem = toolCallsElem.ele("tool_call");
153
- if (toolCall.id) {
154
- toolElem.att("id", this.sanitizeAttribute(toolCall.id));
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
- if (response) {
170
- const responseElem = root.ele("response");
171
- const responseMessageElem = responseElem.ele("message", { role: "assistant" });
172
- const responseMessage = response?.choices?.[0]?.message;
173
- const responseContent = responseMessage?.content;
174
- if (typeof responseContent === "string") {
175
- this.appendCdata(responseMessageElem, responseContent);
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
- const responseToolCalls = responseMessage?.tool_calls;
178
- if (Array.isArray(responseToolCalls) && responseToolCalls.length > 0) {
197
+ if (turn.toolExecutions.length > 0) {
179
198
  toolsUsed = true;
180
- const toolCallsElem = responseMessageElem.ele("tool_calls");
181
- for (const toolCall of responseToolCalls) {
182
- const functionData = toolCall?.function;
183
- const functionName = functionData?.name;
184
- if (!functionName || functionName === "null") {
185
- continue;
186
- }
187
- const toolElem = toolCallsElem.ele("tool_call");
188
- if (toolCall?.id) {
189
- toolElem.att("id", this.sanitizeAttribute(toolCall.id));
190
- }
191
- toolElem.att("function_name", this.sanitizeAttribute(functionName));
192
- const args = this.parseArguments(functionData?.arguments);
193
- if (args && typeof args === "object") {
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 response = await next();
27
- const lastResponsePath = path.join(this.options.folder, "last_response.json");
28
- fs.writeFileSync(lastResponsePath, JSON.stringify(response, null, 2));
29
- return response;
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, _context) {
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
- this.writeXmlLog(requestTime, requestId, params, void 0);
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
- this.writeXmlLog(requestTime, requestId, params, response);
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
- writeXmlLog(date, requestId, params, response) {
83
+ writeSessionXml(session) {
54
84
  try {
55
- const { dailyDir, timeString } = this.getDailyFolder(date);
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.buildXml(params, response);
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
- buildXml(params, response) {
87
- const root = create({ version: "1.0", encoding: "UTF-8" }).ele("root");
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
- const requestElem = root.ele("request");
90
- const messages = Array.isArray(params?.messages) ? params.messages : [];
91
- for (const message of messages) {
92
- if (!message || typeof message !== "object") {
93
- continue;
94
- }
95
- const messageElem = requestElem.ele("message", { role: this.sanitizeAttribute(message.role) });
96
- if (message.role === "tool" && message.tool_call_id !== void 0) {
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
- if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
109
- toolsUsed = true;
110
- const toolCallsElem = messageElem.ele("tool_calls");
111
- for (const toolCall of message.tool_calls) {
112
- if (!toolCall || typeof toolCall !== "object") {
113
- continue;
114
- }
115
- const toolElem = toolCallsElem.ele("tool_call");
116
- if (toolCall.id) {
117
- toolElem.att("id", this.sanitizeAttribute(toolCall.id));
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
- if (response) {
133
- const responseElem = root.ele("response");
134
- const responseMessageElem = responseElem.ele("message", { role: "assistant" });
135
- const responseMessage = response?.choices?.[0]?.message;
136
- const responseContent = responseMessage?.content;
137
- if (typeof responseContent === "string") {
138
- this.appendCdata(responseMessageElem, responseContent);
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
- const responseToolCalls = responseMessage?.tool_calls;
141
- if (Array.isArray(responseToolCalls) && responseToolCalls.length > 0) {
160
+ if (turn.toolExecutions.length > 0) {
142
161
  toolsUsed = true;
143
- const toolCallsElem = responseMessageElem.ele("tool_calls");
144
- for (const toolCall of responseToolCalls) {
145
- const functionData = toolCall?.function;
146
- const functionName = functionData?.name;
147
- if (!functionName || functionName === "null") {
148
- continue;
149
- }
150
- const toolElem = toolCallsElem.ele("tool_call");
151
- if (toolCall?.id) {
152
- toolElem.att("id", this.sanitizeAttribute(toolCall.id));
153
- }
154
- toolElem.att("function_name", this.sanitizeAttribute(functionName));
155
- const args = this.parseArguments(functionData?.arguments);
156
- if (args && typeof args === "object") {
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmbridge/plugin-logging",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Logging plugin for LLMBridge",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",