Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions github/copilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,25 @@ type CopilotMetricsCLI struct {
LastKnownCLIVersion *CopilotMetricsCLIVersion `json:"last_known_cli_version,omitempty"`
}

// CopilotMetricsCopilotAppTokenUsage represents Copilot app token totals in a metrics report.
type CopilotMetricsCopilotAppTokenUsage struct {
AvgTokensPerRequest *float64 `json:"avg_tokens_per_request,omitempty"`
OutputTokensSum *int `json:"output_tokens_sum,omitempty"`
PromptTokensSum *int `json:"prompt_tokens_sum,omitempty"`
}

// CopilotMetricsCopilotApp represents Copilot app totals in a metrics report.
//
// Unlike totals_by_cli, there is no last_known_app_version field.
//
// GitHub API docs: https://docs.github.com/en/copilot/reference/copilot-usage-metrics/copilot-usage-metrics#copilot-app-metrics-fields
type CopilotMetricsCopilotApp struct {
SessionCount *int `json:"session_count,omitempty"`
RequestCount *int `json:"request_count,omitempty"`
PromptCount *int `json:"prompt_count,omitempty"`
TokenUsage *CopilotMetricsCopilotAppTokenUsage `json:"token_usage,omitempty"`
}

// CopilotMetricsThirdPartyAgent represents per-agent app totals in a Copilot usage metrics report.
//
// AgentID is the stable grouping key. AgentName is for display and can change.
Expand Down Expand Up @@ -1068,6 +1087,7 @@ type CopilotDailyMetrics struct {
OrganizationID *string `json:"organization_id,omitempty"`
EnterpriseID *string `json:"enterprise_id,omitempty"`
DailyActiveCLIUsers *int `json:"daily_active_cli_users,omitempty"`
DailyActiveCopilotAppUsers *int `json:"daily_active_copilot_app_users,omitempty"`
DailyActiveUsers *int `json:"daily_active_users,omitempty"`
DailyActiveCopilotCloudAgentUsers *int `json:"daily_active_copilot_cloud_agent_users,omitempty"`
WeeklyActiveUsers *int `json:"weekly_active_users,omitempty"`
Expand All @@ -1086,6 +1106,7 @@ type CopilotDailyMetrics struct {
TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"`
TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"`
TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,omitempty"`
TotalsByCopilotApp *CopilotMetricsCopilotApp `json:"totals_by_copilot_app,omitempty"`
TotalsBy3rdPartyAgent []*CopilotMetricsThirdPartyAgent `json:"totals_by_3rd_party_agent,omitempty"`
TotalsByAIAdoptionPhase []*CopilotMetricsAIAdoptionPhaseTotals `json:"totals_by_ai_adoption_phase,omitempty"`
LOCSuggestedToAddSum *int `json:"loc_suggested_to_add_sum,omitempty"`
Expand Down Expand Up @@ -1149,11 +1170,14 @@ type CopilotUserDailyMetrics struct {
TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"`
TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"`
TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,omitempty"`
TotalsByCopilotApp *CopilotMetricsCopilotApp `json:"totals_by_copilot_app,omitempty"`
TotalsBy3rdPartyAgent []*CopilotMetricsThirdPartyAgent `json:"totals_by_3rd_party_agent,omitempty"`
AIAdoptionPhase *CopilotMetricsAIAdoptionPhase `json:"ai_adoption_phase,omitempty"`
UsedAgent *bool `json:"used_agent,omitempty"`
UsedChat *bool `json:"used_chat,omitempty"`
UsedCLI *bool `json:"used_cli,omitempty"`
UsedCopilotApp *bool `json:"used_copilot_app,omitempty"`
UsedCopilotCloudAgent *bool `json:"used_copilot_cloud_agent,omitempty"`
UsedCopilotCodeReviewActive *bool `json:"used_copilot_code_review_active,omitempty"`
UsedCopilotCodeReviewPassive *bool `json:"used_copilot_code_review_passive,omitempty"`
UsedCopilotCodingAgent *bool `json:"used_copilot_coding_agent,omitempty"`
Expand Down Expand Up @@ -1185,11 +1209,14 @@ type CopilotUserPeriodicMetrics struct {
TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"`
TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"`
TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,omitempty"`
TotalsByCopilotApp *CopilotMetricsCopilotApp `json:"totals_by_copilot_app,omitempty"`
TotalsBy3rdPartyAgent []*CopilotMetricsThirdPartyAgent `json:"totals_by_3rd_party_agent,omitempty"`
AIAdoptionPhase *CopilotMetricsAIAdoptionPhase `json:"ai_adoption_phase,omitempty"`
UsedAgent *bool `json:"used_agent,omitempty"`
UsedChat *bool `json:"used_chat,omitempty"`
UsedCLI *bool `json:"used_cli,omitempty"`
UsedCopilotApp *bool `json:"used_copilot_app,omitempty"`
UsedCopilotCloudAgent *bool `json:"used_copilot_cloud_agent,omitempty"`
UsedCopilotCodeReviewActive *bool `json:"used_copilot_code_review_active,omitempty"`
UsedCopilotCodeReviewPassive *bool `json:"used_copilot_code_review_passive,omitempty"`
UsedCopilotCodingAgent *bool `json:"used_copilot_coding_agent,omitempty"`
Expand Down
91 changes: 80 additions & 11 deletions github/copilot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3034,6 +3034,7 @@ func TestCopilotService_DownloadDailyMetrics(t *testing.T) {
"day": "2026-04-01",
"organization_id": "123",
"daily_active_cli_users": 2,
"daily_active_copilot_app_users": 1,
"daily_active_users": 10,
"weekly_active_users": 20,
"monthly_active_users": 30,
Expand Down Expand Up @@ -3064,6 +3065,16 @@ func TestCopilotService_DownloadDailyMetrics(t *testing.T) {
"prompt_tokens_sum": 9494
}
},
"totals_by_copilot_app": {
"session_count": 2,
"request_count": 6,
"prompt_count": 3,
"token_usage": {
"avg_tokens_per_request": 150.0,
"output_tokens_sum": 600,
"prompt_tokens_sum": 300
}
},
"totals_by_3rd_party_agent": [
{
"agent_name": "Claude",
Expand Down Expand Up @@ -3113,12 +3124,13 @@ func TestCopilotService_DownloadDailyMetrics(t *testing.T) {
}

want := &CopilotDailyMetrics{
Day: "2026-04-01",
OrganizationID: Ptr("123"),
DailyActiveCLIUsers: Ptr(2),
DailyActiveUsers: Ptr(10),
WeeklyActiveUsers: Ptr(20),
MonthlyActiveUsers: Ptr(30),
Day: "2026-04-01",
OrganizationID: Ptr("123"),
DailyActiveCLIUsers: Ptr(2),
DailyActiveCopilotAppUsers: Ptr(1),
DailyActiveUsers: Ptr(10),
WeeklyActiveUsers: Ptr(20),
MonthlyActiveUsers: Ptr(30),
CopilotMetricsChatPanel: CopilotMetricsChatPanel{
ChatPanelAskMode: Ptr(4),
},
Expand Down Expand Up @@ -3148,6 +3160,16 @@ func TestCopilotService_DownloadDailyMetrics(t *testing.T) {
PromptTokensSum: Ptr(9494),
},
},
TotalsByCopilotApp: &CopilotMetricsCopilotApp{
SessionCount: Ptr(2),
RequestCount: Ptr(6),
PromptCount: Ptr(3),
TokenUsage: &CopilotMetricsCopilotAppTokenUsage{
AvgTokensPerRequest: Ptr(150.0),
OutputTokensSum: Ptr(600),
PromptTokensSum: Ptr(300),
},
},
TotalsBy3rdPartyAgent: []*CopilotMetricsThirdPartyAgent{
{
AgentName: "Claude",
Expand Down Expand Up @@ -3227,6 +3249,7 @@ func TestCopilotService_DownloadPeriodicMetrics(t *testing.T) {
{
"day": "2026-03-05",
"daily_active_cli_users": 2,
"daily_active_copilot_app_users": 1,
"daily_active_users": 5,
"totals_by_cli": {
"session_count": 1,
Expand All @@ -3238,6 +3261,16 @@ func TestCopilotService_DownloadPeriodicMetrics(t *testing.T) {
"prompt_tokens_sum": 3000
}
},
"totals_by_copilot_app": {
"session_count": 1,
"request_count": 2,
"prompt_count": 1,
"token_usage": {
"avg_tokens_per_request": 800.0,
"output_tokens_sum": 400,
"prompt_tokens_sum": 400
}
},
"pull_requests": {
"median_minutes_to_merge": 8.5,
"median_minutes_to_merge_copilot_authored": 5.0,
Expand Down Expand Up @@ -3266,9 +3299,10 @@ func TestCopilotService_DownloadPeriodicMetrics(t *testing.T) {
CreatedAt: refTimestamp(1136178000),
DayTotals: []*CopilotDailyMetrics{
{
Day: "2026-03-05",
DailyActiveCLIUsers: Ptr(2),
DailyActiveUsers: Ptr(5),
Day: "2026-03-05",
DailyActiveCLIUsers: Ptr(2),
DailyActiveCopilotAppUsers: Ptr(1),
DailyActiveUsers: Ptr(5),
TotalsByCLI: &CopilotMetricsCLI{
SessionCount: Ptr(1),
RequestCount: Ptr(2),
Expand All @@ -3279,6 +3313,16 @@ func TestCopilotService_DownloadPeriodicMetrics(t *testing.T) {
PromptTokensSum: Ptr(3000),
},
},
TotalsByCopilotApp: &CopilotMetricsCopilotApp{
SessionCount: Ptr(1),
RequestCount: Ptr(2),
PromptCount: Ptr(1),
TokenUsage: &CopilotMetricsCopilotAppTokenUsage{
AvgTokensPerRequest: Ptr(800.0),
OutputTokensSum: Ptr(400),
PromptTokensSum: Ptr(400),
},
},
PullRequests: &CopilotMetricsPullRequests{
MedianMinutesToMerge: Ptr(8.5),
MedianMinutesToMergeCopilotAuthored: Ptr(5.0),
Expand Down Expand Up @@ -3322,7 +3366,7 @@ func TestCopilotService_DownloadUserDailyMetrics(t *testing.T) {

mux.HandleFunc("/path/to/users-daily", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"user_id":1,"user_login":"alice","day":"2026-04-01","ai_credits_used":12.5,"user_initiated_interaction_count":5,"chat_panel_edit_mode":2,"used_chat":true,"used_cli":true,"used_copilot_code_review_active":true,"ai_adoption_phase":{"phase":"Phase 2","phase_number":2,"version":"v1"},"totals_by_cli":{"session_count":2,"request_count":2,"prompt_count":1,"last_known_cli_version":{"sampled_at":`+referenceTimeStr+`,"cli_version":"1.0.8"}},"totals_by_ide":[{"ide":"vscode","user_initiated_interaction_count":5,"last_known_plugin_version":{"sampled_at":`+referenceTimeStr+`,"plugin":"copilot","plugin_version":"1.0.0"},"last_known_ide_version":{"sampled_at":`+referenceTimeStr+`,"ide_version":"1.90"}}],"totals_by_3rd_party_agent":[{"agent_name":"Claude","agent_id":"claude","user_initiated_interaction_count":2}]}
fmt.Fprint(w, `{"user_id":1,"user_login":"alice","day":"2026-04-01","ai_credits_used":12.5,"user_initiated_interaction_count":5,"chat_panel_edit_mode":2,"used_chat":true,"used_cli":true,"used_copilot_app":true,"used_copilot_cloud_agent":false,"used_copilot_coding_agent":false,"used_copilot_code_review_active":true,"ai_adoption_phase":{"phase":"Phase 2","phase_number":2,"version":"v1"},"totals_by_cli":{"session_count":2,"request_count":2,"prompt_count":1,"last_known_cli_version":{"sampled_at":`+referenceTimeStr+`,"cli_version":"1.0.8"}},"totals_by_copilot_app":{"session_count":1,"request_count":4,"prompt_count":2,"token_usage":{"avg_tokens_per_request":150.0,"output_tokens_sum":100,"prompt_tokens_sum":50}},"totals_by_ide":[{"ide":"vscode","user_initiated_interaction_count":5,"last_known_plugin_version":{"sampled_at":`+referenceTimeStr+`,"plugin":"copilot","plugin_version":"1.0.0"},"last_known_ide_version":{"sampled_at":`+referenceTimeStr+`,"ide_version":"1.90"}}],"totals_by_3rd_party_agent":[{"agent_name":"Claude","agent_id":"claude","user_initiated_interaction_count":2}]}
{"user_id":2,"user_login":"bob","day":"2026-04-01","used_agent":true,"used_copilot_code_review_passive":true}
`)
})
Expand All @@ -3349,6 +3393,9 @@ func TestCopilotService_DownloadUserDailyMetrics(t *testing.T) {
},
UsedChat: Ptr(true),
UsedCLI: Ptr(true),
UsedCopilotApp: Ptr(true),
UsedCopilotCloudAgent: Ptr(false),
UsedCopilotCodingAgent: Ptr(false),
UsedCopilotCodeReviewActive: Ptr(true),
AIAdoptionPhase: &CopilotMetricsAIAdoptionPhase{
Phase: "Phase 2",
Expand All @@ -3364,6 +3411,16 @@ func TestCopilotService_DownloadUserDailyMetrics(t *testing.T) {
CLIVersion: "1.0.8",
},
},
TotalsByCopilotApp: &CopilotMetricsCopilotApp{
SessionCount: Ptr(1),
RequestCount: Ptr(4),
PromptCount: Ptr(2),
TokenUsage: &CopilotMetricsCopilotAppTokenUsage{
AvgTokensPerRequest: Ptr(150.0),
OutputTokensSum: Ptr(100),
PromptTokensSum: Ptr(50),
},
},
TotalsByIDE: []*CopilotUserMetricsIDE{
{
IDE: "vscode",
Expand Down Expand Up @@ -3443,7 +3500,7 @@ func TestCopilotService_DownloadUserPeriodicMetrics(t *testing.T) {
mux.HandleFunc("/path/to/users-periodic", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"report_start_day":"2026-03-05","report_end_day":"2026-04-01","day":"2026-03-05","user_id":1,"user_login":"alice","ai_credits_used":3.25,"user_initiated_interaction_count":3,"used_copilot_code_review_active":true,"ai_adoption_phase":{"phase":"Phase 1","phase_number":1,"version":"v1"}}
{"report_start_day":"2026-03-05","report_end_day":"2026-04-01","day":"2026-03-06","user_id":1,"user_login":"alice","used_cli":true,"used_copilot_code_review_passive":true,"used_copilot_coding_agent":true,"totals_by_cli":{"session_count":1,"request_count":3,"prompt_count":2,"token_usage":{"avg_tokens_per_request":1200.5,"output_tokens_sum":2400,"prompt_tokens_sum":1201}}}
{"report_start_day":"2026-03-05","report_end_day":"2026-04-01","day":"2026-03-06","user_id":1,"user_login":"alice","used_cli":true,"used_copilot_app":true,"used_copilot_cloud_agent":true,"used_copilot_coding_agent":true,"used_copilot_code_review_passive":true,"totals_by_cli":{"session_count":1,"request_count":3,"prompt_count":2,"token_usage":{"avg_tokens_per_request":1200.5,"output_tokens_sum":2400,"prompt_tokens_sum":1201}},"totals_by_copilot_app":{"session_count":2,"request_count":5,"prompt_count":3,"token_usage":{"avg_tokens_per_request":900.0,"output_tokens_sum":1500,"prompt_tokens_sum":1500}}}
`)
})

Expand Down Expand Up @@ -3480,6 +3537,8 @@ func TestCopilotService_DownloadUserPeriodicMetrics(t *testing.T) {
UserID: 1,
UserLogin: "alice",
UsedCLI: Ptr(true),
UsedCopilotApp: Ptr(true),
UsedCopilotCloudAgent: Ptr(true),
UsedCopilotCodeReviewPassive: Ptr(true),
UsedCopilotCodingAgent: Ptr(true),
TotalsByCLI: &CopilotMetricsCLI{
Expand All @@ -3492,6 +3551,16 @@ func TestCopilotService_DownloadUserPeriodicMetrics(t *testing.T) {
PromptTokensSum: Ptr(1201),
},
},
TotalsByCopilotApp: &CopilotMetricsCopilotApp{
SessionCount: Ptr(2),
RequestCount: Ptr(5),
PromptCount: Ptr(3),
TokenUsage: &CopilotMetricsCopilotAppTokenUsage{
AvgTokensPerRequest: Ptr(900.0),
OutputTokensSum: Ptr(1500),
PromptTokensSum: Ptr(1500),
},
},
},
}

Expand Down
Loading
Loading