Skip to content

Commit 0a7697d

Browse files
amishnedevversion
authored andcommitted
feat: add tool logs
1 parent 77fba75 commit 0a7697d

File tree

12 files changed

+182
-28
lines changed

12 files changed

+182
-28
lines changed

pnpm-lock.yaml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

report-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@shikijs/themes": "3.13.0",
2424
"express": "^4.18.2",
2525
"jszip": "^3.10.1",
26+
"ngx-json-viewer": "^3.2.1",
2627
"rxjs": "~7.8.0",
2728
"shiki": "^3.6.0",
2829
"tinyglobby": "^0.2.14",

report-app/src/app/pages/report-viewer/report-viewer.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,32 @@ <h4>Debugging Tools</h4>
411411
(click)="downloadDebuggingZip(result)">
412412
Download ZIP for debugging
413413
</button>
414+
@if (result.toolLogs.length > 0) {
415+
<expansion-panel>
416+
<expansion-panel-header>Tool Logs</expansion-panel-header>
417+
<ul class="tool-logs-list">
418+
@for (log of result.toolLogs; track $index) {
419+
<li>
420+
<details class="details mcp-log-entry">
421+
@let name = log.request.name;
422+
<summary>
423+
Log Entry #{{ $index + 1
424+
}}{{ name ? ' - ' + name : '' }}
425+
</summary>
426+
<div class="mcp-log-content">
427+
<h5>Request</h5>
428+
<ngx-json-viewer [json]="log.request" [expanded]="false"></ngx-json-viewer>
429+
<h5>Response</h5>
430+
<ngx-json-viewer [json]="log.response" [expanded]="false"></ngx-json-viewer>
431+
</div>
432+
</details>
433+
</li>
434+
} @empty {
435+
<li>No MCP logs were recorded for this run.</li>
436+
}
437+
</ul>
438+
</expansion-panel>
439+
}
414440
</div>
415441

416442
@if (finalBuild.runtimeErrors) {

report-app/src/app/pages/report-viewer/report-viewer.scss

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ expansion-panel {
6363
padding: 0 1rem 1rem;
6464
}
6565

66-
.app-details-section expansion-panel {
66+
.app-details-section expansion-panel, .app-details-section button {
6767
margin-bottom: 0.5rem;
6868
}
6969

@@ -233,3 +233,32 @@ expansion-panel {
233233
padding: 0px 20px;
234234
}
235235

236+
.mcp-log-entry {
237+
border: 1px solid var(--border-color);
238+
border-radius: var(--border-radius);
239+
margin: 1rem 0;
240+
241+
& > summary {
242+
cursor: pointer;
243+
font-weight: 500;
244+
padding: 1rem 1.5rem;
245+
246+
&:hover {
247+
background-color: var(--button-active-bg-color);
248+
}
249+
}
250+
251+
& .mcp-log-content {
252+
padding: 0 1.5rem 1.5rem;
253+
254+
h5 {
255+
margin-top: 1.5rem;
256+
margin-bottom: 0.5rem;
257+
}
258+
}
259+
}
260+
261+
.tool-logs-list {
262+
list-style: none;
263+
padding: 0;
264+
}

report-app/src/app/pages/report-viewer/report-viewer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
signal,
1212
viewChild,
1313
} from '@angular/core';
14+
import { NgxJsonViewerModule } from 'ngx-json-viewer';
1415
import { BuildErrorType } from '../../../../../runner/builder/builder-types';
1516
import {
1617
AssessmentResult,
@@ -55,6 +56,7 @@ import { ProviderLabel } from '../../shared/provider-label';
5556
ExpansionPanel,
5657
ExpansionPanelHeader,
5758
ProviderLabel,
59+
NgxJsonViewerModule,
5860
],
5961
templateUrl: './report-viewer.html',
6062
styleUrls: ['./report-viewer.scss'],

runner/codegen/gemini-cli/gemini-cli-runner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export class GeminiCliRunner implements LlmRunner {
8989
});
9090
}
9191

92-
return { files, reasoning };
92+
return { files, reasoning, toolLogs: [] };
9393
}
9494

