Skip to content

Commit d6ff4e4

Browse files
authored
Copilot analytics Extensions (#63)
2 parents 8e4ca82 + 91cfe09 commit d6ff4e4

37 files changed

+3438
-258
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@
3131
/src/SPO/AITracker/TypeScript/node_modules
3232
/src/SPO/AITracker/DevConfig.json
3333
/src/SPO/AITracker/aitracker.js
34+
/src/AnalyticsEngine/Directory.Build.props

src/AnalyticsEngine/App.ControlPanel.Engine/Utils/DatabaseUpgrader.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
using Common.Entities;
33
using DataUtils;
44
using System;
5-
using System.Collections.Generic;
65
using System.Linq;
7-
using System.Text.RegularExpressions;
86

97
namespace App.ControlPanel.Engine
108
{

src/AnalyticsEngine/Common/DataUtils/Sql/Inserts/InsertBatchTypeFieldCache.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ public class InsertBatchTypeFieldCache<T>
1212
{
1313
private List<InsertBatchPropertyMapping> _fieldInfoPropertyInfoCache = null;
1414

15-
static List<Type> _validTempColumnTypes = new List<Type>()
15+
static List<Type> _validTempColumnTypes = new List<Type>()
1616
{
1717
typeof(string), typeof(DateTime), typeof(DateTime?), typeof(int), typeof(float), typeof(double), typeof(bool), typeof(Guid), typeof(int?),
18-
typeof(int?), typeof(double?)
18+
typeof(int?), typeof(double?)
1919
};
2020

2121
public List<InsertBatchPropertyMapping> PropertyMappingInfo

src/AnalyticsEngine/Common/Entities/AnalyticsEntitiesContext.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,19 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder)
300300
public DbSet<CopilotEventMetadataFile> CopilotEventMetadataFiles { get; set; }
301301
public DbSet<CopilotEventMetadataMeeting> CopilotEventMetadataMeetings { get; set; }
302302
public DbSet<CopilotAgent> CopilotAgents { get; set; }
303-
304-
303+
public DbSet<CopilotAccessedResourceId> CopilotAccessedResourceIds { get; set; }
304+
public DbSet<CopilotAccessedResourceName> CopilotAccessedResourceNames { get; set; }
305+
public DbSet<CopilotAccessedResourceSiteUrl> CopilotAccessedResourceSiteUrls { get; set; }
306+
public DbSet<CopilotAccessedResourceType> CopilotAccessedResourceTypes { get; set; }
307+
public DbSet<SensitivityLabel> SensitivityLabels { get; set; }
308+
public DbSet<CopilotEventAccessedResource> CopilotEventAccessedResources { get; set; }
309+
310+
// Message tracking
311+
public DbSet<CopilotMessage> CopilotMessages { get; set; }
312+
313+
// AI Model transparency
314+
public DbSet<CopilotAIModel> CopilotAIModels { get; set; }
315+
public DbSet<CopilotEventAIModel> CopilotEventAIModels { get; set; }
305316
#endregion
306317
}
307318

src/AnalyticsEngine/Common/Entities/Entities.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@
259259
<Compile Include="Migrations\202510231511508_CopilotAgents.Designer.cs">
260260
<DependentUpon>202510231511508_CopilotAgents.cs</DependentUpon>
261261
</Compile>
262+
<Compile Include="Migrations\202511211834002_CopilotExtendedData.cs" />
263+
<Compile Include="Migrations\202511211834002_CopilotExtendedData.Designer.cs">
264+
<DependentUpon>202511211834002_CopilotExtendedData.cs</DependentUpon>
265+
</Compile>
262266
<Compile Include="Migrations\v1-0-5.cs" />
263267
<Compile Include="Migrations\v1-0-5.Designer.cs">
264268
<DependentUpon>v1-0-5.cs</DependentUpon>
@@ -373,6 +377,9 @@
373377
<EmbeddedResource Include="Migrations\202510231511508_CopilotAgents.resx">
374378
<DependentUpon>202510231511508_CopilotAgents.cs</DependentUpon>
375379
</EmbeddedResource>
380+
<EmbeddedResource Include="Migrations\202511211834002_CopilotExtendedData.resx">
381+
<DependentUpon>202511211834002_CopilotExtendedData.cs</DependentUpon>
382+
</EmbeddedResource>
376383
<EmbeddedResource Include="Migrations\v1-0-5.resx">
377384
<DependentUpon>v1-0-5.cs</DependentUpon>
378385
</EmbeddedResource>

src/AnalyticsEngine/Common/Entities/Entities/AuditLog/CopilotEvents.cs

Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Common.Entities.Entities.AuditLog
99
/// A copilot interaction event. May not be related to any file or meeting.
1010
/// Relates back to a common audit event.
1111
/// </summary>
12-
[Table("event_copilot_chats")]
12+
[Table("copilot_chats")]
1313
public class CopilotChat : BaseOfficeEvent
1414
{
1515
[Column("app_host")]
@@ -20,6 +20,20 @@ public class CopilotChat : BaseOfficeEvent
2020
[Column("agent_id")]
2121
public int? AgentId { get; set; }
2222
public CopilotAgent Agent { get; set; } = null;
23+
24+
/// <summary>
25+
/// Estimated total Copilot Credits consumed for this interaction.
26+
/// Calculated from CopilotCreditEstimation in CopilotAuditLogContent.
27+
/// </summary>
28+
[Column("copilot_credit_estimate_total")]
29+
public int? CopilotCreditEstimateTotal { get; set; }
30+
31+
/// <summary>
32+
/// JSON-serialized Copilot Credit estimation details from CopilotCreditEstimation.
33+
/// Contains breakdown of generative answers, tenant graph grounding, deep reasoning, etc.
34+
/// </summary>
35+
[Column("copilot_credit_estimate_json")]
36+
public string CopilotCreditEstimateJson { get; set; } = null;
2337
}
2438

2539
/// <summary>
@@ -39,7 +53,7 @@ public abstract class BaseCopilotSpecificEvent
3953
}
4054

