Skip to content

Commit 52f056c

Browse files
committed
Optimize job logs and reset startup log
1 parent bc205e5 commit 52f056c

14 files changed

Lines changed: 743 additions & 107 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ jobs:
133133
- domain: Chat Streaming
134134
filter: 'FullyQualifiedName~TestHarnessStreamingCorrectnessTests'
135135
- domain: Jobs Direct
136-
filter: 'FullyQualifiedName~DirectJobSubmissionCompletes|FullyQualifiedName~DirectJobDeniedStops|FullyQualifiedName~DirectJobAwaitingApproval|FullyQualifiedName~DirectJobApprovalDenies|FullyQualifiedName~DirectJobFailure'
136+
filter: 'FullyQualifiedName~DirectJobSubmissionCompletes|FullyQualifiedName~DirectJobDetailAndLifecycleActionsReuseCachedLogs|FullyQualifiedName~DirectJobDeniedStops|FullyQualifiedName~DirectJobAwaitingApproval|FullyQualifiedName~DirectJobApprovalDenies|FullyQualifiedName~DirectJobFailure'
137137
- domain: Jobs Lifecycle
138138
filter: 'FullyQualifiedName~LongRunningDirectJobPauseResumeStopAndCompletedCancelAreStable'
139139
- domain: Jobs Streaming
@@ -169,7 +169,7 @@ jobs:
169169
- domain: Defaults Resource Resolution
170170
filter: 'FullyQualifiedName~PerResourceJob'
171171
- domain: Module API Host Startup
172-
filter: 'FullyQualifiedName~SharpClaw.Tests.Modules.ApiHostStartupTests'
172+
filter: 'FullyQualifiedName~SharpClaw.Tests.Modules.ApiHostStartupTests|FullyQualifiedName~SharpClaw.Tests.Logging.SessionLogWriterTests'
173173
- domain: Module Bundled Outputs
174174
filter: 'FullyQualifiedName~SharpClaw.Tests.Modules.BundledModuleOutputTests'
175175
- domain: Module External Lifecycle
@@ -229,7 +229,7 @@ jobs:
229229
- domain: Jobs Direct Actions
230230
filter: 'FullyQualifiedName~PerformanceGate_DirectJobAllowedWarmNoOp|FullyQualifiedName~PerformanceGate_DirectJobDeniedWarmNoOp'
231231
- domain: Jobs Summaries
232-
filter: 'FullyQualifiedName~PerformanceGate_DirectJobSummaries'
232+
filter: 'FullyQualifiedName~PerformanceGate_DirectJobSummaries|FullyQualifiedName~PerformanceGate_DirectJobDetailHotLogs'
233233
- domain: Jobs Parallel
234234
filter: 'FullyQualifiedName~PerformanceGate_DirectJob_TwentyParallelAllowedNoOpJobs'
235235
- domain: Tools Permission Checks

