From b072652bf34f4ed3dd1c551180d64ae2b7b2ba83 Mon Sep 17 00:00:00 2001 From: ewega Date: Tue, 30 Jun 2026 19:47:47 +0300 Subject: [PATCH] feat(gh-copilot): ingest AI credit usage and add credit consumption dashboard panels Add first-class GitHub Copilot AI credit billing ingestion to the gh-copilot plugin and surface credit-consumption KPIs on the Copilot Adoption dashboard. - New tool model _tool_copilot_ai_credit_usage (+ migration, GetTablesInfo) - Enterprise + organization daily collectors and extractors for the Billing Usage AI credit endpoints (API version 2026-03-10), reusing the existing stateful daily-iterator pattern - Wire 4 new subtask metas (collect/extract x enterprise/org) - Add "AI Credit Consumption" row (stats, time series, by-model and by-product/SKU tables) to mysql + postgresql adoption dashboards - e2e fixtures + extractor unit tests - Docs: plugin README + dashboard README Resolves #8961 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- backend/plugins/gh-copilot/README.md | 14 + .../_raw_copilot_ai_credit_usage_org.csv | 2 + .../_tool_copilot_ai_credit_usage.csv | 3 + .../plugins/gh-copilot/e2e/metrics_test.go | 8 + .../gh-copilot/models/ai_credit_usage.go | 76 ++++ .../20260701_add_copilot_ai_credit_usage.go | 78 ++++ .../models/migrationscripts/register.go | 1 + backend/plugins/gh-copilot/models/models.go | 2 + .../tasks/ai_credit_usage_collector.go | 172 +++++++ .../tasks/ai_credit_usage_extractor.go | 162 +++++++ .../tasks/ai_credit_usage_extractor_test.go | 128 ++++++ backend/plugins/gh-copilot/tasks/register.go | 4 + backend/plugins/gh-copilot/tasks/subtasks.go | 34 ++ .../dashboards/mysql/GithubCopilotREADME.md | 2 + .../mysql/github-copilot-adoption.json | 2 +- .../postgresql/github-copilot-adoption.json | 423 ++++++++++++++++++ 16 files changed, 1110 insertions(+), 1 deletion(-) create mode 100644 backend/plugins/gh-copilot/e2e/metrics/raw_tables/_raw_copilot_ai_credit_usage_org.csv create mode 100644 backend/plugins/gh-copilot/e2e/metrics/snapshot_tables/_tool_copilot_ai_credit_usage.csv create mode 100644 backend/plugins/gh-copilot/models/ai_credit_usage.go create mode 100644 backend/plugins/gh-copilot/models/migrationscripts/20260701_add_copilot_ai_credit_usage.go create mode 100644 backend/plugins/gh-copilot/tasks/ai_credit_usage_collector.go create mode 100644 backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor.go create mode 100644 backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor_test.go diff --git a/backend/plugins/gh-copilot/README.md b/backend/plugins/gh-copilot/README.md index 1c4aeda2481..5d36e6aaec8 100644 --- a/backend/plugins/gh-copilot/README.md +++ b/backend/plugins/gh-copilot/README.md @@ -28,11 +28,22 @@ It follows the same structure/patterns as other DevLake data-source plugins (not - `GET /orgs/{org}/copilot/billing/seats` - `GET /orgs/{org}/copilot/metrics` +**AI credit usage endpoints** (GitHub Billing Usage REST API, version `2026-03-10`): + +- `GET /enterprises/{enterprise}/settings/billing/ai_credit/usage` +- `GET /organizations/{org}/settings/billing/ai_credit/usage` (note: `organizations/`, not `orgs/`) + +These are collected **daily** (one request per day, iterating `year`/`month`/`day`) and report Copilot +**AI credit consumption** broken down by product, SKU and model. Quantities and amounts are floats and the +`unit_type` is `ai-credits`. + **Stored data (tool layer)**: - `_tool_copilot_org_metrics` (daily aggregates) - `_tool_copilot_language_metrics` (editor/language breakdown) - `_tool_copilot_seats` (seat assignments) +- `_tool_copilot_ai_credit_usage` (daily AI credit consumption: gross/net quantity and amount per + product/sku/model, at `enterprise` or `organization` level) ## Data flow (high level) @@ -61,6 +72,9 @@ flowchart LR - GitHub Copilot Business or Enterprise enabled for the target organization - A token that can access GitHub Copilot billing/metrics (classic PAT with `manage_billing:copilot` works) +- For **AI credit usage**, the token additionally needs billing read access: + `manage_billing:copilot` (or `admin:enterprise` for the enterprise endpoint / `admin:org` for the + organization endpoint) ### 1) Create a connection diff --git a/backend/plugins/gh-copilot/e2e/metrics/raw_tables/_raw_copilot_ai_credit_usage_org.csv b/backend/plugins/gh-copilot/e2e/metrics/raw_tables/_raw_copilot_ai_credit_usage_org.csv new file mode 100644 index 00000000000..6d59ff43bbd --- /dev/null +++ b/backend/plugins/gh-copilot/e2e/metrics/raw_tables/_raw_copilot_ai_credit_usage_org.csv @@ -0,0 +1,2 @@ +id,params,data,url,input,created_at +1,"{""ConnectionId"": 1, ""ScopeId"": ""octodemo"", ""Organization"": ""octodemo"", ""Endpoint"": ""https://api.github.com""}","{""timePeriod"": {""year"": 2026, ""month"": 6, ""day"": 8}, ""organization"": ""octodemo"", ""usageItems"": [{""product"": ""Copilot"", ""sku"": ""Copilot AI Credits"", ""model"": ""Claude Opus 4.8"", ""unitType"": ""ai-credits"", ""pricePerUnit"": 0.01, ""grossQuantity"": 5445.77595, ""grossAmount"": 54.4577595, ""discountQuantity"": 5445.77595, ""discountAmount"": 54.4577595, ""netQuantity"": 0.0, ""netAmount"": 0.0}, {""product"": ""Copilot"", ""sku"": ""Copilot AI Credits"", ""model"": ""Claude Sonnet 4.6"", ""unitType"": ""ai-credits"", ""pricePerUnit"": 0.01, ""grossQuantity"": 176.97417, ""grossAmount"": 1.7697417, ""discountQuantity"": 175.3533, ""discountAmount"": 1.753533, ""netQuantity"": 1.62087, ""netAmount"": 0.0162087}]}",https://api.github.com/organizations/octodemo/settings/billing/ai_credit/usage?year=2026&month=6&day=8,null,2026-06-09 00:00:00.000 diff --git a/backend/plugins/gh-copilot/e2e/metrics/snapshot_tables/_tool_copilot_ai_credit_usage.csv b/backend/plugins/gh-copilot/e2e/metrics/snapshot_tables/_tool_copilot_ai_credit_usage.csv new file mode 100644 index 00000000000..3fbe908d9dd --- /dev/null +++ b/backend/plugins/gh-copilot/e2e/metrics/snapshot_tables/_tool_copilot_ai_credit_usage.csv @@ -0,0 +1,3 @@ +connection_id,scope_id,level,year,month,day,product,sku,model,unit_type,cost_center_id,date,enterprise,organization,user_login,cost_center_name,price_per_unit,gross_quantity,gross_amount,discount_quantity,discount_amount,net_quantity,net_amount +1,octodemo,organization,2026,6,8,Copilot,Copilot AI Credits,Claude Opus 4.8,ai-credits,,2026-06-08T00:00:00.000+00:00,,octodemo,,,0.01,5445.77595,54.4577595,5445.77595,54.4577595,0,0 +1,octodemo,organization,2026,6,8,Copilot,Copilot AI Credits,Claude Sonnet 4.6,ai-credits,,2026-06-08T00:00:00.000+00:00,,octodemo,,,0.01,176.97417,1.7697417,175.3533,1.753533,1.62087,0.0162087 diff --git a/backend/plugins/gh-copilot/e2e/metrics_test.go b/backend/plugins/gh-copilot/e2e/metrics_test.go index 2f339f95d16..03b9c265d92 100644 --- a/backend/plugins/gh-copilot/e2e/metrics_test.go +++ b/backend/plugins/gh-copilot/e2e/metrics_test.go @@ -60,13 +60,16 @@ func TestCopilotMetricsDataFlow(t *testing.T) { dataflowTester.ImportCsvIntoRawTable("./metrics/raw_tables/_raw_copilot_metrics.csv", "_raw_copilot_org_metrics") dataflowTester.ImportCsvIntoRawTable("./metrics/raw_tables/_raw_copilot_seats.csv", "_raw_copilot_seats") + dataflowTester.ImportCsvIntoRawTable("./metrics/raw_tables/_raw_copilot_ai_credit_usage_org.csv", "_raw_copilot_ai_credit_usage_org") dataflowTester.FlushTabler(&models.GhCopilotSeat{}) dataflowTester.FlushTabler(&models.GhCopilotEnterpriseDailyMetrics{}) dataflowTester.FlushTabler(&models.GhCopilotMetricsByLanguageFeature{}) + dataflowTester.FlushTabler(&models.GhCopilotAiCreditUsage{}) dataflowTester.Subtask(tasks.ExtractSeatsMeta, taskData) dataflowTester.Subtask(tasks.ExtractOrgMetricsMeta, taskData) + dataflowTester.Subtask(tasks.ExtractOrgAiCreditUsageMeta, taskData) dataflowTester.VerifyTableWithOptions(&models.GhCopilotEnterpriseDailyMetrics{}, e2ehelper.TableOptions{ CSVRelPath: "./metrics/snapshot_tables/_tool_copilot_enterprise_daily_metrics.csv", @@ -84,4 +87,9 @@ func TestCopilotMetricsDataFlow(t *testing.T) { CSVRelPath: "./metrics/snapshot_tables/_tool_copilot_metrics_by_language_feature.csv", IgnoreTypes: []interface{}{common.NoPKModel{}}, }) + + dataflowTester.VerifyTableWithOptions(&models.GhCopilotAiCreditUsage{}, e2ehelper.TableOptions{ + CSVRelPath: "./metrics/snapshot_tables/_tool_copilot_ai_credit_usage.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) } diff --git a/backend/plugins/gh-copilot/models/ai_credit_usage.go b/backend/plugins/gh-copilot/models/ai_credit_usage.go new file mode 100644 index 00000000000..6cb794080ff --- /dev/null +++ b/backend/plugins/gh-copilot/models/ai_credit_usage.go @@ -0,0 +1,76 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package models + +import ( + "time" + + "github.com/apache/incubator-devlake/core/models/common" +) + +// AI credit usage "level" identifies which billing endpoint a usage row came from. +const ( + AiCreditUsageLevelEnterprise = "enterprise" + AiCreditUsageLevelOrganization = "organization" +) + +// GhCopilotAiCreditUsage stores a single AI credit billing usage line item for a +// time period, sourced from the GitHub Billing Usage "AI credit usage" reports +// (enterprise and organization endpoints). Each row corresponds to one usageItem +// (a product/sku/model/unitType combination) within a single day. +type GhCopilotAiCreditUsage struct { + ConnectionId uint64 `gorm:"primaryKey" json:"connectionId"` + ScopeId string `gorm:"primaryKey;type:varchar(255)" json:"scopeId"` + // Level distinguishes enterprise vs organization sourced usage. + Level string `gorm:"primaryKey;type:varchar(20)" json:"level"` + // Time period of the usage line (daily granularity). + Year int `gorm:"primaryKey" json:"year"` + Month int `gorm:"primaryKey" json:"month"` + Day int `gorm:"primaryKey" json:"day"` + // Usage line dimensions. + Product string `gorm:"primaryKey;type:varchar(100)" json:"product"` + Sku string `gorm:"primaryKey;type:varchar(150)" json:"sku"` + Model string `gorm:"primaryKey;type:varchar(150)" json:"model"` + UnitType string `gorm:"primaryKey;type:varchar(50)" json:"unitType"` + CostCenterId string `gorm:"primaryKey;type:varchar(100)" json:"costCenterId"` + + // Date is the time period expressed as a date (year-month-day) for easier + // time-series querying in dashboards. + Date time.Time `gorm:"type:date;index" json:"date"` + + // Top-level context (may be empty for the unfiltered aggregate report). + Enterprise string `gorm:"type:varchar(100)" json:"enterprise"` + Organization string `gorm:"type:varchar(100)" json:"organization"` + UserLogin string `gorm:"type:varchar(255)" json:"userLogin"` + CostCenterName string `gorm:"type:varchar(255)" json:"costCenterName"` + + // Billing metrics. GitHub returns fractional values, so these are float64. + PricePerUnit float64 `json:"pricePerUnit" gorm:"comment:Price per unit for the line item"` + GrossQuantity float64 `json:"grossQuantity" gorm:"comment:Gross quantity consumed (e.g. credits)"` + GrossAmount float64 `json:"grossAmount" gorm:"comment:Gross billed amount"` + DiscountQuantity float64 `json:"discountQuantity" gorm:"comment:Discounted quantity"` + DiscountAmount float64 `json:"discountAmount" gorm:"comment:Discounted amount"` + NetQuantity float64 `json:"netQuantity" gorm:"comment:Net quantity after discounts"` + NetAmount float64 `json:"netAmount" gorm:"comment:Net billed amount after discounts"` + + common.NoPKModel +} + +func (GhCopilotAiCreditUsage) TableName() string { + return "_tool_copilot_ai_credit_usage" +} diff --git a/backend/plugins/gh-copilot/models/migrationscripts/20260701_add_copilot_ai_credit_usage.go b/backend/plugins/gh-copilot/models/migrationscripts/20260701_add_copilot_ai_credit_usage.go new file mode 100644 index 00000000000..7c8f946da93 --- /dev/null +++ b/backend/plugins/gh-copilot/models/migrationscripts/20260701_add_copilot_ai_credit_usage.go @@ -0,0 +1,78 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package migrationscripts + +import ( + "time" + + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/helpers/migrationhelper" +) + +type addCopilotAiCreditUsage struct{} + +type aiCreditUsage20260701 struct { + ConnectionId uint64 `gorm:"primaryKey"` + ScopeId string `gorm:"primaryKey;type:varchar(255)"` + Level string `gorm:"primaryKey;type:varchar(20)"` + Year int `gorm:"primaryKey"` + Month int `gorm:"primaryKey"` + Day int `gorm:"primaryKey"` + Product string `gorm:"primaryKey;type:varchar(100)"` + Sku string `gorm:"primaryKey;type:varchar(150)"` + Model string `gorm:"primaryKey;type:varchar(150)"` + UnitType string `gorm:"primaryKey;type:varchar(50)"` + CostCenterId string `gorm:"primaryKey;type:varchar(100)"` + + Date time.Time `gorm:"type:date;index"` + + Enterprise string `gorm:"type:varchar(100)"` + Organization string `gorm:"type:varchar(100)"` + UserLogin string `gorm:"type:varchar(255)"` + CostCenterName string `gorm:"type:varchar(255)"` + + PricePerUnit float64 + GrossQuantity float64 + GrossAmount float64 + DiscountQuantity float64 + DiscountAmount float64 + NetQuantity float64 + NetAmount float64 + + archived.NoPKModel +} + +func (aiCreditUsage20260701) TableName() string { + return "_tool_copilot_ai_credit_usage" +} + +func (script *addCopilotAiCreditUsage) Up(basicRes context.BasicRes) errors.Error { + return migrationhelper.AutoMigrateTables(basicRes, + &aiCreditUsage20260701{}, + ) +} + +func (*addCopilotAiCreditUsage) Version() uint64 { + return 20260701000000 +} + +func (*addCopilotAiCreditUsage) Name() string { + return "Add Copilot AI credit usage table" +} diff --git a/backend/plugins/gh-copilot/models/migrationscripts/register.go b/backend/plugins/gh-copilot/models/migrationscripts/register.go index 399735695e0..0529532e288 100644 --- a/backend/plugins/gh-copilot/models/migrationscripts/register.go +++ b/backend/plugins/gh-copilot/models/migrationscripts/register.go @@ -31,5 +31,6 @@ func All() []plugin.MigrationScript { new(addPRFieldsToEnterpriseMetrics), new(addOrganizationIdToUserMetrics), new(addCopilotMetricsGaps), + new(addCopilotAiCreditUsage), } } diff --git a/backend/plugins/gh-copilot/models/models.go b/backend/plugins/gh-copilot/models/models.go index 5143ce5f8b7..2e43a1af6dd 100644 --- a/backend/plugins/gh-copilot/models/models.go +++ b/backend/plugins/gh-copilot/models/models.go @@ -47,5 +47,7 @@ func GetTablesInfo() []dal.Tabler { &GhCopilotSeat{}, // User-team mappings &GhCopilotUserTeam{}, + // AI credit billing usage + &GhCopilotAiCreditUsage{}, } } diff --git a/backend/plugins/gh-copilot/tasks/ai_credit_usage_collector.go b/backend/plugins/gh-copilot/tasks/ai_credit_usage_collector.go new file mode 100644 index 00000000000..dded8f4f2ea --- /dev/null +++ b/backend/plugins/gh-copilot/tasks/ai_credit_usage_collector.go @@ -0,0 +1,172 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tasks + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/log" + "github.com/apache/incubator-devlake/core/plugin" + helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + "github.com/apache/incubator-devlake/plugins/gh-copilot/models" +) + +const ( + rawEnterpriseAiCreditUsageTable = "copilot_ai_credit_usage_enterprise" + rawOrgAiCreditUsageTable = "copilot_ai_credit_usage_org" +) + +// CollectEnterpriseAiCreditUsage collects enterprise-level daily AI credit billing usage. +func CollectEnterpriseAiCreditUsage(taskCtx plugin.SubTaskContext) errors.Error { + return collectAiCreditUsage(taskCtx, models.AiCreditUsageLevelEnterprise) +} + +// CollectOrgAiCreditUsage collects organization-level daily AI credit billing usage. +func CollectOrgAiCreditUsage(taskCtx plugin.SubTaskContext) errors.Error { + return collectAiCreditUsage(taskCtx, models.AiCreditUsageLevelOrganization) +} + +// collectAiCreditUsage iterates day-by-day over the GitHub Billing Usage +// "AI credit usage" endpoint, passing year/month/day query parameters, and stores +// each daily report as a raw record. Shared by the enterprise and organization subtasks. +func collectAiCreditUsage(taskCtx plugin.SubTaskContext, level string) errors.Error { + data, ok := taskCtx.TaskContext().GetData().(*GhCopilotTaskData) + if !ok { + return errors.Default.New("task data is not GhCopilotTaskData") + } + connection := data.Connection + connection.Normalize() + logger := taskCtx.GetLogger() + + var urlTemplate, rawTable string + switch level { + case models.AiCreditUsageLevelEnterprise: + if !connection.HasEnterprise() { + logger.Info("No enterprise configured, skipping enterprise AI credit usage collection") + return nil + } + urlTemplate = fmt.Sprintf("enterprises/%s/settings/billing/ai_credit/usage", connection.Enterprise) + rawTable = rawEnterpriseAiCreditUsageTable + case models.AiCreditUsageLevelOrganization: + if connection.Organization == "" { + logger.Info("No organization configured, skipping org AI credit usage collection") + return nil + } + // Note: the billing usage endpoint uses "organizations/{org}", not "orgs/{org}". + urlTemplate = fmt.Sprintf("organizations/%s/settings/billing/ai_credit/usage", connection.Organization) + rawTable = rawOrgAiCreditUsageTable + default: + return errors.Default.New(fmt.Sprintf("unknown AI credit usage level: %s", level)) + } + + apiClient, err := CreateApiClient(taskCtx.TaskContext(), connection) + if err != nil { + return err + } + + rawArgs := helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Table: rawTable, + Options: copilotRawParams{ + ConnectionId: data.Options.ConnectionId, + ScopeId: data.Options.ScopeId, + Organization: connection.Organization, + Endpoint: connection.Endpoint, + }, + } + + collector, err := helper.NewStatefulApiCollector(rawArgs) + if err != nil { + return err + } + + now := time.Now().UTC() + start, until := computeReportDateRange(now, collector.GetSince()) + start = clampDailyMetricsStartForBackfill(start, until) + + dayIter := newDayIterator(start, until) + + err = collector.InitCollector(helper.ApiCollectorArgs{ + ApiClient: apiClient, + Input: dayIter, + UrlTemplate: urlTemplate, + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + input := reqData.Input.(*dayInput) + y, m, d, parseErr := splitDay(input.Day) + if parseErr != nil { + return nil, parseErr + } + q := url.Values{} + q.Set("year", strconv.Itoa(y)) + q.Set("month", strconv.Itoa(m)) + q.Set("day", strconv.Itoa(d)) + return q, nil + }, + Incremental: true, + Concurrency: 1, + AfterResponse: ignoreNoContent, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + return parseAiCreditUsageResponse(res, logger) + }, + }) + if err != nil { + return err + } + return collector.Execute() +} + +// splitDay parses a YYYY-MM-DD string into year, month and day integers. +func splitDay(day string) (int, int, int, errors.Error) { + t, parseErr := time.Parse("2006-01-02", day) + if parseErr != nil { + return 0, 0, 0, errors.Default.Wrap(parseErr, fmt.Sprintf("invalid day %q", day)) + } + return t.Year(), int(t.Month()), t.Day(), nil +} + +// parseAiCreditUsageResponse reads the single JSON report object returned for one day. +// Empty bodies (no usage) are skipped; the whole object is stored as one raw record +// and exploded into per-usageItem rows by the extractor. +func parseAiCreditUsageResponse(res *http.Response, logger log.Logger) ([]json.RawMessage, errors.Error) { + body, readErr := io.ReadAll(res.Body) + res.Body.Close() + if readErr != nil { + return nil, errors.Default.Wrap(readErr, "failed to read AI credit usage response") + } + if isEmptyReport(body) { + return nil, nil + } + trimmed := bytes.TrimSpace(body) + // Defensive: only forward objects that actually carry usage items. + if !strings.Contains(string(trimmed), "usageItems") { + if logger != nil { + logger.Info("AI credit usage response had no usageItems, skipping") + } + return nil, nil + } + return []json.RawMessage{json.RawMessage(trimmed)}, nil +} diff --git a/backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor.go b/backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor.go new file mode 100644 index 00000000000..f94cdf600b4 --- /dev/null +++ b/backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor.go @@ -0,0 +1,162 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tasks + +import ( + "encoding/json" + "time" + + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/plugin" + helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + "github.com/apache/incubator-devlake/plugins/gh-copilot/models" +) + +// aiCreditUsageReport mirrors the GitHub Billing Usage "AI credit usage" response. +type aiCreditUsageReport struct { + TimePeriod struct { + Year int `json:"year"` + Month int `json:"month"` + Day int `json:"day"` + } `json:"timePeriod"` + Enterprise string `json:"enterprise"` + Organization string `json:"organization"` + User string `json:"user"` + CostCenter *struct { + Id string `json:"id"` + Name string `json:"name"` + } `json:"costCenter"` + UsageItems []aiCreditUsageItem `json:"usageItems"` +} + +type aiCreditUsageItem struct { + Product string `json:"product"` + Sku string `json:"sku"` + Model string `json:"model"` + UnitType string `json:"unitType"` + PricePerUnit float64 `json:"pricePerUnit"` + GrossQuantity float64 `json:"grossQuantity"` + GrossAmount float64 `json:"grossAmount"` + DiscountQuantity float64 `json:"discountQuantity"` + DiscountAmount float64 `json:"discountAmount"` + NetQuantity float64 `json:"netQuantity"` + NetAmount float64 `json:"netAmount"` +} + +// ExtractEnterpriseAiCreditUsage parses raw enterprise AI credit usage reports. +func ExtractEnterpriseAiCreditUsage(taskCtx plugin.SubTaskContext) errors.Error { + return extractAiCreditUsage(taskCtx, models.AiCreditUsageLevelEnterprise, rawEnterpriseAiCreditUsageTable) +} + +// ExtractOrgAiCreditUsage parses raw organization AI credit usage reports. +func ExtractOrgAiCreditUsage(taskCtx plugin.SubTaskContext) errors.Error { + return extractAiCreditUsage(taskCtx, models.AiCreditUsageLevelOrganization, rawOrgAiCreditUsageTable) +} + +// extractAiCreditUsage explodes each daily report's usageItems into one +// GhCopilotAiCreditUsage row per product/sku/model/unitType. Shared by the +// enterprise and organization extractor subtasks. +func extractAiCreditUsage(taskCtx plugin.SubTaskContext, level, rawTable string) errors.Error { + data, ok := taskCtx.TaskContext().GetData().(*GhCopilotTaskData) + if !ok { + return errors.Default.New("task data is not GhCopilotTaskData") + } + connection := data.Connection + connection.Normalize() + + params := copilotRawParams{ + ConnectionId: data.Options.ConnectionId, + ScopeId: data.Options.ScopeId, + Organization: connection.Organization, + Endpoint: connection.Endpoint, + } + + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Table: rawTable, + Options: params, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + rows, err := mapAiCreditUsageReport(level, data.Options.ConnectionId, data.Options.ScopeId, row.Data) + if err != nil { + return nil, err + } + results := make([]interface{}, len(rows)) + for i, r := range rows { + results[i] = r + } + return results, nil + }, + }) + if err != nil { + return err + } + return extractor.Execute() +} + +// mapAiCreditUsageReport unmarshals a single AI credit usage report and explodes its +// usageItems into one GhCopilotAiCreditUsage row per product/sku/model/unitType. +func mapAiCreditUsageReport(level string, connectionId uint64, scopeId string, raw []byte) ([]*models.GhCopilotAiCreditUsage, errors.Error) { + var report aiCreditUsageReport + if err := errors.Convert(json.Unmarshal(raw, &report)); err != nil { + return nil, err + } + + tp := report.TimePeriod + var date time.Time + if tp.Year > 0 && tp.Month > 0 && tp.Day > 0 { + date = time.Date(tp.Year, time.Month(tp.Month), tp.Day, 0, 0, 0, 0, time.UTC) + } + + costCenterId, costCenterName := "", "" + if report.CostCenter != nil { + costCenterId = report.CostCenter.Id + costCenterName = report.CostCenter.Name + } + + results := make([]*models.GhCopilotAiCreditUsage, 0, len(report.UsageItems)) + for _, item := range report.UsageItems { + results = append(results, &models.GhCopilotAiCreditUsage{ + ConnectionId: connectionId, + ScopeId: scopeId, + Level: level, + Year: tp.Year, + Month: tp.Month, + Day: tp.Day, + Product: item.Product, + Sku: item.Sku, + Model: item.Model, + UnitType: item.UnitType, + CostCenterId: costCenterId, + Date: date, + Enterprise: report.Enterprise, + Organization: report.Organization, + UserLogin: report.User, + CostCenterName: costCenterName, + PricePerUnit: item.PricePerUnit, + GrossQuantity: item.GrossQuantity, + GrossAmount: item.GrossAmount, + DiscountQuantity: item.DiscountQuantity, + DiscountAmount: item.DiscountAmount, + NetQuantity: item.NetQuantity, + NetAmount: item.NetAmount, + }) + } + return results, nil +} diff --git a/backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor_test.go b/backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor_test.go new file mode 100644 index 00000000000..8292f4fdd7b --- /dev/null +++ b/backend/plugins/gh-copilot/tasks/ai_credit_usage_extractor_test.go @@ -0,0 +1,128 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tasks + +import ( + "testing" + "time" + + "github.com/apache/incubator-devlake/plugins/gh-copilot/models" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSplitDay(t *testing.T) { + y, m, d, err := splitDay("2026-06-08") + require.Nil(t, err) + assert.Equal(t, 2026, y) + assert.Equal(t, 6, m) + assert.Equal(t, 8, d) + + _, _, _, err = splitDay("not-a-date") + assert.NotNil(t, err) +} + +func TestMapAiCreditUsageReport(t *testing.T) { + raw := []byte(`{ + "timePeriod": {"year": 2026, "month": 6, "day": 8}, + "enterprise": "Avocado Corp", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "Claude Opus 4.8", + "unitType": "ai-credits", + "pricePerUnit": 0.01, + "grossQuantity": 5445.77595, + "grossAmount": 54.4577595, + "discountQuantity": 5445.77595, + "discountAmount": 54.4577595, + "netQuantity": 0.0, + "netAmount": 0.0 + }, + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "Claude Sonnet 4.6", + "unitType": "ai-credits", + "pricePerUnit": 0.01, + "grossQuantity": 176.97417, + "grossAmount": 1.7697417, + "discountQuantity": 175.3533, + "discountAmount": 1.753533, + "netQuantity": 1.62087, + "netAmount": 0.0162087 + } + ] + }`) + + rows, err := mapAiCreditUsageReport(models.AiCreditUsageLevelEnterprise, 1, "avocado", raw) + require.Nil(t, err) + require.Len(t, rows, 2) + + first := rows[0] + assert.Equal(t, uint64(1), first.ConnectionId) + assert.Equal(t, "avocado", first.ScopeId) + assert.Equal(t, models.AiCreditUsageLevelEnterprise, first.Level) + assert.Equal(t, 2026, first.Year) + assert.Equal(t, 6, first.Month) + assert.Equal(t, 8, first.Day) + assert.Equal(t, time.Date(2026, 6, 8, 0, 0, 0, 0, time.UTC), first.Date) + assert.Equal(t, "Avocado Corp", first.Enterprise) + assert.Equal(t, "Claude Opus 4.8", first.Model) + assert.Equal(t, "ai-credits", first.UnitType) + assert.InDelta(t, 5445.77595, first.GrossQuantity, 1e-9) + assert.InDelta(t, 0.0, first.NetQuantity, 1e-9) + + second := rows[1] + assert.Equal(t, "Claude Sonnet 4.6", second.Model) + assert.InDelta(t, 1.62087, second.NetQuantity, 1e-9) + assert.InDelta(t, 0.0162087, second.NetAmount, 1e-9) +} + +func TestMapAiCreditUsageReportWithCostCenter(t *testing.T) { + raw := []byte(`{ + "timePeriod": {"year": 2025, "month": 6, "day": 10}, + "enterprise": "acme", + "organization": "platform", + "user": "octocat", + "costCenter": {"id": "cc-1", "name": "Engineering"}, + "usageItems": [ + {"product": "Copilot", "sku": "AI-CREDITS", "model": "gpt-4.1", "unitType": "credits", + "pricePerUnit": 1, "grossQuantity": 10, "grossAmount": 10, "discountQuantity": 0, + "discountAmount": 0, "netQuantity": 10, "netAmount": 10} + ] + }`) + + rows, err := mapAiCreditUsageReport(models.AiCreditUsageLevelOrganization, 2, "platform", raw) + require.Nil(t, err) + require.Len(t, rows, 1) + + row := rows[0] + assert.Equal(t, "cc-1", row.CostCenterId) + assert.Equal(t, "Engineering", row.CostCenterName) + assert.Equal(t, "platform", row.Organization) + assert.Equal(t, "octocat", row.UserLogin) + assert.InDelta(t, 10.0, row.NetAmount, 1e-9) +} + +func TestMapAiCreditUsageReportEmpty(t *testing.T) { + rows, err := mapAiCreditUsageReport(models.AiCreditUsageLevelEnterprise, 1, "x", []byte(`{"timePeriod":{"year":2026,"month":6,"day":8},"usageItems":[]}`)) + require.Nil(t, err) + assert.Len(t, rows, 0) +} diff --git a/backend/plugins/gh-copilot/tasks/register.go b/backend/plugins/gh-copilot/tasks/register.go index 3c7e5b1eeb9..fe6724cf125 100644 --- a/backend/plugins/gh-copilot/tasks/register.go +++ b/backend/plugins/gh-copilot/tasks/register.go @@ -28,11 +28,15 @@ func GetSubTaskMetas() []plugin.SubTaskMeta { CollectEnterpriseMetricsMeta, CollectUserMetricsMeta, CollectUserTeamsMeta, + CollectEnterpriseAiCreditUsageMeta, + CollectOrgAiCreditUsageMeta, // Extractors ExtractSeatsMeta, ExtractOrgMetricsMeta, ExtractEnterpriseMetricsMeta, ExtractUserMetricsMeta, ExtractUserTeamsMeta, + ExtractEnterpriseAiCreditUsageMeta, + ExtractOrgAiCreditUsageMeta, } } diff --git a/backend/plugins/gh-copilot/tasks/subtasks.go b/backend/plugins/gh-copilot/tasks/subtasks.go index 61ed5799525..fd58766d5df 100644 --- a/backend/plugins/gh-copilot/tasks/subtasks.go +++ b/backend/plugins/gh-copilot/tasks/subtasks.go @@ -105,3 +105,37 @@ var ExtractUserTeamsMeta = plugin.SubTaskMeta{ Description: "Extract Copilot user-team mappings into tool-layer table", Dependencies: []*plugin.SubTaskMeta{&CollectUserTeamsMeta}, } + +var CollectEnterpriseAiCreditUsageMeta = plugin.SubTaskMeta{ + Name: "collectEnterpriseAiCreditUsage", + EntryPoint: CollectEnterpriseAiCreditUsage, + EnabledByDefault: true, + DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS}, + Description: "Collect GitHub Copilot enterprise AI credit billing usage", +} + +var CollectOrgAiCreditUsageMeta = plugin.SubTaskMeta{ + Name: "collectOrgAiCreditUsage", + EntryPoint: CollectOrgAiCreditUsage, + EnabledByDefault: true, + DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS}, + Description: "Collect GitHub Copilot organization AI credit billing usage", +} + +var ExtractEnterpriseAiCreditUsageMeta = plugin.SubTaskMeta{ + Name: "extractEnterpriseAiCreditUsage", + EntryPoint: ExtractEnterpriseAiCreditUsage, + EnabledByDefault: true, + DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS}, + Description: "Extract Copilot enterprise AI credit usage into tool-layer table", + Dependencies: []*plugin.SubTaskMeta{&CollectEnterpriseAiCreditUsageMeta}, +} + +var ExtractOrgAiCreditUsageMeta = plugin.SubTaskMeta{ + Name: "extractOrgAiCreditUsage", + EntryPoint: ExtractOrgAiCreditUsage, + EnabledByDefault: true, + DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS}, + Description: "Extract Copilot organization AI credit usage into tool-layer table", + Dependencies: []*plugin.SubTaskMeta{&CollectOrgAiCreditUsageMeta}, +} diff --git a/grafana/dashboards/mysql/GithubCopilotREADME.md b/grafana/dashboards/mysql/GithubCopilotREADME.md index 642ff425296..92f42482458 100644 --- a/grafana/dashboards/mysql/GithubCopilotREADME.md +++ b/grafana/dashboards/mysql/GithubCopilotREADME.md @@ -30,6 +30,8 @@ Tracks GitHub Copilot usage metrics across your organization: - IDE distribution (VS Code, JetBrains, Neovim, etc.) - Acceptance rates for code suggestions - Chat and PR summary feature usage +- **AI Credit Consumption**: gross/net credits and billed amount over time, broken down by model and + product/SKU (sourced from `_tool_copilot_ai_credit_usage`) ### 2. GitHub Copilot Impact Dashboard (`GithubCopilotImpact.json`) diff --git a/grafana/dashboards/mysql/github-copilot-adoption.json b/grafana/dashboards/mysql/github-copilot-adoption.json index e018b6ede64..8c83471201d 100644 --- a/grafana/dashboards/mysql/github-copilot-adoption.json +++ b/grafana/dashboards/mysql/github-copilot-adoption.json @@ -1 +1 @@ -{"annotations":{"list":[{"builtIn":1,"datasource":{"type":"datasource","uid":"grafana"},"enable":true,"hide":true,"iconColor":"rgba(0, 211, 255, 1)","name":"Annotations & Alerts","type":"dashboard"}]},"editable":true,"fiscalYearStartMonth":0,"graphTooltip":0,"links":[],"liveNow":false,"panels":[{"datasource":"mysql","description":"Latest daily active users count","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":50},{"color":"red","value":100}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":0},"id":1,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT daily_active_users as \"Daily Active Users\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY day DESC LIMIT 1","refId":"A"}],"title":"Daily Active Users","type":"stat"},{"datasource":"mysql","description":"Code acceptance rate across the selected time range","fieldConfig":{"defaults":{"mappings":[],"min":0,"max":100,"unit":"percent","thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"yellow","value":20},{"color":"green","value":40}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":0},"id":2,"options":{"minVizHeight":75,"minVizWidth":75,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showThresholdLabels":false,"showThresholdMarkers":true,"sizing":"auto"},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Code Acceptance Rate","type":"gauge"},{"datasource":"mysql","description":"Total lines of code added by Copilot in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":12,"y":0},"id":3,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(loc_added_sum) as \"Lines Added\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Lines of Code Added","type":"stat"},{"datasource":"mysql","description":"Total pull requests created by Copilot in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"blue","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":18,"y":0},"id":4,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(pr_total_created_by_copilot) as \"PRs by Copilot\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"PRs Created by Copilot","type":"stat"},{"datasource":"mysql","description":"Latest weekly active users count","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":4},"id":15,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Weekly Active Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE day >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)\n AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n","refId":"A"}],"title":"Weekly Active Users (WAU)","type":"stat"},{"datasource":"mysql","description":"Latest monthly active users count","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":4},"id":16,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Monthly Active Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE day >= DATE_SUB(CURDATE(), INTERVAL 28 DAY)\n AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n","refId":"A"}],"title":"Monthly Active Users (MAU)","type":"stat"},{"datasource":"mysql","description":"Total pull requests reviewed by Copilot in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"blue","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":12,"y":4},"id":17,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(pr_total_reviewed_by_copilot) as \"PRs Reviewed by Copilot\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"PRs Reviewed by Copilot","type":"stat"},{"datasource":"mysql","description":"Total user-initiated interactions in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"purple","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":18,"y":4},"id":18,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(user_initiated_interaction_count) as \"User-Initiated Interactions\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"User-Initiated Interactions","type":"stat"},{"datasource":"mysql","description":"Daily and monthly active users over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Users","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":8},"id":5,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, daily_active_users as \"Daily Active\", monthly_active_users as \"Monthly Active\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"Active Users Over Time","type":"timeseries"},{"datasource":"mysql","description":"Code suggestions generated vs accepted over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Count","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":8},"id":6,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, code_generation_activity_count as \"Suggestions\", code_acceptance_activity_count as \"Acceptances\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"Code Suggestions & Acceptances","type":"timeseries"},{"datasource":"mysql","description":"Lines of code suggested to add versus lines added over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Lines of Code","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":16},"id":19,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, loc_suggested_to_add_sum as \"LOC Suggested\", loc_added_sum as \"LOC Added\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"LOC Suggested vs LOC Added Over Time","type":"timeseries"},{"datasource":"mysql","description":"Pull request activity over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Pull Requests","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":16},"id":20,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, pr_total_created as \"PRs Created\", pr_total_created_by_copilot as \"PRs Created by Copilot\", pr_total_reviewed as \"PRs Reviewed\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"PR Activity Over Time","type":"timeseries"},{"datasource":"mysql","description":"Weekly user-initiated interactions by top Copilot features","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Interactions","axisPlacement":"auto","barAlignment":0,"drawStyle":"bars","fillOpacity":80,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":0,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"mode":"normal","group":"A"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":24},"id":7,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"},"stacking":{"mode":"normal","group":"A"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT DATE(DATE_SUB(day, INTERVAL WEEKDAY(day) DAY)) as time,\n CASE\n WHEN feature IN (\n SELECT feature FROM (\n SELECT feature\n FROM _tool_copilot_metrics_by_feature\n WHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n GROUP BY feature\n HAVING SUM(user_initiated_interaction_count) > 0\n ORDER BY SUM(user_initiated_interaction_count) DESC\n LIMIT 4\n ) top_features\n ) THEN REPLACE(REPLACE(feature, 'chat_panel_', ''), '_mode', '')\n ELSE 'Other'\n END as metric,\n SUM(user_initiated_interaction_count) as value\nFROM _tool_copilot_metrics_by_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY time, metric\nORDER BY time","refId":"A"}],"title":"Feature Mix Over Time","type":"timeseries","transformations":[{"id":"prepareTimeSeries","options":{"format":"many"}},{"id":"renameByRegex","options":{"regex":"^value (.+)$","renamePattern":"$1"}}]},{"datasource":"mysql","description":"Weekly user-initiated interactions by top IDEs (Top 5 + Other)","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Weekly Interactions","axisPlacement":"auto","barAlignment":0,"drawStyle":"bars","fillOpacity":80,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":0,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"mode":"normal","group":"A"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":24},"id":8,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"},"stacking":{"mode":"normal","group":"A"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT DATE(DATE_SUB(day, INTERVAL WEEKDAY(day) DAY)) as time,\n CASE\n WHEN ide IN (\n SELECT ide FROM (\n SELECT ide\n FROM _tool_copilot_metrics_by_ide\n WHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n GROUP BY ide\n HAVING SUM(user_initiated_interaction_count) > 0\n ORDER BY SUM(user_initiated_interaction_count) DESC\n LIMIT 4\n ) top_ides\n ) THEN ide\n ELSE 'Other'\n END as metric,\n SUM(user_initiated_interaction_count) as value\nFROM _tool_copilot_metrics_by_ide\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY time, metric\nORDER BY time","refId":"A"}],"title":"IDE Adoption Over Time","type":"timeseries","transformations":[{"id":"prepareTimeSeries","options":{"format":"many"}},{"id":"renameByRegex","options":{"regex":"^value (.+)$","renamePattern":"$1"}}]},{"datasource":"mysql","description":"Top languages ranked by code acceptance rate","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":40},"id":9,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT language as \"Language\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_language_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY language\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 15","refId":"A"}],"title":"Top Languages by Acceptance Rate","type":"table"},{"datasource":"mysql","description":"Top models ranked by code acceptance rate","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":40},"id":10,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT model as \"Model\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_model_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY model\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 10","refId":"A"}],"title":"Top Models by Acceptance Rate","type":"table"},{"datasource":"mysql","description":"Matrix-style breakdown of suggestions and acceptances by language and feature","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":24,"x":0,"y":48},"id":21,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT language as \"Language\",\n feature as \"Feature\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\"\nFROM _tool_copilot_metrics_by_language_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY language, feature\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY language, SUM(code_generation_activity_count) DESC\nLIMIT 100","refId":"A"}],"title":"Feature x Language Matrix","type":"table"},{"datasource":"mysql","description":"Count of distinct users active in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":56},"id":11,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Unique Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Unique Users (Period)","type":"stat"},{"datasource":"mysql","description":"Users who have used Copilot agent mode","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"purple","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":56},"id":12,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Agent Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}' AND used_agent = 1","refId":"A"}],"title":"Agent Mode Adopters","type":"stat"},{"datasource":"mysql","description":"Users who have used Copilot chat","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"orange","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":12,"y":56},"id":13,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Chat Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}' AND used_chat = 1","refId":"A"}],"title":"Chat Adopters","type":"stat"},{"datasource":"mysql","description":"Percentage of seats with recorded activity","fieldConfig":{"defaults":{"mappings":[],"min":0,"max":100,"unit":"percent","thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"yellow","value":50},{"color":"green","value":75}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":18,"y":56},"id":14,"options":{"minVizHeight":75,"minVizWidth":75,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showThresholdLabels":false,"showThresholdMarkers":true,"sizing":"auto"},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT\n COUNT(CASE WHEN last_activity_at IS NOT NULL THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0) as \"Utilization %\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id}","refId":"A"}],"title":"Seat Utilization","type":"gauge"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":60},"id":22,"panels":[],"title":"Quality & Efficiency Ratios","type":"row"},{"datasource":"mysql","description":"Daily acceptance rate trend computed as acceptances divided by suggestions","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"mappings":[],"max":1,"min":0,"unit":"percentunit","thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":61},"id":23,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time,\n code_acceptance_activity_count / NULLIF(code_generation_activity_count, 0) as \"Acceptance Rate\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"Overall Acceptance Rate Trend","type":"timeseries"},{"datasource":"mysql","description":"Daily LOC yield trend computed as added LOC divided by suggested LOC","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"mappings":[],"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":61},"id":24,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time,\n ROUND(loc_added_sum / NULLIF(loc_suggested_to_add_sum, 0), 2) as \"LOC Yield\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"LOC Yield Trend","type":"timeseries"},{"datasource":"mysql","description":"Acceptance ratio by Copilot feature","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":0,"y":69},"id":25,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT feature as \"Feature\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY feature\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 20","refId":"A"}],"title":"Acceptance Rate by Feature","type":"table"},{"datasource":"mysql","description":"Acceptance ratio by programming language","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":8,"y":69},"id":26,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT language as \"Language\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_language_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY language\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 20","refId":"A"}],"title":"Acceptance Rate by Language","type":"table"},{"datasource":"mysql","description":"Acceptance ratio by model","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":16,"y":69},"id":27,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT model as \"Model\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_model_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY model\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 20","refId":"A"}],"title":"Acceptance Rate by Model","type":"table"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":77},"id":28,"panels":[],"title":"User Behavior","type":"row"},{"datasource":"mysql","description":"Top users ranked by code generation activity in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":78},"id":29,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT user_login as \"user_login\",\n SUM(code_generation_activity_count) as \"suggestions\",\n SUM(code_acceptance_activity_count) as \"acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"acceptance %\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY user_login\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY SUM(code_generation_activity_count) DESC\nLIMIT 25","refId":"A"}],"title":"Top Users by Code Generations","type":"table"},{"datasource":"mysql","description":"Daily trend of distinct users engaging with agent mode versus chat","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Users","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":78},"id":30,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time,\n COUNT(DISTINCT CASE WHEN used_agent = 1 THEN user_login END) as \"Agent Users\",\n COUNT(DISTINCT CASE WHEN used_chat = 1 THEN user_login END) as \"Chat Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY day\nORDER BY 1","refId":"A"}],"title":"Agent Users vs Chat Users Trend","type":"timeseries"},{"datasource":"mysql","description":"First-seen daily trend of users becoming active for the first time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Users","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":24,"x":0,"y":86},"id":31,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT first_day as time,\n COUNT(*) as \"New Active Users\"\nFROM (\n SELECT user_login,\n MIN(day) as first_day\n FROM _tool_copilot_user_daily_metrics\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n GROUP BY user_login\n) first_seen\nWHERE $__timeFilter(first_day)\nGROUP BY first_day\nORDER BY 1","refId":"A"}],"title":"New Active Users per Day","type":"timeseries"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":94},"id":32,"panels":[],"title":"Seat Effectiveness","type":"row"},{"datasource":"mysql","description":"Distribution of seat activity by last recorded editor","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":0,"y":95},"id":33,"options":{"displayLabels":["name","percent"],"legend":{"displayMode":"list","placement":"right","showLegend":true},"pieType":"pie","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COALESCE(NULLIF(last_activity_editor, ''), 'unknown') as \"Editor\",\n COUNT(*) as \"Seats\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id} AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')\nGROUP BY COALESCE(NULLIF(last_activity_editor, ''), 'unknown')\nORDER BY COUNT(*) DESC","refId":"A"}],"title":"Seat Activity by Editor","type":"piechart"},{"datasource":"mysql","description":"Total, active (last 30 days), and inactive seats for the selected scope","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":8,"y":95},"id":34,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(*) as \"total_seats\",\n SUM(CASE WHEN last_activity_at IS NOT NULL AND last_activity_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as \"active_seats\",\n SUM(CASE WHEN last_activity_at IS NULL OR last_activity_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as \"inactive_seats\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id} AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')","refId":"A"}],"title":"Seats Summary","type":"table"},{"datasource":"mysql","description":"Seats with no activity or activity older than 30 days","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":16,"y":95},"id":35,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT user_login as \"user_login\",\n last_activity_at as \"last_activity_at\",\n plan_type as \"plan_type\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id}\n AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')\n AND (last_activity_at IS NULL OR last_activity_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY))\nORDER BY last_activity_at IS NULL DESC, last_activity_at ASC\nLIMIT 100","refId":"A"}],"title":"Inactive Seats","type":"table"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":103},"id":36,"panels":[],"title":"Diagnostics","type":"row"},{"datasource":"mysql","description":"Latest available day in enterprise-level Copilot metrics","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":104},"id":37,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COALESCE(TIMESTAMPDIFF(DAY, MAX(day), UTC_DATE()), 9999) as \"Days Since Latest Data\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Data Freshness","type":"stat"},{"datasource":"mysql","description":"Share of generated suggestions tied to unknown model/feature taxonomy","fieldConfig":{"defaults":{"mappings":[],"min":0,"max":100,"unit":"percent","thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":5},{"color":"red","value":20}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":104},"id":38,"options":{"minVizHeight":75,"minVizWidth":75,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showThresholdLabels":false,"showThresholdMarkers":true,"sizing":"auto"},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT ROUND(\n SUM(CASE WHEN LOWER(COALESCE(model, '')) = 'unknown' OR LOWER(COALESCE(feature, '')) LIKE '%unknown%' THEN code_generation_activity_count ELSE 0 END) * 100.0\n / NULLIF(SUM(code_generation_activity_count), 0),\n 2\n) as \"Unknown Taxonomy %\"\nFROM _tool_copilot_metrics_by_model_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Unknown Taxonomy Share","type":"gauge"},{"datasource":"mysql","description":"Row counts for core Copilot metrics tables","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":104},"id":39,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT *\nFROM (\n SELECT '_tool_copilot_enterprise_daily_metrics' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_enterprise_daily_metrics\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_user_daily_metrics' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_user_daily_metrics\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_metrics_by_feature' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_metrics_by_feature\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_metrics_by_model_feature' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_metrics_by_model_feature\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_seats' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_seats\n WHERE connection_id = ${connection_id} AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')\n) data_volume\nORDER BY row_count DESC","refId":"A"}],"title":"Data Volume by Table","type":"table"}],"refresh":"","schemaVersion":38,"tags":["copilot","devlake"],"templating":{"list":[{"current":{"selected":false,"text":"","value":""},"datasource":"mysql","definition":"SELECT DISTINCT connection_id FROM _tool_copilot_enterprise_daily_metrics ORDER BY 1","hide":0,"includeAll":false,"label":"Connection ID","multi":false,"name":"connection_id","options":[],"query":"SELECT DISTINCT connection_id FROM _tool_copilot_scopes ORDER BY connection_id DESC","refresh":1,"regex":"","skipUrlSync":false,"sort":0,"type":"query"},{"current":{"selected":false,"text":"","value":""},"datasource":"mysql","definition":"SELECT DISTINCT id as scope_id FROM _tool_copilot_scopes WHERE connection_id = ${connection_id} ORDER BY 1","hide":0,"includeAll":false,"label":"Scope ID","multi":false,"name":"scope_id","options":[],"query":"SELECT DISTINCT id as scope_id FROM _tool_copilot_scopes WHERE connection_id = ${connection_id} ORDER BY 1","refresh":2,"regex":"","skipUrlSync":false,"sort":0,"type":"query"}]},"time":{"from":"now-90d","to":"now"},"timepicker":{},"timezone":"utc","title":"GitHub Copilot Adoption","uid":"copilot_adoption","version":1,"weekStart":""} \ No newline at end of file +{"annotations":{"list":[{"builtIn":1,"datasource":{"type":"datasource","uid":"grafana"},"enable":true,"hide":true,"iconColor":"rgba(0, 211, 255, 1)","name":"Annotations & Alerts","type":"dashboard"}]},"editable":true,"fiscalYearStartMonth":0,"graphTooltip":0,"links":[],"liveNow":false,"panels":[{"datasource":"mysql","description":"Latest daily active users count","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":50},{"color":"red","value":100}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":0},"id":1,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT daily_active_users as \"Daily Active Users\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY day DESC LIMIT 1","refId":"A"}],"title":"Daily Active Users","type":"stat"},{"datasource":"mysql","description":"Code acceptance rate across the selected time range","fieldConfig":{"defaults":{"mappings":[],"min":0,"max":100,"unit":"percent","thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"yellow","value":20},{"color":"green","value":40}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":0},"id":2,"options":{"minVizHeight":75,"minVizWidth":75,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showThresholdLabels":false,"showThresholdMarkers":true,"sizing":"auto"},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Code Acceptance Rate","type":"gauge"},{"datasource":"mysql","description":"Total lines of code added by Copilot in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":12,"y":0},"id":3,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(loc_added_sum) as \"Lines Added\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Lines of Code Added","type":"stat"},{"datasource":"mysql","description":"Total pull requests created by Copilot in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"blue","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":18,"y":0},"id":4,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(pr_total_created_by_copilot) as \"PRs by Copilot\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"PRs Created by Copilot","type":"stat"},{"datasource":"mysql","description":"Latest weekly active users count","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":4},"id":15,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Weekly Active Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE day >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)\n AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n","refId":"A"}],"title":"Weekly Active Users (WAU)","type":"stat"},{"datasource":"mysql","description":"Latest monthly active users count","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":4},"id":16,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Monthly Active Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE day >= DATE_SUB(CURDATE(), INTERVAL 28 DAY)\n AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n","refId":"A"}],"title":"Monthly Active Users (MAU)","type":"stat"},{"datasource":"mysql","description":"Total pull requests reviewed by Copilot in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"blue","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":12,"y":4},"id":17,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(pr_total_reviewed_by_copilot) as \"PRs Reviewed by Copilot\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"PRs Reviewed by Copilot","type":"stat"},{"datasource":"mysql","description":"Total user-initiated interactions in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"purple","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":18,"y":4},"id":18,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(user_initiated_interaction_count) as \"User-Initiated Interactions\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"User-Initiated Interactions","type":"stat"},{"datasource":"mysql","description":"Daily and monthly active users over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Users","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":8},"id":5,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, daily_active_users as \"Daily Active\", monthly_active_users as \"Monthly Active\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"Active Users Over Time","type":"timeseries"},{"datasource":"mysql","description":"Code suggestions generated vs accepted over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Count","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":8},"id":6,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, code_generation_activity_count as \"Suggestions\", code_acceptance_activity_count as \"Acceptances\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"Code Suggestions & Acceptances","type":"timeseries"},{"datasource":"mysql","description":"Lines of code suggested to add versus lines added over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Lines of Code","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":16},"id":19,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, loc_suggested_to_add_sum as \"LOC Suggested\", loc_added_sum as \"LOC Added\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"LOC Suggested vs LOC Added Over Time","type":"timeseries"},{"datasource":"mysql","description":"Pull request activity over time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Pull Requests","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":16},"id":20,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time, pr_total_created as \"PRs Created\", pr_total_created_by_copilot as \"PRs Created by Copilot\", pr_total_reviewed as \"PRs Reviewed\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"PR Activity Over Time","type":"timeseries"},{"datasource":"mysql","description":"Weekly user-initiated interactions by top Copilot features","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Interactions","axisPlacement":"auto","barAlignment":0,"drawStyle":"bars","fillOpacity":80,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":0,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"mode":"normal","group":"A"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":24},"id":7,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"},"stacking":{"mode":"normal","group":"A"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT DATE(DATE_SUB(day, INTERVAL WEEKDAY(day) DAY)) as time,\n CASE\n WHEN feature IN (\n SELECT feature FROM (\n SELECT feature\n FROM _tool_copilot_metrics_by_feature\n WHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n GROUP BY feature\n HAVING SUM(user_initiated_interaction_count) > 0\n ORDER BY SUM(user_initiated_interaction_count) DESC\n LIMIT 4\n ) top_features\n ) THEN REPLACE(REPLACE(feature, 'chat_panel_', ''), '_mode', '')\n ELSE 'Other'\n END as metric,\n SUM(user_initiated_interaction_count) as value\nFROM _tool_copilot_metrics_by_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY time, metric\nORDER BY time","refId":"A"}],"title":"Feature Mix Over Time","type":"timeseries","transformations":[{"id":"prepareTimeSeries","options":{"format":"many"}},{"id":"renameByRegex","options":{"regex":"^value (.+)$","renamePattern":"$1"}}]},{"datasource":"mysql","description":"Weekly user-initiated interactions by top IDEs (Top 5 + Other)","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Weekly Interactions","axisPlacement":"auto","barAlignment":0,"drawStyle":"bars","fillOpacity":80,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":0,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"mode":"normal","group":"A"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":24},"id":8,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"},"stacking":{"mode":"normal","group":"A"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT DATE(DATE_SUB(day, INTERVAL WEEKDAY(day) DAY)) as time,\n CASE\n WHEN ide IN (\n SELECT ide FROM (\n SELECT ide\n FROM _tool_copilot_metrics_by_ide\n WHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\n GROUP BY ide\n HAVING SUM(user_initiated_interaction_count) > 0\n ORDER BY SUM(user_initiated_interaction_count) DESC\n LIMIT 4\n ) top_ides\n ) THEN ide\n ELSE 'Other'\n END as metric,\n SUM(user_initiated_interaction_count) as value\nFROM _tool_copilot_metrics_by_ide\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY time, metric\nORDER BY time","refId":"A"}],"title":"IDE Adoption Over Time","type":"timeseries","transformations":[{"id":"prepareTimeSeries","options":{"format":"many"}},{"id":"renameByRegex","options":{"regex":"^value (.+)$","renamePattern":"$1"}}]},{"datasource":"mysql","description":"Top languages ranked by code acceptance rate","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":40},"id":9,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT language as \"Language\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_language_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY language\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 15","refId":"A"}],"title":"Top Languages by Acceptance Rate","type":"table"},{"datasource":"mysql","description":"Top models ranked by code acceptance rate","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":40},"id":10,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT model as \"Model\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_model_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY model\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 10","refId":"A"}],"title":"Top Models by Acceptance Rate","type":"table"},{"datasource":"mysql","description":"Matrix-style breakdown of suggestions and acceptances by language and feature","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":24,"x":0,"y":48},"id":21,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT language as \"Language\",\n feature as \"Feature\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\"\nFROM _tool_copilot_metrics_by_language_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY language, feature\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY language, SUM(code_generation_activity_count) DESC\nLIMIT 100","refId":"A"}],"title":"Feature x Language Matrix","type":"table"},{"datasource":"mysql","description":"Count of distinct users active in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":56},"id":11,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Unique Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Unique Users (Period)","type":"stat"},{"datasource":"mysql","description":"Users who have used Copilot agent mode","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"purple","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":56},"id":12,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Agent Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}' AND used_agent = 1","refId":"A"}],"title":"Agent Mode Adopters","type":"stat"},{"datasource":"mysql","description":"Users who have used Copilot chat","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"orange","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":12,"y":56},"id":13,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(DISTINCT user_login) as \"Chat Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}' AND used_chat = 1","refId":"A"}],"title":"Chat Adopters","type":"stat"},{"datasource":"mysql","description":"Percentage of seats with recorded activity","fieldConfig":{"defaults":{"mappings":[],"min":0,"max":100,"unit":"percent","thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"yellow","value":50},{"color":"green","value":75}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":18,"y":56},"id":14,"options":{"minVizHeight":75,"minVizWidth":75,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showThresholdLabels":false,"showThresholdMarkers":true,"sizing":"auto"},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT\n COUNT(CASE WHEN last_activity_at IS NOT NULL THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0) as \"Utilization %\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id}","refId":"A"}],"title":"Seat Utilization","type":"gauge"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":60},"id":22,"panels":[],"title":"Quality & Efficiency Ratios","type":"row"},{"datasource":"mysql","description":"Daily acceptance rate trend computed as acceptances divided by suggestions","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"mappings":[],"max":1,"min":0,"unit":"percentunit","thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":61},"id":23,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time,\n code_acceptance_activity_count / NULLIF(code_generation_activity_count, 0) as \"Acceptance Rate\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"Overall Acceptance Rate Trend","type":"timeseries"},{"datasource":"mysql","description":"Daily LOC yield trend computed as added LOC divided by suggested LOC","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"mappings":[],"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":61},"id":24,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time,\n ROUND(loc_added_sum / NULLIF(loc_suggested_to_add_sum, 0), 2) as \"LOC Yield\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nORDER BY 1","refId":"A"}],"title":"LOC Yield Trend","type":"timeseries"},{"datasource":"mysql","description":"Acceptance ratio by Copilot feature","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":0,"y":69},"id":25,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT feature as \"Feature\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY feature\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 20","refId":"A"}],"title":"Acceptance Rate by Feature","type":"table"},{"datasource":"mysql","description":"Acceptance ratio by programming language","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":8,"y":69},"id":26,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT language as \"Language\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_language_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY language\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 20","refId":"A"}],"title":"Acceptance Rate by Language","type":"table"},{"datasource":"mysql","description":"Acceptance ratio by model","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":16,"y":69},"id":27,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT model as \"Model\",\n SUM(code_generation_activity_count) as \"Suggestions\",\n SUM(code_acceptance_activity_count) as \"Acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"Acceptance Rate %\"\nFROM _tool_copilot_metrics_by_model_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY model\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) DESC, SUM(code_generation_activity_count) DESC\nLIMIT 20","refId":"A"}],"title":"Acceptance Rate by Model","type":"table"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":77},"id":28,"panels":[],"title":"User Behavior","type":"row"},{"datasource":"mysql","description":"Top users ranked by code generation activity in the selected time range","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":78},"id":29,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT user_login as \"user_login\",\n SUM(code_generation_activity_count) as \"suggestions\",\n SUM(code_acceptance_activity_count) as \"acceptances\",\n ROUND(SUM(code_acceptance_activity_count) * 100.0 / NULLIF(SUM(code_generation_activity_count), 0), 1) as \"acceptance %\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY user_login\nHAVING SUM(code_generation_activity_count) > 0\nORDER BY SUM(code_generation_activity_count) DESC\nLIMIT 25","refId":"A"}],"title":"Top Users by Code Generations","type":"table"},{"datasource":"mysql","description":"Daily trend of distinct users engaging with agent mode versus chat","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Users","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":78},"id":30,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT day as time,\n COUNT(DISTINCT CASE WHEN used_agent = 1 THEN user_login END) as \"Agent Users\",\n COUNT(DISTINCT CASE WHEN used_chat = 1 THEN user_login END) as \"Chat Users\"\nFROM _tool_copilot_user_daily_metrics\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'\nGROUP BY day\nORDER BY 1","refId":"A"}],"title":"Agent Users vs Chat Users Trend","type":"timeseries"},{"datasource":"mysql","description":"First-seen daily trend of users becoming active for the first time","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Users","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":24,"x":0,"y":86},"id":31,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT first_day as time,\n COUNT(*) as \"New Active Users\"\nFROM (\n SELECT user_login,\n MIN(day) as first_day\n FROM _tool_copilot_user_daily_metrics\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n GROUP BY user_login\n) first_seen\nWHERE $__timeFilter(first_day)\nGROUP BY first_day\nORDER BY 1","refId":"A"}],"title":"New Active Users per Day","type":"timeseries"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":94},"id":32,"panels":[],"title":"Seat Effectiveness","type":"row"},{"datasource":"mysql","description":"Distribution of seat activity by last recorded editor","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":0,"y":95},"id":33,"options":{"displayLabels":["name","percent"],"legend":{"displayMode":"list","placement":"right","showLegend":true},"pieType":"pie","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COALESCE(NULLIF(last_activity_editor, ''), 'unknown') as \"Editor\",\n COUNT(*) as \"Seats\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id} AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')\nGROUP BY COALESCE(NULLIF(last_activity_editor, ''), 'unknown')\nORDER BY COUNT(*) DESC","refId":"A"}],"title":"Seat Activity by Editor","type":"piechart"},{"datasource":"mysql","description":"Total, active (last 30 days), and inactive seats for the selected scope","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":8,"y":95},"id":34,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COUNT(*) as \"total_seats\",\n SUM(CASE WHEN last_activity_at IS NOT NULL AND last_activity_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as \"active_seats\",\n SUM(CASE WHEN last_activity_at IS NULL OR last_activity_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as \"inactive_seats\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id} AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')","refId":"A"}],"title":"Seats Summary","type":"table"},{"datasource":"mysql","description":"Seats with no activity or activity older than 30 days","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":8,"x":16,"y":95},"id":35,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT user_login as \"user_login\",\n last_activity_at as \"last_activity_at\",\n plan_type as \"plan_type\"\nFROM _tool_copilot_seats\nWHERE connection_id = ${connection_id}\n AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')\n AND (last_activity_at IS NULL OR last_activity_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY))\nORDER BY last_activity_at IS NULL DESC, last_activity_at ASC\nLIMIT 100","refId":"A"}],"title":"Inactive Seats","type":"table"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":103},"id":36,"panels":[],"title":"Diagnostics","type":"row"},{"datasource":"mysql","description":"Latest available day in enterprise-level Copilot metrics","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":104},"id":37,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT COALESCE(TIMESTAMPDIFF(DAY, MAX(day), UTC_DATE()), 9999) as \"Days Since Latest Data\"\nFROM _tool_copilot_enterprise_daily_metrics\nWHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Data Freshness","type":"stat"},{"datasource":"mysql","description":"Share of generated suggestions tied to unknown model/feature taxonomy","fieldConfig":{"defaults":{"mappings":[],"min":0,"max":100,"unit":"percent","thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":5},{"color":"red","value":20}]}},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":104},"id":38,"options":{"minVizHeight":75,"minVizWidth":75,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showThresholdLabels":false,"showThresholdMarkers":true,"sizing":"auto"},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT ROUND(\n SUM(CASE WHEN LOWER(COALESCE(model, '')) = 'unknown' OR LOWER(COALESCE(feature, '')) LIKE '%unknown%' THEN code_generation_activity_count ELSE 0 END) * 100.0\n / NULLIF(SUM(code_generation_activity_count), 0),\n 2\n) as \"Unknown Taxonomy %\"\nFROM _tool_copilot_metrics_by_model_feature\nWHERE $__timeFilter(day) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Unknown Taxonomy Share","type":"gauge"},{"datasource":"mysql","description":"Row counts for core Copilot metrics tables","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":104},"id":39,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT *\nFROM (\n SELECT '_tool_copilot_enterprise_daily_metrics' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_enterprise_daily_metrics\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_user_daily_metrics' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_user_daily_metrics\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_metrics_by_feature' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_metrics_by_feature\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_metrics_by_model_feature' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_metrics_by_model_feature\n WHERE connection_id = ${connection_id} AND scope_id = '${scope_id}'\n UNION ALL\n SELECT '_tool_copilot_seats' as table_name,\n COUNT(*) as row_count\n FROM _tool_copilot_seats\n WHERE connection_id = ${connection_id} AND ('${scope_id}' = '' OR organization = '${scope_id}' OR organization = '')\n) data_volume\nORDER BY row_count DESC","refId":"A"}],"title":"Data Volume by Table","type":"table"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":112},"id":40,"panels":[],"title":"AI Credit Consumption","type":"row"},{"datasource":"mysql","description":"Total gross AI credits consumed in the selected period","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]},"unit":"short","decimals":2},"overrides":[]},"gridPos":{"h":4,"w":6,"x":0,"y":113},"id":41,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(gross_quantity) AS \"Gross Credits\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(`date`) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Total Gross Credits","type":"stat"},{"datasource":"mysql","description":"Total net AI credits (after discounts) in the selected period","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]},"unit":"short","decimals":2},"overrides":[]},"gridPos":{"h":4,"w":6,"x":6,"y":113},"id":42,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(net_quantity) AS \"Net Credits\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(`date`) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Total Net Credits","type":"stat"},{"datasource":"mysql","description":"Total net billed amount (after discounts) in the selected period","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]},"unit":"currencyUSD","decimals":2},"overrides":[]},"gridPos":{"h":4,"w":6,"x":12,"y":113},"id":43,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(net_amount) AS \"Net Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(`date`) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Total Net Amount","type":"stat"},{"datasource":"mysql","description":"Total gross billed amount in the selected period","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]},"unit":"currencyUSD","decimals":2},"overrides":[]},"gridPos":{"h":4,"w":6,"x":18,"y":113},"id":44,"options":{"colorMode":"value","graphMode":"none","justifyMode":"auto","orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showPercentChange":false,"textMode":"auto","wideLayout":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT SUM(gross_amount) AS \"Gross Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(`date`) AND connection_id = ${connection_id} AND scope_id = '${scope_id}'","refId":"A"}],"title":"Total Gross Amount","type":"stat"},{"datasource":"mysql","description":"Gross vs net AI credits consumed per day","fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"Credits","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":10,"gradientMode":"none","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"linear","lineWidth":2,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"never","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"unit":"short","thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":24,"x":0,"y":117},"id":45,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT `date` AS time, SUM(gross_quantity) AS \"Gross Credits\", SUM(net_quantity) AS \"Net Credits\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(`date`) AND connection_id = ${connection_id} AND scope_id = '${scope_id}' GROUP BY `date` ORDER BY 1","refId":"A"}],"title":"AI Credits Consumed Over Time","type":"timeseries"},{"datasource":"mysql","description":"AI credit consumption broken down by model","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":125},"id":46,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT model AS \"Model\", SUM(gross_quantity) AS \"Gross Credits\", SUM(net_quantity) AS \"Net Credits\", SUM(net_amount) AS \"Net Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(`date`) AND connection_id = ${connection_id} AND scope_id = '${scope_id}' GROUP BY model ORDER BY SUM(gross_quantity) DESC LIMIT 20","refId":"A"}],"title":"AI Credits by Model","type":"table"},{"datasource":"mysql","description":"AI credit consumption broken down by product and SKU","fieldConfig":{"defaults":{"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null}]}},"overrides":[]},"gridPos":{"h":8,"w":12,"x":12,"y":125},"id":47,"options":{"cellHeight":"sm","footer":{"countRows":false,"fields":"","reducer":["sum"],"show":false},"showHeader":true},"pluginVersion":"11.0.0","targets":[{"datasource":"mysql","format":"table","rawQuery":true,"rawSql":"SELECT product AS \"Product\", sku AS \"SKU\", SUM(gross_quantity) AS \"Gross Credits\", SUM(net_quantity) AS \"Net Credits\", SUM(net_amount) AS \"Net Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(`date`) AND connection_id = ${connection_id} AND scope_id = '${scope_id}' GROUP BY product, sku ORDER BY SUM(gross_quantity) DESC LIMIT 20","refId":"A"}],"title":"AI Credits by Product / SKU","type":"table"}],"refresh":"","schemaVersion":38,"tags":["copilot","devlake"],"templating":{"list":[{"current":{"selected":false,"text":"","value":""},"datasource":"mysql","definition":"SELECT DISTINCT connection_id FROM _tool_copilot_enterprise_daily_metrics ORDER BY 1","hide":0,"includeAll":false,"label":"Connection ID","multi":false,"name":"connection_id","options":[],"query":"SELECT DISTINCT connection_id FROM _tool_copilot_scopes ORDER BY connection_id DESC","refresh":1,"regex":"","skipUrlSync":false,"sort":0,"type":"query"},{"current":{"selected":false,"text":"","value":""},"datasource":"mysql","definition":"SELECT DISTINCT id as scope_id FROM _tool_copilot_scopes WHERE connection_id = ${connection_id} ORDER BY 1","hide":0,"includeAll":false,"label":"Scope ID","multi":false,"name":"scope_id","options":[],"query":"SELECT DISTINCT id as scope_id FROM _tool_copilot_scopes WHERE connection_id = ${connection_id} ORDER BY 1","refresh":2,"regex":"","skipUrlSync":false,"sort":0,"type":"query"}]},"time":{"from":"now-90d","to":"now"},"timepicker":{},"timezone":"utc","title":"GitHub Copilot Adoption","uid":"copilot_adoption","version":1,"weekStart":""} \ No newline at end of file diff --git a/grafana/dashboards/postgresql/github-copilot-adoption.json b/grafana/dashboards/postgresql/github-copilot-adoption.json index 978d263b859..a503ee4a47d 100644 --- a/grafana/dashboards/postgresql/github-copilot-adoption.json +++ b/grafana/dashboards/postgresql/github-copilot-adoption.json @@ -2255,6 +2255,429 @@ ], "title": "Data Volume by Table", "type": "table" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 112 + }, + "id": 40, + "panels": [], + "title": "AI Credit Consumption", + "type": "row" + }, + { + "datasource": "postgresql", + "description": "Total gross AI credits consumed in the selected period", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short", + "decimals": 2 + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 113 + }, + "id": 41, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": "postgresql", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT SUM(gross_quantity) AS \"Gross Credits\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(\"date\") AND ('${connection_id}' = '' OR connection_id::text = '${connection_id}') AND scope_id = '${scope_id}'", + "refId": "A" + } + ], + "title": "Total Gross Credits", + "type": "stat" + }, + { + "datasource": "postgresql", + "description": "Total net AI credits (after discounts) in the selected period", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short", + "decimals": 2 + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 113 + }, + "id": 42, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": "postgresql", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT SUM(net_quantity) AS \"Net Credits\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(\"date\") AND ('${connection_id}' = '' OR connection_id::text = '${connection_id}') AND scope_id = '${scope_id}'", + "refId": "A" + } + ], + "title": "Total Net Credits", + "type": "stat" + }, + { + "datasource": "postgresql", + "description": "Total net billed amount (after discounts) in the selected period", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "currencyUSD", + "decimals": 2 + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 113 + }, + "id": 43, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": "postgresql", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT SUM(net_amount) AS \"Net Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(\"date\") AND ('${connection_id}' = '' OR connection_id::text = '${connection_id}') AND scope_id = '${scope_id}'", + "refId": "A" + } + ], + "title": "Total Net Amount", + "type": "stat" + }, + { + "datasource": "postgresql", + "description": "Total gross billed amount in the selected period", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "currencyUSD", + "decimals": 2 + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 113 + }, + "id": 44, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": "postgresql", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT SUM(gross_amount) AS \"Gross Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(\"date\") AND ('${connection_id}' = '' OR connection_id::text = '${connection_id}') AND scope_id = '${scope_id}'", + "refId": "A" + } + ], + "title": "Total Gross Amount", + "type": "stat" + }, + { + "datasource": "postgresql", + "description": "Gross vs net AI credits consumed per day", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "Credits", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "unit": "short", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 117 + }, + "id": 45, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": "postgresql", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT \"date\" AS time, SUM(gross_quantity) AS \"Gross Credits\", SUM(net_quantity) AS \"Net Credits\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(\"date\") AND ('${connection_id}' = '' OR connection_id::text = '${connection_id}') AND scope_id = '${scope_id}' GROUP BY \"date\" ORDER BY 1 NULLS LAST", + "refId": "A" + } + ], + "title": "AI Credits Consumed Over Time", + "type": "timeseries" + }, + { + "datasource": "postgresql", + "description": "AI credit consumption broken down by model", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 125 + }, + "id": 46, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": "postgresql", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT model AS \"Model\", SUM(gross_quantity) AS \"Gross Credits\", SUM(net_quantity) AS \"Net Credits\", SUM(net_amount) AS \"Net Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(\"date\") AND ('${connection_id}' = '' OR connection_id::text = '${connection_id}') AND scope_id = '${scope_id}' GROUP BY \"model\" ORDER BY SUM(gross_quantity) DESC NULLS LAST LIMIT 20", + "refId": "A" + } + ], + "title": "AI Credits by Model", + "type": "table" + }, + { + "datasource": "postgresql", + "description": "AI credit consumption broken down by product and SKU", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 125 + }, + "id": 47, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": "postgresql", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT product AS \"Product\", sku AS \"SKU\", SUM(gross_quantity) AS \"Gross Credits\", SUM(net_quantity) AS \"Net Credits\", SUM(net_amount) AS \"Net Amount\" FROM _tool_copilot_ai_credit_usage WHERE $__timeFilter(\"date\") AND ('${connection_id}' = '' OR connection_id::text = '${connection_id}') AND scope_id = '${scope_id}' GROUP BY \"product\", \"sku\" ORDER BY SUM(gross_quantity) DESC NULLS LAST LIMIT 20", + "refId": "A" + } + ], + "title": "AI Credits by Product / SKU", + "type": "table" } ], "refresh": "",