4155

42-
[Table("event_copilot_files")]
56+
[Table("copilot_event_files")]
4357
public class CopilotEventMetadataFile : BaseCopilotSpecificEvent
4458
{
4559
[ForeignKey(nameof(FileExtension))]
@@ -68,7 +82,7 @@ public override string GetEventDescription()
6882
}
6983
}
7084

71-
[Table("event_copilot_meetings")]
85+
[Table("copilot_event_meetings")]
7286
public class CopilotEventMetadataMeeting : BaseCopilotSpecificEvent
7387
{
7488
[ForeignKey(nameof(OnlineMeeting))]
@@ -90,5 +104,147 @@ public class CopilotAgent : AbstractEFEntityWithName
90104
public string AgentID { get; set; } = null;
91105
}
92106

107+
/// <summary>
108+
/// Lookup table for accessed resource IDs
109+
/// </summary>
110+
[Table("copilot_event_accessed_resource_ids")]
111+
public class CopilotAccessedResourceId : AbstractEFEntity
112+
{
113+
[Column("resource_id")]
114+
[MaxLength(5000)]
115+
public string ResourceId { get; set; } = null;
116+
}
117+
118+
/// <summary>
119+
/// Lookup table for accessed resource names
120+
/// </summary>
121+
[Table("copilot_event_accessed_resource_names")]
122+
public class CopilotAccessedResourceName : AbstractEFEntity
123+
{
124+
// Not using AbstractEFEntityWithName to allow longer names
125+
[Column("name")]
126+
public string Name { get; set; }
127+
}
128+
129+
/// <summary>
130+
/// Lookup table for accessed resource site URLs
131+
/// </summary>
132+
[Table("copilot_event_accessed_resource_site_urls")]
133+
public class CopilotAccessedResourceSiteUrl : AbstractEFEntity
134+
{
135+
[Column("site_url")]
136+
public string SiteUrl { get; set; }
137+
}
138+
139+
/// <summary>
140+
/// Lookup table for accessed resource types
141+
/// </summary>
142+
[Table("copilot_event_accessed_resource_types")]
143+
public class CopilotAccessedResourceType : AbstractEFEntityWithName
144+
{
145+
}
146+
147+
/// <summary>
148+
/// Lookup table for sensitivity label IDs.
149+
/// Shared across multiple event types (Copilot, SharePoint, etc.)
150+
/// </summary>
151+
[Table("sensitivity_labels")]
152+
public class SensitivityLabel : AbstractEFEntity
153+
{
154+
[Column("label_id")]
155+
[MaxLength(100)]
156+
public string LabelId { get; set; } = null;
157+
}
158+
159+
/// <summary>
160+
/// Junction table linking copilot events to accessed resources
161+
/// </summary>
162+
[Table("copilot_event_accessed_resources")]
163+
public class CopilotEventAccessedResource : AbstractEFEntity
164+
{
165+
[ForeignKey(nameof(RelatedChat))]
166+
[Column("copilot_chat_id")]
167+
public Guid ChatId { get; set; }
168+
public CopilotChat RelatedChat { get; set; } = null;
169+
170+
[ForeignKey(nameof(ResourceId))]
171+
[Column("resource_id_id")]
172+
public int? ResourceIdId { get; set; }
173+
public CopilotAccessedResourceId ResourceId { get; set; } = null;
174+
175+
[ForeignKey(nameof(ResourceName))]
176+
[Column("resource_name_id")]
177+
public int? ResourceNameId { get; set; }
178+
public CopilotAccessedResourceName ResourceName { get; set; } = null;
179+
180+
[ForeignKey(nameof(ResourceSiteUrl))]
181+
[Column("resource_site_url_id")]
182+
public int? ResourceSiteUrlId { get; set; }
183+
public CopilotAccessedResourceSiteUrl ResourceSiteUrl { get; set; } = null;
184+
185+
[ForeignKey(nameof(ResourceType))]
186+
[Column("resource_type_id")]
187+
public int? ResourceTypeId { get; set; }
188+
public CopilotAccessedResourceType ResourceType { get; set; } = null;
189+
190+
[ForeignKey(nameof(SensitivityLabel))]
191+
[Column("sensitivity_label_id")]
192+
public int? SensitivityLabelId { get; set; }
193+
public SensitivityLabel SensitivityLabel { get; set; } = null;
194+
}
195+
196+
#region Message Tracking Tables
197+
198+
/// <summary>
199+
/// Represents a Copilot response message in a conversation.
200+
/// Note: Only response messages (not user prompts) are tracked in the import process.
201+
/// </summary>
202+
[Table("copilot_event_messages")]
203+
public class CopilotMessage : AbstractEFEntity
204+
{
205+
[ForeignKey(nameof(RelatedChat))]
206+
[Column("copilot_chat_id")]
207+
public Guid ChatId { get; set; }
208+
public CopilotChat RelatedChat { get; set; } = null;
209+
210+
[Column("message_id")]
211+
[MaxLength(500)]
212+
public string MessageId { get; set; } = null;
213+
}
214+
215+
#endregion
216+
217+
#region AI Model Transparency Tables
218+
219+
/// <summary>
220+
/// Lookup table for AI model names used in Copilot conversations.
221+
/// Stores unique model names like "DEEP_LEO" for deep reasoning.
222+
/// </summary>
223+
[Table("copilot_ai_models")]
224+
public class CopilotAIModel : AbstractEFEntityWithName
225+
{
226+
// Name inherited from AbstractEFEntityWithName
227+
}
228+
229+
/// <summary>
230+
/// Junction table linking Copilot events to the AI models used.
231+
/// Tracks which AI models were involved in generating responses for each conversation.
232+
/// </summary>
233+
[Table("copilot_event_ai_models")]
234+
public class CopilotEventAIModel : AbstractEFEntity
235+
{
236+
[ForeignKey(nameof(RelatedChat))]
237+
[Column("copilot_chat_id")]
238+
public Guid ChatId { get; set; }
239+
public CopilotChat RelatedChat { get; set; } = null;
240+
241+
[ForeignKey(nameof(AIModel))]
242+
[Column("model_id")]
243+
public int ModelId { get; set; }
244+
public CopilotAIModel AIModel { get; set; } = null;
245+
}
246+
247+
#endregion
248+
93249
}
94250

