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
99 changes: 71 additions & 28 deletions github/copilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,44 @@ type CopilotMetricsThirdPartyAgent struct {
SessionCount *int `json:"session_count,omitempty"`
}

// CopilotMetricsAIAdoptionPhase represents a user's AI adoption phase classification
// in a Copilot usage metrics report.
//
// Phase assignment uses a trailing 28-day engagement window and may change day to day.
// Version identifies the classification model (for example, "v1").
//
// GitHub API docs: https://docs.github.com/en/copilot/reference/copilot-usage-metrics/copilot-usage-metrics#ai-adoption-phase-fields
type CopilotMetricsAIAdoptionPhase struct {
PhaseNumber int `json:"phase_number"`
Phase string `json:"phase"`
Version string `json:"version"`
}

// CopilotMetricsAIAdoptionPhaseTotals represents aggregated per-phase averages in an
// enterprise or organization Copilot usage metrics report.
//
// Aggregated metrics report the average per user within each phase rather than the sum,
// except TotalEngagedUsers and TotalPullRequestsMerged.
//
// GitHub API docs: https://docs.github.com/en/copilot/reference/copilot-usage-metrics/copilot-usage-metrics#ai-adoption-phase-fields
type CopilotMetricsAIAdoptionPhaseTotals struct {
Phase string `json:"phase"`
PhaseNumber int `json:"phase_number"`
TotalEngagedUsers *int `json:"total_engaged_users,omitempty"`
AvgUserInitiatedInteractions *float64 `json:"avg_user_initiated_interactions,omitempty"`
AvgCodeGenerationActivities *float64 `json:"avg_code_generation_activities,omitempty"`
AvgCodeAcceptanceActivities *float64 `json:"avg_code_acceptance_activities,omitempty"`
AvgLOCAdded *float64 `json:"avg_loc_added,omitempty"`
AvgLOCDeleted *float64 `json:"avg_loc_deleted,omitempty"`
AvgPullRequestsReviewed *float64 `json:"avg_pull_requests_reviewed,omitempty"`
AvgPullRequestsCreated *float64 `json:"avg_pull_requests_created,omitempty"`
AvgPullRequestsMerged *float64 `json:"avg_pull_requests_merged,omitempty"`
TotalPullRequestsMerged *int `json:"total_pull_requests_merged,omitempty"`
AvgPullRequestsMedianMinutesToMerge *float64 `json:"avg_pull_requests_median_minutes_to_merge,omitempty"`
AvgPullRequestsMinutesToReview *float64 `json:"avg_pull_requests_minutes_to_review,omitempty"`
AvgPullRequestsReviewCycles *float64 `json:"avg_pull_requests_review_cycles,omitempty"`
}

