@llmbridge/plugin-logging 0.1.2 → 0.1.4

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