9595
generateText(): Promise<LlmGenerateTextResponse> {

runner/codegen/genkit/genkit-runner.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
DynamicResourceAction,
3+
GenerateResponse,
34
genkit,
45
ModelReference,
56
ToolAction,
@@ -27,6 +28,7 @@ import {
2728
GenkitModelProvider,
2829
PromptDataForCounting,
2930
} from './model-provider.js';
31+
import { ToolLogEntry } from '../../shared-interfaces.js';
3032

3133
const globalLogger = new GenkitLogger();
3234
logger.init(globalLogger);
@@ -38,6 +40,7 @@ export class GenkitRunner implements LlmRunner {
3840
readonly hasBuiltInRepairLoop = false;
3941
private readonly genkitInstance = this.getGenkitInstance();
4042
private mcpHost: GenkitMcpHost | null = null;
43+
private toolLogs: ToolLogEntry[] = [];
4144

4245
async generateConstrained<T extends z.ZodTypeAny = z.ZodTypeAny>(
4346
options: LlmConstrainedOutputGenerateRequestOptions<T>
@@ -75,9 +78,14 @@ export class GenkitRunner implements LlmRunner {
7578
files: result.output.outputFiles || [],
7679
usage: result.usage,
7780
reasoning: result.reasoning,
81+
toolLogs: this.flushToolLogs(),
7882
};
7983
}
8084

85+
flushToolLogs(): ToolLogEntry[] {
86+
return this.toolLogs.splice(0);
87+
}
88+
8189
async generateText(
8290
options: LlmGenerateTextRequestOptions
8391
): Promise<LlmGenerateTextResponse> {
@@ -87,6 +95,7 @@ export class GenkitRunner implements LlmRunner {
8795
text: result.text,
8896
usage: result.usage,
8997
reasoning: result.reasoning,
98+
toolLogs: this.flushToolLogs(),
9099
};
91100
}
92101

@@ -120,7 +129,7 @@ export class GenkitRunner implements LlmRunner {
120129
]);
121130
}
122131