src/AnalyticsEngine/Common/Entities/Migrations/202510231511508_CopilotAgents.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@
22
{
33
using System;
44
using System.Data.Entity.Migrations;
5-
5+
66
public partial class CopilotAgents : DbMigration
77
{
88
public override void Up()
99
{
1010
CreateTable(
1111
"dbo.copilot_agents",
1212
c => new
13-
{
14-
id = c.Int(nullable: false, identity: true),
15-
agent_id = c.String(),
16-
name = c.String(maxLength: 100),
17-
})
13+
{
14+
id = c.Int(nullable: false, identity: true),
15+
agent_id = c.String(),
16+
name = c.String(maxLength: 100),
17+
})
1818
.PrimaryKey(t => t.id);
19-
19+
2020
AddColumn("dbo.event_copilot_chats", "agent_id", c => c.Int());
2121
CreateIndex("dbo.event_copilot_chats", "agent_id");
2222
AddForeignKey("dbo.event_copilot_chats", "agent_id", "dbo.copilot_agents", "id");
2323
Console.WriteLine("DB SCHEMA: Applied 'Copilot Agents' succesfully.");
2424
}
25-
25+
2626
public override void Down()
2727
{
2828
DropForeignKey("dbo.event_copilot_chats", "agent_id", "dbo.copilot_agents");

src/AnalyticsEngine/Common/Entities/Migrations/202511211834002_CopilotExtendedData.Designer.cs

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)