Skip to content

Commit

Permalink
Update to use JobLogger
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed Aug 19, 2024
1 parent 424a1f6 commit 383d295
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 45 deletions.
25 changes: 19 additions & 6 deletions MyApp.ServiceInterface/AiServer/CreateAnswerCommentTaskCommand.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using AiServer.ServiceModel;
using Microsoft.Extensions.Logging;
using MyApp.Data;
using MyApp.ServiceModel;
using ServiceStack;
using ServiceStack.Jobs;

namespace MyApp.ServiceInterface.AiServer;

Expand All @@ -17,7 +19,8 @@ public class CreateAnswerCommentTask
}

[Tag(Tags.AI)]
public class CreateAnswerCommentTaskCommand(AppConfig appConfig) : AsyncCommand<CreateAnswerCommentTask>
public class CreateAnswerCommentTaskCommand(ILogger<CreateAnswerCommentTaskCommand> logger,
IBackgroundJobs jobs, AppConfig appConfig) : AsyncCommand<CreateAnswerCommentTask>
{
public const string SystemPrompt =
"""
Expand All @@ -31,6 +34,7 @@ I will provide you with my original question and your initial answer attempt to

protected override async Task RunAsync(CreateAnswerCommentTask request, CancellationToken token)
{
var log = Request.CreateJobLogger(jobs, logger);
var question = request.Question;

request.AiRef ??= Guid.NewGuid().ToString("N");
Expand Down Expand Up @@ -81,19 +85,28 @@ Max 2-3 sentences.
});

var client = appConfig.CreateAiServerClient();
var replyTo = appConfig.BaseUrl.CombineWith("api", nameof(AnswerCommentCallback).AddQueryParams(new()
{
[nameof(AnswerCommentCallback.AnswerId)] = request.Answer.RefId,
[nameof(AnswerCommentCallback.UserId)] = request.UserId,
[nameof(AnswerCommentCallback.AiRef)] = request.AiRef,
}));

var startedAt = DateTime.UtcNow;
log.LogInformation("Sending CreateOpenAiChat for Question {Id} Answer Comment for {Model}, replyTo: {ReplyTo}",
question.Id, request.Model, replyTo);

var api = await client.ApiAsync(new CreateOpenAiChat {
RefId = request.AiRef,
Tag = "pvq",
Provider = null,
ReplyTo = appConfig.BaseUrl.CombineWith("api", nameof(AnswerCommentCallback).AddQueryParams(new() {
[nameof(AnswerCommentCallback.AnswerId)] = request.Answer.RefId,
[nameof(AnswerCommentCallback.UserId)] = request.UserId,
[nameof(AnswerCommentCallback.AiRef)] = request.AiRef,
})),
ReplyTo = replyTo,
Request = openAiChat
});

log.LogInformation("Completed CreateOpenAiChat for Question {Id} Answer Comment for {Model} in {Ms}: {Status}",
question.Id, request.Model, (int)(DateTime.UtcNow - startedAt).TotalMilliseconds,
api.Error == null ? "OK" : $"{api.Error.ErrorCode}: {api.Error.Message}");
api.ThrowIfError();
}
}
31 changes: 21 additions & 10 deletions MyApp.ServiceInterface/AiServer/CreateAnswerTasksCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
using MyApp.Data;
using MyApp.ServiceModel;
using ServiceStack;
using ServiceStack.Jobs;

namespace MyApp.ServiceInterface.AiServer;

[Tag(Tags.AI)]
public class CreateAnswerTasksCommand(ILogger<CreateAnswerTasksCommand> log,
AppConfig appConfig, QuestionsProvider questions) : IAsyncCommand<CreateAnswerTasks>
public class CreateAnswerTasksCommand(ILogger<CreateAnswerTasksCommand> logger, IBackgroundJobs jobs, AppConfig appConfig)
: AsyncCommand<CreateAnswerTasks>
{
//https://github.com/f/awesome-chatgpt-prompts?tab=readme-ov-file#act-as-an-it-expert
//https://github.com/f/awesome-chatgpt-prompts?tab=readme-ov-file#act-as-a-developer-relations-consultant
Expand Down Expand Up @@ -40,12 +41,13 @@ public static string CreateQuestionPrompt(Post question)
return content;
}

public async Task ExecuteAsync(CreateAnswerTasks request)
protected override async Task RunAsync(CreateAnswerTasks request, CancellationToken token)
{
var question = request.Post;
if (question == null)
throw new ArgumentNullException(nameof(request.Post));

var log = Request.CreateJobLogger(jobs, logger);
if (request.ModelUsers == null || request.ModelUsers.Count == 0)
{
log.LogError("Missing ModelUsers for question {Id}", question.Id);
Expand All @@ -63,6 +65,7 @@ public async Task ExecuteAsync(CreateAnswerTasks request)
foreach (var userName in request.ModelUsers)
{
ApplicationUser? modelUser;
var startedAt = DateTime.UtcNow;
try
{
modelUser = appConfig.GetModelUser(userName);
Expand All @@ -72,7 +75,6 @@ public async Task ExecuteAsync(CreateAnswerTasks request)
continue;
}

log.LogInformation("Creating Question {Id} OpenAiChat Model for {UserName} to AI Server", question.Id, userName);
var prompt = CreateQuestionPrompt(question);
var openAiChat = new OpenAiChat
{
Expand All @@ -84,19 +86,28 @@ public async Task ExecuteAsync(CreateAnswerTasks request)
Temperature = 0.7,
MaxTokens = 2048,
};
var replyTo = appConfig.BaseUrl.CombineWith("api", nameof(CreateAnswerCallback).AddQueryParams(new() {
[nameof(CreateAnswerCallback.PostId)] = question.Id,
[nameof(CreateAnswerCallback.UserId)] = modelUser.Id,
}));

log.LogInformation("Sending CreateOpenAiChat for Question {Id} Answer for {UserName}, replyTo: {ReplyTo}",
question.Id, userName, replyTo);
var response = await client.PostAsync(new CreateOpenAiChat
{
Tag = "pvq",
ReplyTo = appConfig.BaseUrl.CombineWith("api", nameof(CreateAnswerCallback).AddQueryParams(new() {
[nameof(CreateAnswerCallback.PostId)] = question.Id,
[nameof(CreateAnswerCallback.UserId)] = modelUser.Id,
})),
ReplyTo = replyTo,
Request = openAiChat
});
}, token);

log.LogInformation("Completed CreateOpenAiChat for Question {Id} Answer for {UserName} in {Ms}: OK",
question.Id, userName, (int)(DateTime.UtcNow - startedAt).TotalMilliseconds);
}
catch (Exception e)
{
log.LogError(e, "Failed to Creating Question {Id} OpenAiChat Model for {UserName}", question.Id, userName);
log.LogError(e, "Completed CreateOpenAiChat for Question {Id} Answer for {UserName} in {Ms}: {Status}",
question.Id, userName, (int)(DateTime.UtcNow - startedAt).TotalMilliseconds,
$"{(e as WebServiceException)?.ErrorCode ?? e.GetType().Name}: {e.Message}");
}
}
}
Expand Down
31 changes: 22 additions & 9 deletions MyApp.ServiceInterface/AiServer/CreateRankAnswerTaskCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using MyApp.Data;
using Microsoft.Extensions.Logging;
using MyApp.Data;
using MyApp.ServiceModel;
using ServiceStack;
using ServiceStack.Jobs;

namespace MyApp.ServiceInterface.AiServer;

Expand All @@ -11,8 +13,9 @@ public class CreateRankAnswerTask
}

[Tag(Tags.AI)]
public class CreateRankAnswerTaskCommand(AppConfig appConfig, QuestionsProvider questions)
: IAsyncCommand<CreateRankAnswerTask>
public class CreateRankAnswerTaskCommand(ILogger<CreateRankAnswerTaskCommand> logger, IBackgroundJobs jobs,
AppConfig appConfig, QuestionsProvider questions)
: AsyncCommand<CreateRankAnswerTask>
{
//https://github.com/f/awesome-chatgpt-prompts?tab=readme-ov-file#act-as-a-tech-reviewer
public const string SystemPrompt =
Expand All @@ -22,8 +25,9 @@ I will give you the user's question and the answer that you should review and re
Before giving a score, give a critique of the answer based on quality and relevance to the user's question.
""";

public async Task ExecuteAsync(CreateRankAnswerTask request)
protected override async Task RunAsync(CreateRankAnswerTask request, CancellationToken token)
{
var log = Request.CreateJobLogger(jobs, logger);
var postId = request.AnswerId.LeftPart('-').ToInt();
var question = await questions.GetLocalQuestionFiles(postId).GetQuestionAsync();
if (question == null)
Expand Down Expand Up @@ -75,16 +79,22 @@ Concisely articulate what a good answer needs to contain and how the answer prov
Use code fences, aka triple backticks, to encapsulate your JSON object.
""";

var replyTo = appConfig.BaseUrl.CombineWith("api", nameof(RankAnswerCallback).AddQueryParams(new() {
[nameof(RankAnswerCallback.PostId)] = postId,
[nameof(RankAnswerCallback.UserId)] = request.UserId,
[nameof(RankAnswerCallback.Grader)] = "mixtral",
}));

var startedAt = DateTime.UtcNow;
log.LogInformation("Sending CreateOpenAiChat for Question {Id} Rank Answer, replyTo: {ReplyTo}",
question.Id, replyTo);

var client = appConfig.CreateAiServerClient();
var api = await client.ApiAsync(new CreateOpenAiChat {
RefId = Guid.NewGuid().ToString("N"),
Tag = "pvq",
Provider = null,
ReplyTo = appConfig.BaseUrl.CombineWith("api", nameof(RankAnswerCallback).AddQueryParams(new() {
[nameof(RankAnswerCallback.PostId)] = postId,
[nameof(RankAnswerCallback.UserId)] = request.UserId,
[nameof(RankAnswerCallback.Grader)] = "mixtral",
})),
ReplyTo = replyTo,
Request = new()
{
Model = "mixtral",
Expand All @@ -98,6 +108,9 @@ Concisely articulate what a good answer needs to contain and how the answer prov
}
});

log.LogInformation("Completed CreateOpenAiChat for Question {Id} Rank Answer in {Ms}: {Status}",
question.Id, (int)(DateTime.UtcNow - startedAt).TotalMilliseconds,
api.Error == null ? "OK" : $"{api.Error.ErrorCode}: {api.Error.Message}");
api.ThrowIfError();
}
}
4 changes: 3 additions & 1 deletion MyApp.ServiceInterface/App/CreatePostCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
using MyApp.Data;
using MyApp.ServiceModel;
using ServiceStack;
using ServiceStack.Jobs;
using ServiceStack.OrmLite;

namespace MyApp.ServiceInterface.App;

[Worker(Databases.App)]
[Tag(Tags.Questions)]
public class CreatePostCommand(ILogger<CreatePostCommand> log, AppConfig appConfig, IDbConnection db) : AsyncCommand<Post>
public class CreatePostCommand(ILogger<CreatePostCommand> logger, IBackgroundJobs jobs, AppConfig appConfig, IDbConnection db) : AsyncCommand<Post>
{
protected override async Task RunAsync(Post post, CancellationToken token)
{
var log = Request.CreateJobLogger(jobs, logger);
var body = post.Body;
post.Body = null;

Expand Down
12 changes: 4 additions & 8 deletions MyApp.ServiceInterface/App/SendWatchedTagEmailsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ namespace MyApp.ServiceInterface.App;

[Tag(Tags.Database)]
[Worker(Databases.App)]
public class SendWatchedTagEmailsCommand(ILogger<SendWatchedTagEmailsCommand> log,
public class SendWatchedTagEmailsCommand(ILogger<SendWatchedTagEmailsCommand> logger,
IBackgroundJobs jobs, IDbConnectionFactory dbFactory, EmailRenderer renderer)
: AsyncCommand
{
protected override async Task RunAsync(CancellationToken token)
{
var job = Request.GetBackgroundJob();
var log = Request.CreateJobLogger(jobs, logger);
//var job = Request.GetBackgroundJob();
var yesterday = DateTime.UtcNow.AddDays(-1).Date;
var day = yesterday.ToString("yyyy-MM-dd");
using var db = await dbFactory.OpenDbConnectionAsync(token:token);
Expand All @@ -33,7 +34,6 @@ protected override async Task RunAsync(CancellationToken token)
if (newPosts.Count == 0)
{
log.LogInformation("No new posts found for {Date}", day);
jobs.UpdateJobStatus(new(job, log:"No new posts"));
return;
}

Expand All @@ -54,7 +54,6 @@ protected override async Task RunAsync(CancellationToken token)
if (watchTags.Count == 0)
{
log.LogInformation("No Tag Watchers found for {Date}", day);
jobs.UpdateJobStatus(new(job, log:"No Tag Watchers found"));
return;
}

Expand Down Expand Up @@ -87,11 +86,8 @@ protected override async Task RunAsync(CancellationToken token)
CreatedDate = DateTime.UtcNow,
};
watchPostMail.Id = (int)await db.InsertAsync(watchPostMail, selectIdentity: true, token:token);
log.LogInformation(
"Created {Day} WatchPostMail {Id} for {Tag} with posts:{PostIds} for users:{UserNames}",
log.LogInformation("Created {Day} WatchPostMail {Id} for {Tag} with posts:{PostIds} for users:{UserNames}",
day, watchPostMail.Id, tag, postIds.Join(","), userNames.Join(","));
jobs.UpdateJobStatus(new(job, log:
$"Created {day} WatchPostMail {watchPostMail.Id} for {tag} with posts:{postIds.Join(",")} for users:{userNames.Join(",")}"));

var layout = "tags";
var template = "tagged-questions";
Expand Down
26 changes: 15 additions & 11 deletions MyApp.ServiceInterface/Renderers/RegenerateMetaCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,20 @@ public class RegenerateMeta

[Tag(Tags.Renderer)]
public class RegenerateMetaCommand(
ILogger<RegenerateMetaCommand> log,
ILogger<RegenerateMetaCommand> logger,
IBackgroundJobs jobs,
IDbConnectionFactory dbFactory,
QuestionsProvider questions,
RendererCache cache,
IBackgroundJobs jobs)
: IAsyncCommand<RegenerateMeta, QuestionAndAnswers?>
RendererCache cache)
: AsyncCommandWithResult<RegenerateMeta, QuestionAndAnswers?>
{
public QuestionAndAnswers? Result { get; set; }
public async Task ExecuteAsync(RegenerateMeta question)
protected override async Task<QuestionAndAnswers?> RunAsync(RegenerateMeta question, CancellationToken token)
{
CancellationToken token = new();
var id = question.IfPostModified.GetValueOrDefault(question.ForPost ?? 0);
if (id < 0)
throw new ArgumentNullException(nameof(id));

var log = Request.CreateJobLogger(jobs, logger);
// Whether to rerender the Post HTML
using var db = await dbFactory.OpenDbConnectionAsync(token: token);
var localFiles = questions.GetLocalQuestionFiles(id);
Expand All @@ -47,7 +46,7 @@ public async Task ExecuteAsync(RegenerateMeta question)
if (regenerateMeta)
{
log.LogInformation("Regenerating Meta for Post {Id}...", id);
await RegenerateMeta(db, dbAnalytics, id, remoteFiles, dbStatTotals, allPostVotes, token);
await RegenerateMeta(log, db, dbAnalytics, id, remoteFiles, dbStatTotals, allPostVotes, token);
jobs.RunCommand<UpdateReputationsCommand>();
}

Expand Down Expand Up @@ -75,9 +74,14 @@ public async Task ExecuteAsync(RegenerateMeta question)

if (rerenderPostHtml)
{
Result = await localFiles.GetQuestionAsync();
jobs.RunCommand("RenderQuestionPostCommand", question);
var result = await localFiles.GetQuestionAsync();
if (result != null)
{
jobs.RunCommand("RenderQuestionPostCommand", result);
}
return result;
}
return null;
}

public async Task<bool> ShouldRegenerateMeta(
Expand Down Expand Up @@ -127,7 +131,7 @@ public async Task<bool> ShouldRegenerateMeta(
return recalculateMeta;
}

public async Task RegenerateMeta(IDbConnection db, IDbConnection dbAnalytics, int id, QuestionFiles remoteFiles,
public async Task RegenerateMeta(JobLogger log, IDbConnection db, IDbConnection dbAnalytics, int id, QuestionFiles remoteFiles,
List<StatTotals> dbStatTotals, List<Vote> allPostVotes, CancellationToken token)
{
var now = DateTime.Now;
Expand Down

0 comments on commit 383d295

Please sign in to comment.