feat: Add programming language to CLI events (#6071)

Co-authored-by: christine betts <chrstn@uw.edu>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Adam Weidman <65992621+adamfweidman@users.noreply.github.com>
Co-authored-by: JaeHo Jang <diehreo@gmail.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Victor May <mayvic@google.com>
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
Co-authored-by: joshualitt <joshualitt@google.com>
Co-authored-by: Billy Biggs <bbiggs@google.com>
Co-authored-by: Ricardo Fabbri <rfabbri@gmail.com>
Co-authored-by: Arya Gummadi <aryagummadi@google.com>
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: Shreya Keshive <skeshive@gmail.com>
Co-authored-by: Ben Guo <36952867+HunDun0Ben@users.noreply.github.com>
Co-authored-by: Ben Guo <hundunben@gmail.com>
Co-authored-by: mkusaka <hinoshita1992@gmail.com>
This commit is contained in:
Nanda Kishore
2025-08-22 17:47:32 +05:30
committed by GitHub
parent 4ced997d63
commit 528227a0f8
18 changed files with 420 additions and 46 deletions

View File

@@ -26,6 +26,10 @@ vi.mock('../utils/editor.js', () => ({
openDiff: mockOpenDiff,
}));
vi.mock('../telemetry/loggers.js', () => ({
logFileOperation: vi.fn(),
}));
import { describe, it, expect, beforeEach, afterEach, vi, Mock } from 'vitest';
import { applyReplacement, EditTool, EditToolParams } from './edit.js';
import { FileDiff, ToolConfirmationOutcome } from './tools.js';

View File

@@ -27,6 +27,11 @@ import { DEFAULT_DIFF_OPTIONS, getDiffStat } from './diffOptions.js';
import { ReadFileTool } from './read-file.js';
import { ModifiableDeclarativeTool, ModifyContext } from './modifiable-tool.js';
import { IDEConnectionStatus } from '../ide/ide-client.js';
import { FileOperation } from '../telemetry/metrics.js';
import { logFileOperation } from '../telemetry/loggers.js';
import { FileOperationEvent } from '../telemetry/types.js';
import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js';
import { getSpecificMimeType } from '../utils/fileUtils.js';
export function applyReplacement(
currentContent: string | null,
@@ -345,12 +350,21 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
.writeTextFile(this.params.file_path, editData.newContent);
let displayResult: ToolResultDisplay;
const fileName = path.basename(this.params.file_path);
const originallyProposedContent =
this.params.ai_proposed_string || this.params.new_string;
const diffStat = getDiffStat(
fileName,
editData.currentContent ?? '',
originallyProposedContent,
this.params.new_string,
);
if (editData.isNewFile) {
displayResult = `Created ${shortenPath(makeRelative(this.params.file_path, this.config.getTargetDir()))}`;
} else {
// Generate diff for display, even though core logic doesn't technically need it
// The CLI wrapper will use this part of the ToolResult
const fileName = path.basename(this.params.file_path);
const fileDiff = Diff.createPatch(
fileName,
editData.currentContent ?? '', // Should not be null here if not isNewFile
@@ -359,14 +373,6 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
'Proposed',
DEFAULT_DIFF_OPTIONS,
);
const originallyProposedContent =
this.params.ai_proposed_string || this.params.new_string;
const diffStat = getDiffStat(
fileName,
editData.currentContent ?? '',
originallyProposedContent,
this.params.new_string,
);
displayResult = {
fileDiff,
fileName,
@@ -387,6 +393,26 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
);
}
const lines = editData.newContent.split('\n').length;
const mimetype = getSpecificMimeType(this.params.file_path);
const extension = path.extname(this.params.file_path);
const programming_language = getProgrammingLanguage({
file_path: this.params.file_path,
});
logFileOperation(
this.config,
new FileOperationEvent(
EditTool.Name,
editData.isNewFile ? FileOperation.CREATE : FileOperation.UPDATE,
lines,
mimetype,
extension,
diffStat,
programming_language,
),
);
return {
llmContent: llmSuccessMessageParts.join(' '),
returnDisplay: displayResult,

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ReadFileTool, ReadFileToolParams } from './read-file.js';
import { ToolErrorType } from './tool-error.js';
import path from 'path';
@@ -17,6 +17,10 @@ import { StandardFileSystemService } from '../services/fileSystemService.js';
import { createMockWorkspaceContext } from '../test-utils/mockWorkspaceContext.js';
import { ToolInvocation, ToolResult } from './tools.js';
vi.mock('../telemetry/loggers.js', () => ({
logFileOperation: vi.fn(),
}));
describe('ReadFileTool', () => {
let tempRootDir: string;
let tool: ReadFileTool;

View File

@@ -21,10 +21,10 @@ import {
getSpecificMimeType,
} from '../utils/fileUtils.js';
import { Config } from '../config/config.js';
import {
recordFileOperationMetric,
FileOperation,
} from '../telemetry/metrics.js';
import { FileOperation } from '../telemetry/metrics.js';
import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js';
import { logFileOperation } from '../telemetry/loggers.js';
import { FileOperationEvent } from '../telemetry/types.js';
/**
* Parameters for the ReadFile tool
@@ -112,12 +112,20 @@ ${result.llmContent}`;
? result.llmContent.split('\n').length
: undefined;
const mimetype = getSpecificMimeType(this.params.absolute_path);
recordFileOperationMetric(
const programming_language = getProgrammingLanguage({
absolute_path: this.params.absolute_path,
});
logFileOperation(
this.config,
FileOperation.READ,
lines,
mimetype,
path.extname(this.params.absolute_path),
new FileOperationEvent(
ReadFileTool.Name,
FileOperation.READ,
lines,
mimetype,
path.extname(this.params.absolute_path),
undefined,
programming_language,
),
);
return {

View File

@@ -47,6 +47,10 @@ vi.mock('mime-types', () => {
};
});
vi.mock('../telemetry/loggers.js', () => ({
logFileOperation: vi.fn(),
}));
describe('ReadManyFilesTool', () => {
let tool: ReadManyFilesTool;
let tempRootDir: string;

View File

@@ -25,10 +25,10 @@ import {
} from '../utils/fileUtils.js';
import { PartListUnion } from '@google/genai';
import { Config, DEFAULT_FILE_FILTERING_OPTIONS } from '../config/config.js';
import {
recordFileOperationMetric,
FileOperation,
} from '../telemetry/metrics.js';
import { FileOperation } from '../telemetry/metrics.js';
import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js';
import { logFileOperation } from '../telemetry/loggers.js';
import { FileOperationEvent } from '../telemetry/types.js';
import { ToolErrorType } from './tool-error.js';
/**
@@ -468,12 +468,20 @@ ${finalExclusionPatternsForDescription
? fileReadResult.llmContent.split('\n').length
: undefined;
const mimetype = getSpecificMimeType(filePath);
recordFileOperationMetric(
const programming_language = getProgrammingLanguage({
absolute_path: filePath,
});
logFileOperation(
this.config,
FileOperation.READ,
lines,
mimetype,
path.extname(filePath),
new FileOperationEvent(
ReadManyFilesTool.Name,
FileOperation.READ,
lines,
mimetype,
path.extname(filePath),
undefined,
programming_language,
),
);
}
} else {

View File

@@ -88,6 +88,11 @@ const mockConfigInternal = {
}) as unknown as ToolRegistry,
};
const mockConfig = mockConfigInternal as unknown as Config;
vi.mock('../telemetry/loggers.js', () => ({
logFileOperation: vi.fn(),
}));
// --- END MOCKS ---
describe('WriteFileTool', () => {

View File

@@ -30,11 +30,11 @@ import {
import { DEFAULT_DIFF_OPTIONS, getDiffStat } from './diffOptions.js';
import { ModifiableDeclarativeTool, ModifyContext } from './modifiable-tool.js';
import { getSpecificMimeType } from '../utils/fileUtils.js';
import {
recordFileOperationMetric,
FileOperation,
} from '../telemetry/metrics.js';
import { FileOperation } from '../telemetry/metrics.js';
import { IDEConnectionStatus } from '../ide/ide-client.js';
import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js';
import { logFileOperation } from '../telemetry/loggers.js';
import { FileOperationEvent } from '../telemetry/types.js';
/**
* Parameters for the WriteFile tool
@@ -314,23 +314,32 @@ class WriteFileToolInvocation extends BaseToolInvocation<
const lines = fileContent.split('\n').length;
const mimetype = getSpecificMimeType(file_path);
const extension = path.extname(file_path); // Get extension
const programming_language = getProgrammingLanguage({ file_path });
if (isNewFile) {
recordFileOperationMetric(
logFileOperation(
this.config,
FileOperation.CREATE,
lines,
mimetype,
extension,
diffStat,
new FileOperationEvent(
WriteFileTool.Name,
FileOperation.CREATE,
lines,
mimetype,
extension,
diffStat,
programming_language,
),
);
} else {
recordFileOperationMetric(
logFileOperation(
this.config,
FileOperation.UPDATE,
lines,
mimetype,
extension,
diffStat,
new FileOperationEvent(
WriteFileTool.Name,
FileOperation.UPDATE,
lines,
mimetype,
extension,
diffStat,
programming_language,
),
);
}