SharpClaw.Application.API/Handlers/AgentJobHandlers.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static async Task<IResult> GetById(
3939
public static async Task<IResult> Approve(
4040
Guid channelId, Guid jobId, ApproveAgentJobRequest request, AgentJobService svc, ChatService chatSvc)
4141
{
42-
if (await GetScopedJobAsync(channelId, jobId, svc) is null) return Results.NotFound();
42+
if (await GetScopedJobSummaryAsync(channelId, jobId, svc) is null) return Results.NotFound();
4343
var job = await svc.ApproveAsync(jobId, request);
4444
if (job is null) return Results.NotFound();
4545
var cost = await chatSvc.GetChannelCostAsync(job.ChannelId);
@@ -50,7 +50,7 @@ public static async Task<IResult> Approve(
5050
public static async Task<IResult> Stop(
5151
Guid channelId, Guid jobId, AgentJobService svc, ChatService chatSvc)
5252
{
53-
if (await GetScopedJobAsync(channelId, jobId, svc) is null) return Results.NotFound();
53+
if (await GetScopedJobSummaryAsync(channelId, jobId, svc) is null) return Results.NotFound();
5454
var job = await svc.StopAsync(jobId);
5555
if (job is null) return Results.NotFound();
5656
var cost = await chatSvc.GetChannelCostAsync(job.ChannelId);
@@ -61,7 +61,7 @@ public static async Task<IResult> Stop(
6161
public static async Task<IResult> Cancel(
6262
Guid channelId, Guid jobId, AgentJobService svc, ChatService chatSvc)
6363
{
64-
if (await GetScopedJobAsync(channelId, jobId, svc) is null) return Results.NotFound();
64+
if (await GetScopedJobSummaryAsync(channelId, jobId, svc) is null) return Results.NotFound();
6565
var job = await svc.CancelAsync(jobId);
6666
if (job is null) return Results.NotFound();
6767
var cost = await chatSvc.GetChannelCostAsync(job.ChannelId);
@@ -72,7 +72,7 @@ public static async Task<IResult> Cancel(
7272
public static async Task<IResult> Pause(
7373
Guid channelId, Guid jobId, AgentJobService svc, ChatService chatSvc)
7474
{
75-
if (await GetScopedJobAsync(channelId, jobId, svc) is null) return Results.NotFound();
75+
if (await GetScopedJobSummaryAsync(channelId, jobId, svc) is null) return Results.NotFound();
7676
var job = await svc.PauseAsync(jobId);
7777
if (job is null) return Results.NotFound();
7878
var cost = await chatSvc.GetChannelCostAsync(job.ChannelId);
@@ -83,7 +83,7 @@ public static async Task<IResult> Pause(
8383
public static async Task<IResult> Resume(
8484
Guid channelId, Guid jobId, AgentJobService svc, ChatService chatSvc)
8585
{
86-
if (await GetScopedJobAsync(channelId, jobId, svc) is null) return Results.NotFound();
86+
if (await GetScopedJobSummaryAsync(channelId, jobId, svc) is null) return Results.NotFound();
8787
var job = await svc.ResumeAsync(jobId);
8888
if (job is null) return Results.NotFound();
8989
var cost = await chatSvc.GetChannelCostAsync(job.ChannelId);
@@ -96,4 +96,11 @@ public static async Task<IResult> Resume(
9696
var job = await svc.GetAsync(jobId);
9797
return job?.ChannelId == channelId ? job : null;
9898
}
99+
100+
private static async Task<AgentJobSummaryResponse?> GetScopedJobSummaryAsync(
101+
Guid channelId, Guid jobId, AgentJobService svc)
102+
{
103+
var job = await svc.GetSummaryAsync(jobId);
104+
return job?.ChannelId == channelId ? job : null;
105+
}
99106
}

SharpClaw.Application.Core/Modules/HostAgentJobController.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ namespace SharpClaw.Application.Core.Modules;
1010

1111
public sealed class HostAgentJobController(
1212
AgentJobService jobs,
13-
SharpClawDbContext db) : IAgentJobController
13+
SharpClawDbContext db,
14+
ChatCache chatCache) : IAgentJobController
1415
{
1516
public Task<AgentJobResponse> SubmitJobAsync(
1617
Guid channelId,
@@ -33,14 +34,16 @@ public async Task AddJobLogAsync(
3334
var job = await db.AgentJobs.FirstOrDefaultAsync(j => j.Id == jobId, ct);
3435
if (job is null) return;
3536

36-
job.LogEntries.Add(new AgentJobLogEntryDB
37+
var entry = new AgentJobLogEntryDB
3738
{
3839
AgentJobId = jobId,
3940
Message = message,
4041
Level = level,
41-
});
42+
};
43+
job.LogEntries.Add(entry);
4244

4345
await db.SaveChangesAsync(ct);
46+
chatCache.AppendJobLogIfCached(jobId, ToLogResponse(entry));
4447
}
4548

4649
public async Task MarkJobFailedAsync(
@@ -55,14 +58,16 @@ public async Task MarkJobFailedAsync(
5558
job.Status = AgentJobStatus.Failed;
5659
job.CompletedAt = DateTimeOffset.UtcNow;
5760
job.ErrorLog = exception.ToString();
58-
job.LogEntries.Add(new AgentJobLogEntryDB
61+
var entry = new AgentJobLogEntryDB
5962
{
6063
AgentJobId = jobId,
6164
Message = $"Job failed: {exception.Message}",
6265
Level = JobLogLevels.Error,
63-
});
66+
};
67+
job.LogEntries.Add(entry);
6468

6569
await db.SaveChangesAsync(ct);
70+
chatCache.AppendJobLogIfCached(jobId, ToLogResponse(entry));
6671
}
6772

6873
public async Task MarkJobCompletedAsync(
@@ -80,16 +85,18 @@ public async Task MarkJobCompletedAsync(
8085
if (resultData is not null)
8186
job.ResultData = resultData;
8287

83-
job.LogEntries.Add(new AgentJobLogEntryDB
88+
var entry = new AgentJobLogEntryDB
8489
{
8590
AgentJobId = jobId,
8691
Message = string.IsNullOrWhiteSpace(message)
8792
? "Job completed by module."
8893
: message,
8994
Level = JobLogLevels.Info,
90-
});
95+
};
96+
job.LogEntries.Add(entry);
9197

9298
await db.SaveChangesAsync(ct);
99+
chatCache.AppendJobLogIfCached(jobId, ToLogResponse(entry));
93100
}
94101

95102
public async Task MarkJobCancelledAsync(
@@ -103,16 +110,18 @@ public async Task MarkJobCancelledAsync(
103110

104111
job.Status = AgentJobStatus.Cancelled;
105112
job.CompletedAt = DateTimeOffset.UtcNow;
106-
job.LogEntries.Add(new AgentJobLogEntryDB
113+
var entry = new AgentJobLogEntryDB
107114
{
108115
AgentJobId = jobId,
109116
Message = string.IsNullOrWhiteSpace(message)
110117
? "Job cancelled by module."
111118
: message,
112119
Level = JobLogLevels.Warning,
113-
});
120+
};
121+
job.LogEntries.Add(entry);
114122

115123
await db.SaveChangesAsync(ct);
124+
chatCache.AppendJobLogIfCached(jobId, ToLogResponse(entry));
116125
}
117126

118127
public async Task CancelStaleJobsByActionPrefixAsync(
@@ -131,19 +140,29 @@ public async Task CancelStaleJobsByActionPrefixAsync(
131140
.Where(j => j.ActionKey!.StartsWith(actionKeyPrefix, StringComparison.OrdinalIgnoreCase))
132141
.ToList();
133142

143+
var cancelledLogs = new List<(Guid JobId, AgentJobLogEntryDB Entry)>(stale.Count);
134144
foreach (var job in stale)
135145
{
136146
job.Status = AgentJobStatus.Cancelled;
137147
job.CompletedAt = DateTimeOffset.UtcNow;
138-
job.LogEntries.Add(new AgentJobLogEntryDB
148+
var entry = new AgentJobLogEntryDB
139149
{
140150
AgentJobId = job.Id,
141151
Message = "Cancelled: stale from previous session.",
142152
Level = JobLogLevels.Warning,
143-
});
153+
};
154+
job.LogEntries.Add(entry);
155+
cancelledLogs.Add((job.Id, entry));
144156
}
145157

146158
if (stale.Count > 0)
159+
{
147160
await db.SaveChangesAsync(ct);
161+
foreach (var (jobId, entry) in cancelledLogs)
162+
chatCache.AppendJobLogIfCached(jobId, ToLogResponse(entry));
163+
}
148164
}
165+
166+
private static AgentJobLogResponse ToLogResponse(AgentJobLogEntryDB log)
167+
=> new(log.Message, log.Level, log.CreatedAt);
149168
}

0 commit comments

Comments
 (0)