-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add telemetry for slash command usage #6381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // npx vitest run packages/telemetry/src/__tests__/TelemetryService.slashCommands.test.ts | ||
|
|
||
| import { describe, it, expect, vi, beforeEach, afterEach } from "vitest" | ||
| import { TelemetryService } from "../TelemetryService" | ||
| import { TelemetryEventName } from "@roo-code/types" | ||
|
|
||
| describe("TelemetryService - Slash Commands", () => { | ||
| let telemetryService: TelemetryService | ||
| let mockClient: { | ||
| capture: ReturnType<typeof vi.fn> | ||
| setProvider: ReturnType<typeof vi.fn> | ||
| updateTelemetryState: ReturnType<typeof vi.fn> | ||
| isTelemetryEnabled: ReturnType<typeof vi.fn> | ||
| shutdown: ReturnType<typeof vi.fn> | ||
| } | ||
|
|
||
| beforeEach(() => { | ||
| // Reset the singleton instance | ||
| ;(TelemetryService as unknown as { _instance: TelemetryService | null })._instance = null | ||
|
|
||
| mockClient = { | ||
| capture: vi.fn(), | ||
| setProvider: vi.fn(), | ||
| updateTelemetryState: vi.fn(), | ||
| isTelemetryEnabled: vi.fn().mockReturnValue(true), | ||
| shutdown: vi.fn(), | ||
| } | ||
|
|
||
| telemetryService = TelemetryService.createInstance([mockClient]) | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| // Clean up singleton instance after each test | ||
| ;(TelemetryService as unknown as { _instance: TelemetryService | null })._instance = null | ||
| }) | ||
|
|
||
| describe("captureSlashCommandUsed", () => { | ||
| it("should capture custom slash command usage", () => { | ||
| const taskId = "test-task-123" | ||
| const commandType = "custom" | ||
| const commandName = "deploy" | ||
|
|
||
| telemetryService.captureSlashCommandUsed(taskId, commandType, commandName) | ||
|
|
||
| expect(mockClient.capture).toHaveBeenCalledWith({ | ||
| event: TelemetryEventName.SLASH_COMMAND_USED, | ||
| properties: { | ||
| taskId, | ||
| commandType, | ||
| commandName, | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it("should capture mode switch slash command usage", () => { | ||
| const taskId = "test-task-456" | ||
| const commandType = "mode_switch" | ||
| const commandName = "code" | ||
|
|
||
| telemetryService.captureSlashCommandUsed(taskId, commandType, commandName) | ||
|
|
||
| expect(mockClient.capture).toHaveBeenCalledWith({ | ||
| event: TelemetryEventName.SLASH_COMMAND_USED, | ||
| properties: { | ||
| taskId, | ||
| commandType, | ||
| commandName, | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it("should handle multiple slash command captures", () => { | ||
| const taskId = "test-task-789" | ||
|
|
||
| telemetryService.captureSlashCommandUsed(taskId, "custom", "build") | ||
| telemetryService.captureSlashCommandUsed(taskId, "mode_switch", "debug") | ||
| telemetryService.captureSlashCommandUsed(taskId, "custom", "test") | ||
|
|
||
| expect(mockClient.capture).toHaveBeenCalledTimes(3) | ||
| expect(mockClient.capture).toHaveBeenNthCalledWith(1, { | ||
| event: TelemetryEventName.SLASH_COMMAND_USED, | ||
| properties: { | ||
| taskId, | ||
| commandType: "custom", | ||
| commandName: "build", | ||
| }, | ||
| }) | ||
| expect(mockClient.capture).toHaveBeenNthCalledWith(2, { | ||
| event: TelemetryEventName.SLASH_COMMAND_USED, | ||
| properties: { | ||
| taskId, | ||
| commandType: "mode_switch", | ||
| commandName: "debug", | ||
| }, | ||
| }) | ||
| expect(mockClient.capture).toHaveBeenNthCalledWith(3, { | ||
| event: TelemetryEventName.SLASH_COMMAND_USED, | ||
| properties: { | ||
| taskId, | ||
| commandType: "custom", | ||
| commandName: "test", | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it("should not capture when service is not ready", () => { | ||
| // Reset the instance to test empty service | ||
| ;(TelemetryService as unknown as { _instance: TelemetryService | null })._instance = null | ||
| const emptyService = TelemetryService.createInstance([]) | ||
|
|
||
| emptyService.captureSlashCommandUsed("task-id", "custom", "command") | ||
|
|
||
| // Should not throw and should not call any client methods | ||
| expect(mockClient.capture).not.toHaveBeenCalled() | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,8 @@ import { | |
| import { cn } from "@src/lib/utils" | ||
| import { convertToMentionPath } from "@src/utils/path-mentions" | ||
| import { StandardTooltip } from "@src/components/ui" | ||
| import { telemetryClient } from "@src/utils/TelemetryClient" | ||
| import { TelemetryEventName } from "@roo-code/types" | ||
|
|
||
| import Thumbnails from "../common/Thumbnails" | ||
| import { ModeSelector } from "./ModeSelector" | ||
|
|
@@ -299,6 +301,12 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>( | |
| } | ||
|
|
||
| if (type === ContextMenuOptionType.Mode && value) { | ||
| // Track telemetry for mode selection from context menu | ||
| telemetryClient.capture(TelemetryEventName.SLASH_COMMAND_USED, { | ||
| commandType: "mode", | ||
| commandName: value, | ||
| }) | ||
|
Comment on lines
+305
to
+308
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The telemetry capture is missing the |
||
|
|
||
| // Handle mode selection. | ||
| setMode(value) | ||
| setInputValue("") | ||
|
|
@@ -308,6 +316,12 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>( | |
| } | ||
|
|
||
| if (type === ContextMenuOptionType.Command && value) { | ||
| // Track telemetry for slash command usage from context menu | ||
| telemetryClient.capture(TelemetryEventName.SLASH_COMMAND_USED, { | ||
| commandType: "custom", | ||
| commandName: value, | ||
| }) | ||
|
Comment on lines
+320
to
+323
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This telemetry capture is also missing the |
||
|
|
||
| // Handle command selection. | ||
| setSelectedMenuIndex(-1) | ||
| setInputValue("") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
commandTypevalue should be"mode_switch"not"mode"to match the type signature ofTelemetryService.captureSlashCommandUsed(taskId, commandType: "custom" | "mode_switch", commandName). This mismatch will cause the telemetry data to be inconsistent with the intended classification of mode switch commands.