// CopilotDailyMetrics represents the payload downloaded from a 1-day Copilot usage metrics report.
type CopilotDailyMetrics struct {
Day string `json:"day"`
Expand All @@ -1040,20 +1078,21 @@ type CopilotDailyMetrics struct {
MonthlyActiveCopilotCloudAgentUsers *int `json:"monthly_active_copilot_cloud_agent_users,omitempty"`
UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"`
CopilotMetricsChatPanel
CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"`
CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"`
TotalsByIDE []*CopilotMetricsIDE `json:"totals_by_ide,omitempty"`
TotalsByFeature []*CopilotMetricsFeature `json:"totals_by_feature,omitempty"`
TotalsByLanguageFeature []*CopilotMetricsLanguageFeature `json:"totals_by_language_feature,omitempty"`
TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"`
TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"`
TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,omitempty"`
TotalsBy3rdPartyAgent []*CopilotMetricsThirdPartyAgent `json:"totals_by_3rd_party_agent,omitempty"`
LOCSuggestedToAddSum *int `json:"loc_suggested_to_add_sum,omitempty"`
LOCSuggestedToDeleteSum *int `json:"loc_suggested_to_delete_sum,omitempty"`
LOCAddedSum *int `json:"loc_added_sum,omitempty"`
LOCDeletedSum *int `json:"loc_deleted_sum,omitempty"`
PullRequests *CopilotMetricsPullRequests `json:"pull_requests,omitempty"`
CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"`
CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"`
TotalsByIDE []*CopilotMetricsIDE `json:"totals_by_ide,omitempty"`
TotalsByFeature []*CopilotMetricsFeature `json:"totals_by_feature,omitempty"`
TotalsByLanguageFeature []*CopilotMetricsLanguageFeature `json:"totals_by_language_feature,omitempty"`
TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"`
TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"`
TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,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"`
LOCSuggestedToDeleteSum *int `json:"loc_suggested_to_delete_sum,omitempty"`
LOCAddedSum *int `json:"loc_added_sum,omitempty"`
LOCDeletedSum *int `json:"loc_deleted_sum,omitempty"`
PullRequests *CopilotMetricsPullRequests `json:"pull_requests,omitempty"`
}

// CopilotPeriodicMetrics represents the payload downloaded from a multi-day (e.g. 28-day rolling)
Expand Down Expand Up @@ -1094,12 +1133,13 @@ type CopilotUserMetricsIDE struct {
// CopilotUserDailyMetrics represents a single user's per-day Copilot usage metrics record from a
// 1-day user metrics report. User metrics reports are served as newline-delimited JSON.
type CopilotUserDailyMetrics struct {
UserID int `json:"user_id"`
UserLogin string `json:"user_login"`
Day string `json:"day"`
OrganizationID *string `json:"organization_id,omitempty"`
EnterpriseID *string `json:"enterprise_id,omitempty"`
UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"`
UserID int `json:"user_id"`
UserLogin string `json:"user_login"`
Day string `json:"day"`
OrganizationID *string `json:"organization_id,omitempty"`
EnterpriseID *string `json:"enterprise_id,omitempty"`
AICreditsUsed *float64 `json:"ai_credits_used,omitempty"`
UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"`
CopilotMetricsChatPanel
CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"`
CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"`
Expand All @@ -1110,6 +1150,7 @@ type CopilotUserDailyMetrics struct {
TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"`
TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,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"`
Expand All @@ -1126,14 +1167,15 @@ type CopilotUserDailyMetrics struct {
// multi-day (e.g. 28-day rolling) user metrics report. User metrics reports are served as
// newline-delimited JSON.
type CopilotUserPeriodicMetrics struct {
ReportStartDay string `json:"report_start_day"`
ReportEndDay string `json:"report_end_day"`
Day string `json:"day"`
OrganizationID *string `json:"organization_id,omitempty"`
EnterpriseID *string `json:"enterprise_id,omitempty"`
UserID int `json:"user_id"`
UserLogin string `json:"user_login"`
UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"`
ReportStartDay string `json:"report_start_day"`
ReportEndDay string `json:"report_end_day"`
Day string `json:"day"`
OrganizationID *string `json:"organization_id,omitempty"`
EnterpriseID *string `json:"enterprise_id,omitempty"`
UserID int `json:"user_id"`
UserLogin string `json:"user_login"`
AICreditsUsed *float64 `json:"ai_credits_used,omitempty"`
UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"`
CopilotMetricsChatPanel
CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"`
CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"`
Expand All @@ -1144,6 +1186,7 @@ type CopilotUserPeriodicMetrics struct {
TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"`
TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,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"`
Expand Down
54 changes: 52 additions & 2 deletions github/copilot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3072,6 +3072,25 @@ func TestCopilotService_DownloadDailyMetrics(t *testing.T) {
"session_count": 3
}
],
"totals_by_ai_adoption_phase": [
{
"phase": "Phase 1",
"phase_number": 1,
"total_engaged_users": 4,
"avg_user_initiated_interactions": 1.5,
"avg_code_generation_activities": 2.0,
"avg_code_acceptance_activities": 1.0,
"avg_loc_added": 22.0,
"avg_loc_deleted": 0.0,
"avg_pull_requests_reviewed": 0.5,
"avg_pull_requests_created": 1.0,
"avg_pull_requests_merged": 1.0,
"total_pull_requests_merged": 4,
"avg_pull_requests_median_minutes_to_merge": 2.5,
"avg_pull_requests_minutes_to_review": 1.5,
"avg_pull_requests_review_cycles": 1.0
}
],
"loc_added_sum": 100,
"pull_requests": {
"total_reviewed": 1,
Expand Down Expand Up @@ -3137,6 +3156,25 @@ func TestCopilotService_DownloadDailyMetrics(t *testing.T) {
SessionCount: Ptr(3),
},
},
TotalsByAIAdoptionPhase: []*CopilotMetricsAIAdoptionPhaseTotals{
{
Phase: "Phase 1",
PhaseNumber: 1,
TotalEngagedUsers: Ptr(4),
AvgUserInitiatedInteractions: Ptr(1.5),
AvgCodeGenerationActivities: Ptr(2.0),
AvgCodeAcceptanceActivities: Ptr(1.0),
AvgLOCAdded: Ptr(22.0),
AvgLOCDeleted: Ptr(0.0),
AvgPullRequestsReviewed: Ptr(0.5),
AvgPullRequestsCreated: Ptr(1.0),
AvgPullRequestsMerged: Ptr(1.0),
TotalPullRequestsMerged: Ptr(4),
AvgPullRequestsMedianMinutesToMerge: Ptr(2.5),
AvgPullRequestsMinutesToReview: Ptr(1.5),
AvgPullRequestsReviewCycles: Ptr(1.0),
},
},
LOCAddedSum: Ptr(100),
PullRequests: &CopilotMetricsPullRequests{
TotalReviewed: Ptr(1),
Expand Down Expand Up @@ -3284,7 +3322,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","user_initiated_interaction_count":5,"chat_panel_edit_mode":2,"used_chat":true,"used_cli":true,"used_copilot_code_review_active":true,"totals_by_cli":{"session_count":2,"request_count":2,"prompt_count":1,"last_known_cli_version":{"sampled_at":`+refTimeStr(1136178000)+`,"cli_version":"1.0.8"}},"totals_by_ide":[{"ide":"vscode","user_initiated_interaction_count":5,"last_known_plugin_version":{"sampled_at":`+refTimeStr(1136178001)+`,"plugin":"copilot","plugin_version":"1.0.0"},"last_known_ide_version":{"sampled_at":`+refTimeStr(1136178002)+`,"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_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":`+refTimeStr(1136178000)+`,"cli_version":"1.0.8"}},"totals_by_ide":[{"ide":"vscode","user_initiated_interaction_count":5,"last_known_plugin_version":{"sampled_at":`+refTimeStr(1136178001)+`,"plugin":"copilot","plugin_version":"1.0.0"},"last_known_ide_version":{"sampled_at":`+refTimeStr(1136178002)+`,"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 @@ -3304,13 +3342,19 @@ func TestCopilotService_DownloadUserDailyMetrics(t *testing.T) {
UserID: 1,
UserLogin: "alice",
Day: "2026-04-01",
AICreditsUsed: Ptr(12.5),
UserInitiatedInteractionCount: Ptr(5),
CopilotMetricsChatPanel: CopilotMetricsChatPanel{
ChatPanelEditMode: Ptr(2),
},
UsedChat: Ptr(true),
UsedCLI: Ptr(true),
UsedCopilotCodeReviewActive: Ptr(true),
AIAdoptionPhase: &CopilotMetricsAIAdoptionPhase{
Phase: "Phase 2",
PhaseNumber: 2,
Version: "v1",
},
TotalsByCLI: &CopilotMetricsCLI{
SessionCount: Ptr(2),
RequestCount: Ptr(2),
Expand Down Expand Up @@ -3398,7 +3442,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","user_initiated_interaction_count":3,"used_copilot_code_review_active":true}
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}}}
`)
})
Expand All @@ -3420,8 +3464,14 @@ func TestCopilotService_DownloadUserPeriodicMetrics(t *testing.T) {
Day: "2026-03-05",
UserID: 1,
UserLogin: "alice",
AICreditsUsed: Ptr(3.25),
UserInitiatedInteractionCount: Ptr(3),
UsedCopilotCodeReviewActive: Ptr(true),
AIAdoptionPhase: &CopilotMetricsAIAdoptionPhase{
Phase: "Phase 1",
PhaseNumber: 1,
Version: "v1",
},
},
{
ReportStartDay: "2026-03-05",
Expand Down
Loading
Loading