Skip to content

Commit

Permalink
feat(chat): Create chat definition contract and support multiple outp…
Browse files Browse the repository at this point in the history
…uts (#25)

- Create IChatDefinition contract for configuring chat execution
- Move message definition to Chats context
- Rename AllowedModel to LlmModelType and move to Chats context
- Support multiple outputs
  • Loading branch information
skarllot authored Jan 3, 2025
1 parent 879d6a2 commit 13f575e
Show file tree
Hide file tree
Showing 36 changed files with 763 additions and 503 deletions.
2 changes: 0 additions & 2 deletions src/FlowPair/Agent/Infrastructure/AgentJsonContext.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using Ciandt.FlowTools.FlowPair.Agent.Operations.ReviewChanges.v1;
using Ciandt.FlowTools.FlowPair.Flow.Operations.ProxyCompleteChat.v1;

namespace Ciandt.FlowTools.FlowPair.Agent.Infrastructure;

Expand All @@ -12,5 +11,4 @@ namespace Ciandt.FlowTools.FlowPair.Agent.Infrastructure;
PropertyNameCaseInsensitive = true,
RespectNullableAnnotations = true)]
[JsonSerializable(typeof(ImmutableList<ReviewerFeedbackResponse>))]
[JsonSerializable(typeof(ImmutableList<ImmutableList<Message>>))]
public partial class AgentJsonContext : JsonSerializerContext;
5 changes: 2 additions & 3 deletions src/FlowPair/Agent/Infrastructure/IAgentModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Ciandt.FlowTools.FlowPair.Agent.Operations.Login;
using Ciandt.FlowTools.FlowPair.Agent.Operations.ReviewChanges;
using Ciandt.FlowTools.FlowPair.Agent.Services;
using Jab;

namespace Ciandt.FlowTools.FlowPair.Agent.Infrastructure;
Expand All @@ -10,8 +9,8 @@ namespace Ciandt.FlowTools.FlowPair.Agent.Infrastructure;
// Infrastructure
[Singleton(typeof(AgentJsonContext), Factory = nameof(GetJsonContext))]

// Services
[Singleton(typeof(IChatService), typeof(ChatService))]
// Chat definitions
[Singleton(typeof(IReviewChatDefinition), typeof(ReviewChatDefinition))]

// Operations
[Singleton(typeof(ILoginUseCase), typeof(LoginUseCase))]
Expand Down
24 changes: 10 additions & 14 deletions src/FlowPair/Agent/Operations/ReviewChanges/ReviewChangesCommand.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System.Collections.Immutable;
using Ciandt.FlowTools.FlowPair.Agent.Infrastructure;
using Ciandt.FlowTools.FlowPair.Agent.Models;
using Ciandt.FlowTools.FlowPair.Agent.Operations.Login;
using Ciandt.FlowTools.FlowPair.Agent.Operations.ReviewChanges.v1;
using Ciandt.FlowTools.FlowPair.Agent.Services;
using Ciandt.FlowTools.FlowPair.Chats.Models;
using Ciandt.FlowTools.FlowPair.Chats.Services;
using Ciandt.FlowTools.FlowPair.Common;
using Ciandt.FlowTools.FlowPair.Flow.Operations.ProxyCompleteChat.v1;
using Ciandt.FlowTools.FlowPair.Git.GetChanges;
using Ciandt.FlowTools.FlowPair.LocalFileSystem.Services;
using Ciandt.FlowTools.FlowPair.Support.Presentation;
Expand All @@ -16,7 +14,7 @@ namespace Ciandt.FlowTools.FlowPair.Agent.Operations.ReviewChanges;