123-
return this.genkitInstance.generate({
132+
const response = await this.genkitInstance.generate({
124133
prompt: options.prompt,
125134
model,
126135
output: schema
@@ -145,6 +154,10 @@ export class GenkitRunner implements LlmRunner {
145154
resources,
146155
abortSignal: options.abortSignal,
147156
});
157+
158+
this._logToolUsage(response);
159+
160+
return response;
148161
};
149162

150163
return options.timeout
@@ -158,6 +171,42 @@ export class GenkitRunner implements LlmRunner {
158171
);
159172
}
160173

174+
private _logToolUsage(response: GenerateResponse<any>) {
175+
const toolRequests = new Map<string, any>();
176+
const toolResponses = new Map<string, any>();
177+
178+
if (response.request?.messages) {
179+
for (const message of response.request.messages) {
180+
if (!message.content) {
181+
continue;
182+
}
183+
for (const contentPart of message.content) {
184+
if (contentPart.toolRequest) {
185+
toolRequests.set(
186+
contentPart.toolRequest.ref || '0',
187+
contentPart.toolRequest
188+
);
189+
} else if (contentPart.toolResponse) {
190+
toolResponses.set(
191+
contentPart.toolResponse.ref || '0',
192+
contentPart.toolResponse
193+
);
194+
}
195+
}
196+
}
197+
}
198+
199+
for (const [ref, toolRequest] of toolRequests.entries()) {
200+
const toolResponse = toolResponses.get(ref);
201+
if (toolResponse) {
202+
this.toolLogs.push({
203+
request: toolRequest,
204+
response: toolResponse,
205+
});
206+
}
207+
}
208+
}
209+
161210
startMcpServerHost(hostName: string, servers: McpServerOptions[]): void {
162211
if (this.mcpHost !== null) {
163212
throw new Error('MCP host is already started');

runner/codegen/llm-runner.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { z } from 'zod';
2-
import { LlmResponseFile, Usage } from '../shared-interfaces.js';
2+
import { LlmResponseFile, ToolLogEntry, Usage } from '../shared-interfaces.js';
33
import { UserFacingError } from '../utils/errors.js';
44

55
export function assertValidModelName(value: string, availableModels: string[]) {
@@ -141,22 +141,24 @@ export interface LlmConstrainedOutputGenerateResponse<
141141
reasoning: string;
142142
}
143143

144-
/** File generation response from the LLM. */
145-
export interface LlmGenerateFilesResponse {
146-
files: LlmResponseFile[];
144+
/** LLM response. */
145+
interface BaseLlmGenerateResponse {
147146
/** Token usage data, if available. */
148147
usage?: Partial<Usage>;
149148
/** Reasoning messages from the LLM. */
150149
reasoning: string;
150+
/** Tool requests and responses. */
151+
toolLogs: ToolLogEntry[];
152+
}
153+
154+
/** File generation response from the LLM. */
155+
export interface LlmGenerateFilesResponse extends BaseLlmGenerateResponse {
156+
files: LlmResponseFile[];
151157
}
152158

153159
/** Text response from the LLM. */
154-
export interface LlmGenerateTextResponse {
160+
export interface LlmGenerateTextResponse extends BaseLlmGenerateResponse {
155161
text: string;
156-
/** Token usage data, if available. */
157-
usage?: Partial<Usage>;
158-
/** Reasoning messages from the LLM. */
159-
reasoning: string;
160162
}
161163

162164
/** Schema for the LLM server options. */

runner/orchestration/build.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
BuildWorkerMessage,
55
RepairType,
66
} from '../builder/builder-types.js';
7-
import { LlmRunner } from '../codegen/llm-runner.js';
7+
import { LlmGenerateFilesResponse, LlmRunner } from '../codegen/llm-runner.js';
88
import { Environment } from '../configuration/environment.js';
99
import {
1010
AttemptDetails,
@@ -44,11 +44,7 @@ export async function attemptBuild(
4444
rootPromptDef: RootPromptDefinition,
4545
directory: string,
4646
contextFiles: LlmContextFile[],
47-
initialResponse: {
48-
usage: Usage;
49-
outputFiles: LlmResponseFile[];
50-
reasoning: string;
51-
},
47+
initialResponse: LlmGenerateFilesResponse,
5248
attemptDetails: AttemptDetails[],
5349
skipScreenshots: boolean,
5450
skipAxeTesting: boolean,
@@ -72,7 +68,7 @@ export async function attemptBuild(
7268

7369
// Clone the original files, because we're going to mutate them between repair
7470
// attempts and we don't want the different runs to influence each other.
75-
const finalOutputFiles = initialResponse.outputFiles.map((file) => ({
71+
const finalOutputFiles = initialResponse.files.map((file) => ({
7672
...file,
7773
}));
7874
let buildResult = await workerConcurrencyQueue.add(
@@ -86,8 +82,11 @@ export async function attemptBuild(
8682
: DEFAULT_MAX_REPAIR_ATTEMPTS;
8783

8884
attemptDetails.push({
89-
outputFiles: initialResponse.outputFiles,
90-
usage: initialResponse.usage,
85+
outputFiles: initialResponse.files,
86+
usage: {
87+
...{ inputTokens: 0, outputTokens: 0, totalTokens: 0 },
88+
...initialResponse.usage,
89+
},
9190
reasoning: initialResponse.reasoning,
9291
buildResult,
9392
attempt: 0,

runner/orchestration/codegen.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
LlmResponse,
44
LlmResponseFile,
55
RootPromptDefinition,
6+
ToolLogEntry,
67
Usage,
78
} from '../shared-interfaces.js';
89
import {
@@ -49,6 +50,7 @@ export async function generateCodeWithAI(
4950
let usage: Usage;
5051
let success: boolean;
5152
let reasoning: string;
53+
let toolLogs: ToolLogEntry[];
5254

5355
const contextMessageData = prepareContextFilesMessage(contextFiles);
5456
const messages: PromptDataMessage[] | undefined = contextMessageData
@@ -72,6 +74,7 @@ export async function generateCodeWithAI(
7274
totalTokens: response.usage?.totalTokens ?? 0,
7375
};
7476
reasoning = response.reasoning;
77+
toolLogs = response.toolLogs ?? [];
7578

7679
progress.log(
7780
promptDef,
@@ -100,6 +103,7 @@ export async function generateCodeWithAI(
100103
usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
101104
success = false;
102105
reasoning = '';
106+
toolLogs = [];
103107
errors.push(error + '');
104108
progress.log(
105109
promptDef,
@@ -117,6 +121,7 @@ export async function generateCodeWithAI(
117121
errors,
118122
usage,
119123
reasoning,
124+
toolLogs,
120125
} satisfies LlmResponse;
121126
}
122127

0 commit comments

Comments
 (0)