Skip to content

Commit 0e46ae0

Browse files
committed
Add real agent profile editing
1 parent 4aa3442 commit 0e46ae0

20 files changed

Lines changed: 526 additions & 32 deletions

File tree

DotPilot.Core/ChatSessions/Commands/CreateAgentProfileCommand.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ public CreateAgentProfileCommand(
1010
string name,
1111
AgentProviderKind providerKind,
1212
string modelName,
13-
string systemPrompt)
13+
string systemPrompt,
14+
string description = "")
1415
: base(
1516
Guid.CreateVersion7(),
1617
nameof(CreateAgentProfileCommand),
17-
new Payload(name, providerKind, modelName, systemPrompt))
18+
new Payload(name, providerKind, modelName, systemPrompt, description))
1819
{
19-
_payload = new Payload(name, providerKind, modelName, systemPrompt);
20+
_payload = new Payload(name, providerKind, modelName, systemPrompt, description);
2021
Value = _payload;
2122
}
2223

@@ -28,10 +29,13 @@ public CreateAgentProfileCommand(
2829

2930
public string SystemPrompt => _payload.SystemPrompt;
3031

32+
public string Description => _payload.Description;
33+
3134
[GenerateSerializer]
3235
public sealed record Payload(
3336
[property: Id(0)] string Name,
3437
[property: Id(1)] AgentProviderKind ProviderKind,
3538
[property: Id(2)] string ModelName,
36-
[property: Id(3)] string SystemPrompt);
39+
[property: Id(3)] string SystemPrompt,
40+
[property: Id(4)] string Description);
3741
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using DotPilot.Core.ControlPlaneDomain;
2+
using ManagedCode.Communication.Commands;
3+
4+
namespace DotPilot.Core.ChatSessions.Commands;
5+
6+
public sealed class UpdateAgentProfileCommand : Command<UpdateAgentProfileCommand.Payload>
7+
{
8+
private readonly Payload _payload;
9+
10+
public UpdateAgentProfileCommand(
11+
AgentProfileId agentId,
12+
string name,
13+
AgentProviderKind providerKind,
14+
string modelName,
15+
string systemPrompt,
16+
string description = "")
17+
: base(
18+
Guid.CreateVersion7(),
19+
nameof(UpdateAgentProfileCommand),
20+
new Payload(agentId, name, providerKind, modelName, systemPrompt, description))
21+
{
22+
_payload = new Payload(agentId, name, providerKind, modelName, systemPrompt, description);
23+
Value = _payload;
24+
}
25+
26+
public AgentProfileId AgentId => _payload.AgentId;
27+
28+
public string Name => _payload.Name;
29+
30+
public AgentProviderKind ProviderKind => _payload.ProviderKind;
31+
32+
public string ModelName => _payload.ModelName;
33+
34+
public string SystemPrompt => _payload.SystemPrompt;
35+
36+
public string Description => _payload.Description;
37+
38+
[GenerateSerializer]
39+
public sealed record Payload(
40+
[property: Id(0)] AgentProfileId AgentId,
41+
[property: Id(1)] string Name,
42+
[property: Id(2)] AgentProviderKind ProviderKind,
43+
[property: Id(3)] string ModelName,
44+
[property: Id(4)] string SystemPrompt,
45+
[property: Id(5)] string Description);
46+
}

