Browse Source

Merge pull request #1524 from RooVetGit/checkpoint_telemetry

Add telemetry for checkpoint save/restore/diff
Matt Rubens 9 months ago
parent
commit
01b822e73f

+ 6 - 0
src/core/Cline.ts

@@ -3798,6 +3798,8 @@ export class Cline {
 			return
 		}
 
+		telemetryService.captureCheckpointDiffed(this.taskId)
+
 		if (!previousCommitHash && mode === "checkpoint") {
 			const previousCheckpoint = this.clineMessages
 				.filter(({ say }) => say === "checkpoint_saved")
@@ -3849,6 +3851,8 @@ export class Cline {
 			return
 		}
 
+		telemetryService.captureCheckpointCreated(this.taskId)
+
 		// Start the checkpoint process in the background.
 		service.saveCheckpoint(`Task: ${this.taskId}, Time: ${Date.now()}`).catch((err) => {
 			console.error("[Cline#checkpointSave] caught unexpected error, disabling checkpoints", err)
@@ -3880,6 +3884,8 @@ export class Cline {
 		try {
 			await service.restoreCheckpoint(commitHash)
 
+			telemetryService.captureCheckpointRestored(this.taskId)
+
 			await this.providerRef.deref()?.postMessageToWebview({ type: "currentCheckpointUpdated", text: commitHash })
 
 			if (mode === "restore") {

+ 9 - 0
src/core/webview/ClineProvider.ts

@@ -2567,6 +2567,15 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 			properties.apiProvider = apiConfiguration.apiProvider
 		}
 
+		// Add model ID if available
+		const currentCline = this.getCurrentCline()
+		if (currentCline?.api) {
+			const { id: modelId } = currentCline.api.getModel()
+			if (modelId) {
+				properties.modelId = modelId
+			}
+		}
+
 		return properties
 	}
 }

+ 59 - 0
src/core/webview/__tests__/ClineProvider.test.ts

@@ -1652,3 +1652,62 @@ describe("ContextProxy integration", () => {
 		expect(mockContextProxy.setValues).toBeDefined()
 	})
 })
+
+describe("getTelemetryProperties", () => {
+	let provider: ClineProvider
+	let mockContext: vscode.ExtensionContext
+	let mockOutputChannel: vscode.OutputChannel
+	let mockCline: any
+
+	beforeEach(() => {
+		// Reset mocks
+		jest.clearAllMocks()
+
+		// Setup basic mocks
+		mockContext = {
+			globalState: {
+				get: jest.fn().mockImplementation((key: string) => {
+					if (key === "mode") return "code"
+					if (key === "apiProvider") return "anthropic"
+					return undefined
+				}),
+				update: jest.fn(),
+				keys: jest.fn().mockReturnValue([]),
+			},
+			secrets: { get: jest.fn(), store: jest.fn(), delete: jest.fn() },
+			extensionUri: {} as vscode.Uri,
+			globalStorageUri: { fsPath: "/test/path" },
+			extension: { packageJSON: { version: "1.0.0" } },
+		} as unknown as vscode.ExtensionContext
+
+		mockOutputChannel = { appendLine: jest.fn() } as unknown as vscode.OutputChannel
+		provider = new ClineProvider(mockContext, mockOutputChannel)
+
+		// Setup Cline instance with mocked getModel method
+		const { Cline } = require("../../Cline")
+		mockCline = new Cline()
+		mockCline.api = {
+			getModel: jest.fn().mockReturnValue({
+				id: "claude-3-7-sonnet-20250219",
+				info: { contextWindow: 200000 },
+			}),
+		}
+	})
+
+	test("includes basic properties in telemetry", async () => {
+		const properties = await provider.getTelemetryProperties()
+
+		expect(properties).toHaveProperty("vscodeVersion")
+		expect(properties).toHaveProperty("platform")
+		expect(properties).toHaveProperty("appVersion", "1.0.0")
+	})
+
+	test("includes model ID from current Cline instance if available", async () => {
+		// Add mock Cline to stack
+		await provider.addClineToStack(mockCline)
+
+		const properties = await provider.getTelemetryProperties()
+
+		expect(properties).toHaveProperty("modelId", "claude-3-7-sonnet-20250219")
+	})
+})

+ 15 - 0
src/services/telemetry/TelemetryService.ts

@@ -22,6 +22,9 @@ class PostHogClient {
 			CONVERSATION_MESSAGE: "Conversation Message",
 			MODE_SWITCH: "Mode Switched",
 			TOOL_USED: "Tool Used",
+			CHECKPOINT_CREATED: "Checkpoint Created",
+			CHECKPOINT_RESTORED: "Checkpoint Restored",
+			CHECKPOINT_DIFFED: "Checkpoint Diffed",
 		},
 	}
 
@@ -246,6 +249,18 @@ class TelemetryService {
 		})
 	}
 
+	public captureCheckpointCreated(taskId: string): void {
+		this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_CREATED, { taskId })
+	}
+
+	public captureCheckpointDiffed(taskId: string): void {
+		this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_DIFFED, { taskId })
+	}
+
+	public captureCheckpointRestored(taskId: string): void {
+		this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_RESTORED, { taskId })
+	}
+
 	/**
 	 * Checks if telemetry is currently enabled
 	 * @returns Whether telemetry is enabled