public sealed class ReviewChangesCommand(
IAnsiConsole console,
AgentJsonContext jsonContext,
IReviewChatDefinition chatDefinition,
IGitGetChangesHandler getChangesHandler,
ILoginUseCase loginUseCase,
IChatService chatService,
Expand Down Expand Up @@ -44,10 +42,10 @@ from feedback in BuildFeedback(diff)
private Result<Unit, int> BuildFeedback(ImmutableList<FileChange> changes)
{
var feedback = changes
.GroupBy(c => ChatScript.FindChatScriptForFile(ReviewChatScript.Default, c.Path))
.GroupBy(c => ChatScript.FindChatScriptForFile([chatDefinition.ChatScript], c.Path))
.Where(g => g.Key.IsSome)
.Select(g => new { Script = g.Key.Unwrap(), Diff = g.AggregateToStringLines(c => c.Diff) })
.SelectMany(x => GetFeedback(x.Diff, x.Script))
.SelectMany(x => GetFeedback(x.Diff))
.Where(f => !string.IsNullOrWhiteSpace(f.Feedback))
.OrderByDescending(x => x.RiskScore).ThenBy(x => x.Path, StringComparer.OrdinalIgnoreCase)
.ToImmutableList();
Expand All @@ -67,15 +65,13 @@ private Result<Unit, int> BuildFeedback(ImmutableList<FileChange> changes)
}

private ImmutableList<ReviewerFeedbackResponse> GetFeedback(
string diff,
ChatScript chatScript)
string diff)
{
return chatService.RunMultiple(
return chatService.Run(
console.Progress(),
AllowedModel.Claude35Sonnet,
chatScript,
[new Message(Role.User, diff)],
jsonContext.ImmutableListReviewerFeedbackResponse)
LlmModelType.Claude35Sonnet,
chatDefinition,
[new Message(SenderRole.User, diff)])
.DoErr(error => console.MarkupLineInterpolated($"[red]Error:[/] {error}"))
.UnwrapOrElse(static () => []);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System.Collections.Immutable;
using Ciandt.FlowTools.FlowPair.Agent.Infrastructure;
using Ciandt.FlowTools.FlowPair.Agent.Operations.ReviewChanges.v1;
using Ciandt.FlowTools.FlowPair.Chats.Contracts.v1;
using Ciandt.FlowTools.FlowPair.Chats.Models;
using Ciandt.FlowTools.FlowPair.Chats.Services;

namespace Ciandt.FlowTools.FlowPair.Agent.Operations.ReviewChanges;

public interface IReviewChatDefinition : IChatDefinition<ImmutableList<ReviewerFeedbackResponse>>;

public sealed class ReviewChatDefinition(
AgentJsonContext jsonContext)
: IReviewChatDefinition
{
private const string JsonResponseKey = "ReviewerFeedbackResponse";

public ChatScript ChatScript { get; } = new(
"Code review chat script",
[
/* Python */".py", ".pyw", ".pyx", ".pxd", ".pxi",
/* JavaScript */".js", ".jsx", ".mjs", ".cjs",
/* Java */".java",
/* C# */".cs", ".csx",
/* C++ */".cpp", ".cxx", ".cc", ".c++", ".hpp", ".hxx", ".h", ".hh", ".h++",
/* PHP */".php", ".phtml", ".phps",
/* Ruby */".rb", ".rbw", ".rake",
/* Swift */".swift",
/* R */".r",
/* SQL */".sql",
/* Kotlin */".kt", ".kts",
/* TypeScript */".ts", ".tsx",
/* Go (Golang) */".go",
/* Rust */".rs",
/* Scala */".scala", ".sc",
/* Dart */".dart",
/* Perl */".pl", ".pm", ".t", ".pod",
/* MATLAB */".m",
/* VBA */".bas", ".cls", ".frm",
/* Shell Scripting */".sh", ".bash", ".zsh", ".ksh", ".csh", ".tcsh", ".fish",
],
"""
You are an expert developer, your task is to review a set of changes on Git commits.
You are given a set of Git patches, containing the filenames and their partial contents. Note that you might not have the full context of the code.
Only review lines of code which have been changed (added or removed) in the pull request. Other lines are added to provide context but should be ignored in the review.
Begin your feedback by evaluating the changed code using a risk score similar to a LOGAF score but measured from 0 to 3, where 0 is the lowest risk to the codebase if the code is merged and 3 is the highest risk which would likely break something or be unsafe. Risk score should be described as "0 - Not important", "1 - Low priority adjustments", "2 - Medium priority adjustments" or "3 - High priority adjustments".
Only provide feedback on critical issues. If the code is already well-written or issues are minor, do not provide any feedback.
Avoid commenting on breaking functions down into smaller, more manageable functions unless it is a significant problem. Be aware that there will be libraries and techniques used which you might not be familiar with, so do not comment on those unless you are confident that there is a problem.
""",
[
Instruction.MultiStepInstruction.Of(
"Give feedback to ",
[
"improve readability where it can significantly impacts understanding",
"make code cleaner where it introduces substantial benefits",
"maximize the performance of the code where there is a clear, impactful improvement",
"flag any API keys or secrets present in plain text immediately as highest risk",
"rate the changes based on SOLID principles",
"apply the principles of DRY, KISS, YAGNI and Clean Code",
"avoid magic strings and numbers",
"ensure new code follow existing patterns and structure",
],
$" if applicable; otherwise, reply with \"{ChatScript.StopKeywordPlaceholder}\" when there are no suggestions"),
Instruction.StepInstruction.Of(
"""
Ensure the feedback contain the file path and the line number.
Do not provide positive reinforcement or comments on good decisions. Focus solely on areas that need improvement.
Ensure the feedback details are brief, concise, and accurate. If there are multiple similar issues, only comment on the most critical.
"""),
Instruction.StepInstruction.Of(
"""
Include brief example code snippets in the feedback details for your suggested changes when you're confident your suggestions are improvements.
Use the same programming language as the file under review. If there are multiple improvements you suggest in the feedback details, use an ordered list to indicate the priority of the changes.
"""),
Instruction.JsonConvertInstruction.Of(
JsonResponseKey,
"""
Format the feedback in a valid JSON format as a list of feedbacks, or "[]" for no feedbacks.
The "feedback" property can be multiline and include example code snippets.
The schema of the JSON feedback object must be:
""",
ReviewerFeedbackResponse.Schema),
]);

public Result<object, string> Parse(string key, string input) => key switch
{
JsonResponseKey => ContentDeserializer
.TryDeserialize(input, jsonContext.ImmutableListReviewerFeedbackResponse)
.Select(static object (x) => x),
_ => $"Unknown output key '{key}'"
};

public Option<ImmutableList<ReviewerFeedbackResponse>> ConvertResult(ChatWorkspace chatWorkspace) =>
OutputProcessor.AggregateLists<ReviewerFeedbackResponse>(chatWorkspace, JsonResponseKey);
}
77 changes: 0 additions & 77 deletions src/FlowPair/Agent/Operations/ReviewChanges/ReviewChatScript.cs

This file was deleted.

81 changes: 0 additions & 81 deletions src/FlowPair/Agent/Services/ChatService.cs

This file was deleted.

12 changes: 12 additions & 0 deletions src/FlowPair/Chats/Contracts/v1/IChatDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Ciandt.FlowTools.FlowPair.Chats.Models;
using Ciandt.FlowTools.FlowPair.Chats.Services;

namespace Ciandt.FlowTools.FlowPair.Chats.Contracts.v1;

public interface IChatDefinition<TResult> : IMessageParser
where TResult : notnull
{
ChatScript ChatScript { get; }

Option<TResult> ConvertResult(ChatWorkspace chatWorkspace);
}
15 changes: 15 additions & 0 deletions src/FlowPair/Chats/Infrastructure/ChatJsonContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using Ciandt.FlowTools.FlowPair.Chats.Models;

namespace Ciandt.FlowTools.FlowPair.Chats.Infrastructure;

[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = true,
PropertyNameCaseInsensitive = true,
RespectNullableAnnotations = true)]
[JsonSerializable(typeof(ImmutableList<ImmutableList<Message>>))]
public partial class ChatJsonContext : JsonSerializerContext;
16 changes: 16 additions & 0 deletions src/FlowPair/Chats/Infrastructure/IChatModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Ciandt.FlowTools.FlowPair.Chats.Services;
using Jab;

namespace Ciandt.FlowTools.FlowPair.Chats.Infrastructure;

[ServiceProviderModule]

// Infrastructure
[Singleton(typeof(ChatJsonContext), Factory = nameof(GetJsonContext))]

// Services
[Singleton(typeof(IChatService), typeof(ChatService))]
public interface IChatModule
{
static ChatJsonContext GetJsonContext() => ChatJsonContext.Default;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Collections.Immutable;

namespace Ciandt.FlowTools.FlowPair.Agent.Models;
namespace Ciandt.FlowTools.FlowPair.Chats.Models;

public sealed record ChatScript(
string Name,
Expand Down
Loading

0 comments on commit 13f575e

Please sign in to comment.