DotPilot.Core/ChatSessions/Contracts/AgentSessionContracts.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public sealed record ProviderStatusDescriptor(
2929
public sealed record AgentProfileSummary(
3030
AgentProfileId Id,
3131
string Name,
32+
string Description,
3233
AgentProviderKind ProviderKind,
3334
string ProviderDisplayName,
3435
string ModelName,

DotPilot.Core/ChatSessions/Diagnostics/AgentSessionRuntimeLog.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,37 @@ public static partial void AgentCreationFailed(
179179
string agentName,
180180
AgentProviderKind providerKind);
181181

182+
[LoggerMessage(
183+
EventId = 1224,
184+
Level = LogLevel.Information,
185+
Message = "Updating agent profile. AgentId={AgentId} Name={AgentName} Provider={ProviderKind}.")]
186+
public static partial void AgentUpdateStarted(
187+
ILogger logger,
188+
AgentProfileId agentId,
189+
string agentName,
190+
AgentProviderKind providerKind);
191+
192+
[LoggerMessage(
193+
EventId = 1225,
194+
Level = LogLevel.Information,
195+
Message = "Updated agent profile. AgentId={AgentId} Name={AgentName} Provider={ProviderKind}.")]
196+
public static partial void AgentUpdated(
197+
ILogger logger,
198+
Guid agentId,
199+
string agentName,
200+
AgentProviderKind providerKind);
201+
202+
[LoggerMessage(
203+
EventId = 1226,
204+
Level = LogLevel.Error,
205+
Message = "Agent profile update failed. AgentId={AgentId} Name={AgentName} Provider={ProviderKind}.")]
206+
public static partial void AgentUpdateFailed(
207+
ILogger logger,
208+
Exception exception,
209+
AgentProfileId agentId,
210+
string agentName,
211+
AgentProviderKind providerKind);
212+
182213
[LoggerMessage(
183214
EventId = 1208,
184215
Level = LogLevel.Information,

DotPilot.Core/ChatSessions/Execution/AgentSessionService.cs

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public async ValueTask<Result<AgentProfileSummary>> CreateAgentAsync(
148148
var agentName = command.Name.Trim();
149149
var modelName = command.ModelName.Trim();
150150
var systemPrompt = command.SystemPrompt.Trim();
151+
var description = ResolveAgentDescription(command.Description, systemPrompt);
151152
AgentSessionServiceLog.AgentCreationStarted(logger, agentName, command.ProviderKind);
152153

153154
try
@@ -165,6 +166,7 @@ public async ValueTask<Result<AgentProfileSummary>> CreateAgentAsync(
165166
{
166167
Id = Guid.CreateVersion7(),
167168
Name = agentName,
169+
Description = description,
168170
Role = AgentProfileSchemaDefaults.DefaultRole,
169171
ProviderKind = (int)command.ProviderKind,
170172
ModelName = modelName,
@@ -191,6 +193,58 @@ public async ValueTask<Result<AgentProfileSummary>> CreateAgentAsync(
191193
}
192194
}
193195

196+
public async ValueTask<Result<AgentProfileSummary>> UpdateAgentAsync(
197+
UpdateAgentProfileCommand command,
198+
CancellationToken cancellationToken)
199+
{
200+
ArgumentNullException.ThrowIfNull(command);
201+
await EnsureInitializedAsync(cancellationToken);
202+
var agentName = command.Name.Trim();
203+
var modelName = command.ModelName.Trim();
204+
var systemPrompt = command.SystemPrompt.Trim();
205+
var description = ResolveAgentDescription(command.Description, systemPrompt);
206+
AgentSessionServiceLog.AgentUpdateStarted(logger, command.AgentId, agentName, command.ProviderKind);
207+
208+
try
209+
{
210+
await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
211+
var record = await dbContext.AgentProfiles
212+
.FirstOrDefaultAsync(agent => agent.Id == command.AgentId.Value, cancellationToken);
213+
if (record is null)
214+
{
215+
return Result<AgentProfileSummary>.FailNotFound($"Agent '{command.AgentId}' was not found.");
216+
}
217+
218+
var providers = await GetProviderStatusesAsync(forceRefresh: false, cancellationToken);
219+
var provider = providers.First(status => status.Kind == command.ProviderKind);
220+
if (!provider.CanCreateAgents)
221+
{
222+
return Result<AgentProfileSummary>.FailForbidden(provider.StatusSummary);
223+
}
224+
225+
record.Name = agentName;
226+
record.Description = description;
227+
record.ProviderKind = (int)command.ProviderKind;
228+
record.ModelName = modelName;
229+
record.SystemPrompt = systemPrompt;
230+
231+
await dbContext.SaveChangesAsync(cancellationToken);
232+
233+
AgentSessionServiceLog.AgentUpdated(
234+
logger,
235+
record.Id,
236+
record.Name,
237+
command.ProviderKind);
238+
239+
return Result<AgentProfileSummary>.Succeed(MapAgentSummary(record));
240+
}
241+
catch (Exception exception)
242+
{
243+
AgentSessionServiceLog.AgentUpdateFailed(logger, exception, command.AgentId, agentName, command.ProviderKind);
244+
return Result<AgentProfileSummary>.Fail(exception);
245+
}
246+
}
247+
194248
public async ValueTask<Result<SessionTranscriptSnapshot>> CreateSessionAsync(
195249
CreateSessionCommand command,
196250
CancellationToken cancellationToken)
@@ -782,6 +836,7 @@ record => record.ProviderKind == (int)AgentProviderKind.Debug,
782836
{
783837
Id = Guid.CreateVersion7(),
784838
Name = AgentSessionDefaults.SystemAgentName,
839+
Description = AgentSessionDefaults.SystemAgentDescription,
785840
Role = AgentProfileSchemaDefaults.DefaultRole,
786841
ProviderKind = (int)providerKind,
787842
ModelName = AgentSessionDefaults.GetDefaultModel(providerKind),
@@ -816,8 +871,9 @@ private async Task NormalizeLegacyAgentProfilesAsync(
816871
var legacyDebugModel = AgentSessionDefaults.GetDefaultModel(AgentProviderKind.Debug);
817872
var legacyAgents = await dbContext.AgentProfiles
818873
.Where(record =>
819-
record.ProviderKind != (int)AgentProviderKind.Debug &&
820-
record.ModelName == legacyDebugModel)
874+
(record.ProviderKind != (int)AgentProviderKind.Debug &&
875+
record.ModelName == legacyDebugModel) ||
876+
string.IsNullOrWhiteSpace(record.Description))
821877
.ToListAsync(cancellationToken);
822878
if (legacyAgents.Count == 0)
823879
{
@@ -827,8 +883,17 @@ private async Task NormalizeLegacyAgentProfilesAsync(
827883
foreach (var agent in legacyAgents)
828884
{
829885
var providerKind = (AgentProviderKind)agent.ProviderKind;
830-
agent.ModelName = AgentSessionDefaults.GetDefaultModel(providerKind);
831-
AgentSessionServiceLog.LegacyAgentProfileNormalized(logger, agent.Id, providerKind, agent.ModelName);
886+
if (agent.ProviderKind != (int)AgentProviderKind.Debug &&
887+
agent.ModelName == legacyDebugModel)
888+
{
889+
agent.ModelName = AgentSessionDefaults.GetDefaultModel(providerKind);
890+
AgentSessionServiceLog.LegacyAgentProfileNormalized(logger, agent.Id, providerKind, agent.ModelName);
891+
}
892+
893+
if (string.IsNullOrWhiteSpace(agent.Description))
894+
{
895+
agent.Description = ResolveAgentDescription(string.Empty, agent.SystemPrompt);
896+
}
832897
}
833898

834899
await dbContext.SaveChangesAsync(cancellationToken);
@@ -917,6 +982,9 @@ private static AgentProfileSummary MapAgentSummary(AgentProfileRecord record)
917982
return new AgentProfileSummary(
918983
new AgentProfileId(record.Id),
919984
record.Name,
985+
string.IsNullOrWhiteSpace(record.Description)
986+
? ResolveAgentDescription(string.Empty, record.SystemPrompt)
987+
: record.Description,
920988
(AgentProviderKind)record.ProviderKind,
921989
providerProfile.DisplayName,
922990
record.ModelName,
@@ -939,4 +1007,17 @@ public void Dispose()
9391007
{
9401008
_initializationGate.Dispose();
9411009
}
1010+
1011+
private static string ResolveAgentDescription(string description, string systemPrompt)
1012+
{
1013+
var normalizedDescription = description.Trim();
1014+
if (!string.IsNullOrWhiteSpace(normalizedDescription))
1015+
{
1016+
return normalizedDescription;
1017+
}
1018+
1019+
return string.IsNullOrWhiteSpace(systemPrompt)
1020+
? string.Empty
1021+
: AgentSessionDefaults.CreateAgentDescription(systemPrompt);
1022+
}
9421023
}

DotPilot.Core/ChatSessions/Interfaces/IAgentSessionService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ ValueTask<Result<AgentProfileSummary>> CreateAgentAsync(
1515
CreateAgentProfileCommand command,
1616
CancellationToken cancellationToken);
1717

18+
ValueTask<Result<AgentProfileSummary>> UpdateAgentAsync(
19+
UpdateAgentProfileCommand command,
20+
CancellationToken cancellationToken);
21+
1822
ValueTask<Result<SessionTranscriptSnapshot>> CreateSessionAsync(
1923
CreateSessionCommand command,
2024
CancellationToken cancellationToken);

DotPilot.Core/ChatSessions/Persistence/Models/LocalAgentSessionRecords.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ internal sealed class AgentProfileRecord
66

77
public string Name { get; set; } = string.Empty;
88

9+
public string Description { get; set; } = string.Empty;
10+
911
public int Role { get; set; }
1012

1113
public int ProviderKind { get; set; }

DotPilot.Core/ChatSessions/Persistence/Services/AgentProfileSchemaCompatibilityEnsurer.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace DotPilot.Core.ChatSessions;
66
internal static class AgentProfileSchemaCompatibilityEnsurer
77
{
88
private const string AgentProfilesTableName = "AgentProfiles";
9+
private const string DescriptionColumnName = "Description";
910
private const string RoleColumnName = "Role";
1011
private const string CapabilitiesJsonColumnName = "CapabilitiesJson";
1112

@@ -19,6 +20,16 @@ public static async Task EnsureAsync(LocalAgentSessionDbContext dbContext, Cance
1920
}
2021

2122
var existingColumns = await ReadColumnNamesAsync(dbContext, cancellationToken);
23+
if (!existingColumns.Contains(DescriptionColumnName, StringComparer.OrdinalIgnoreCase))
24+
{
25+
await dbContext.Database.ExecuteSqlRawAsync(
26+
$"""
27+
ALTER TABLE "{AgentProfilesTableName}"
28+
ADD COLUMN "{DescriptionColumnName}" TEXT NOT NULL DEFAULT '';
29+
""",
30+
cancellationToken);
31+
}
32+
2233
if (!existingColumns.Contains(RoleColumnName, StringComparer.OrdinalIgnoreCase))
2334
{
2435
await dbContext.Database.ExecuteSqlRawAsync(

DotPilot.Core/ChatSessions/Persistence/Storage/LocalAgentSessionDbContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
1919
{
2020
entity.HasKey(record => record.Id);
2121
entity.Property(record => record.Name).IsRequired();
22+
entity.Property(record => record.Description)
23+
.HasDefaultValue(string.Empty)
24+
.IsRequired();
2225
entity.Property(record => record.Role)
2326
.HasDefaultValue(AgentProfileSchemaDefaults.DefaultRole);
2427
entity.Property(record => record.ModelName).IsRequired();

DotPilot.Core/Workspace/Interfaces/IAgentWorkspaceState.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ ValueTask<Result<AgentProfileSummary>> CreateAgentAsync(
1515
CreateAgentProfileCommand command,
1616
CancellationToken cancellationToken);
1717

18+
ValueTask<Result<AgentProfileSummary>> UpdateAgentAsync(
19+
UpdateAgentProfileCommand command,
20+
CancellationToken cancellationToken);
21+
1822
ValueTask<Result<SessionTranscriptSnapshot>> CreateSessionAsync(
1923
CreateSessionCommand command,
2024
CancellationToken cancellationToken);

0 commit comments

Comments
 (0)