fix: Ignore the Chain-of-Thought in AI response (#952)

- Improve chat response processing to handle thinking patterns using regular expressions.
- Migrate server value by removing trailing '/chat/completions' path.
This commit is contained in:
GadflyFang 2025-02-08 17:30:27 +08:00 committed by GitHub
parent 8cc056d2af
commit ef2e0a8a56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 78 additions and 10 deletions

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Avalonia.Threading;
@ -48,16 +49,18 @@ namespace SourceGit.Commands
var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd();
if (rs.IsSuccess)
{
var hasFirstValidChar = false;
var thinkingBuffer = new StringBuilder();
_service.Chat(
_service.AnalyzeDiffPrompt,
$"Here is the `git diff` output: {rs.StdOut}",
_cancelToken,
update =>
{
responseBuilder.Append(update);
summaryBuilder.Append(update);
_onResponse?.Invoke("Waiting for pre-file analyzing to complated...\n\n" + responseBuilder.ToString());
});
ProcessChatResponse(update, ref hasFirstValidChar, thinkingBuffer,
(responseBuilder, text =>
_onResponse?.Invoke(
$"Waiting for pre-file analyzing to completed...\n\n{text}")),
(summaryBuilder, null)));
}
responseBuilder.Append("\n");
@ -71,15 +74,15 @@ namespace SourceGit.Commands
var responseBody = responseBuilder.ToString();
var subjectBuilder = new StringBuilder();
var hasSubjectFirstValidChar = false;
var subjectThinkingBuffer = new StringBuilder();
_service.Chat(
_service.GenerateSubjectPrompt,
$"Here are the summaries changes:\n{summaryBuilder}",
_cancelToken,
update =>
{
subjectBuilder.Append(update);
_onResponse?.Invoke($"{subjectBuilder}\n\n{responseBody}");
});
ProcessChatResponse(update, ref hasSubjectFirstValidChar, subjectThinkingBuffer,
(subjectBuilder, text => _onResponse?.Invoke($"{text}\n\n{responseBody}"))));
}
catch (Exception e)
{
@ -87,10 +90,67 @@ namespace SourceGit.Commands
}
}
private void ProcessChatResponse(
string update,
ref bool hasFirstValidChar,
StringBuilder thinkingBuffer,
params (StringBuilder builder, Action<string> callback)[] outputs)
{
if (!hasFirstValidChar)
{
update = update.TrimStart();
if (string.IsNullOrEmpty(update))
return;
if (update.StartsWith("<", StringComparison.Ordinal))
thinkingBuffer.Append(update);
hasFirstValidChar = true;
}
if (thinkingBuffer.Length > 0)
thinkingBuffer.Append(update);
if (thinkingBuffer.Length > 15)
{
var match = REG_COT.Match(thinkingBuffer.ToString());
if (match.Success)
{
update = REG_COT.Replace(thinkingBuffer.ToString(), "").TrimStart();
if (update.Length > 0)
{
foreach (var output in outputs)
output.builder.Append(update);
thinkingBuffer.Clear();
}
return;
}
match = REG_THINK_START.Match(thinkingBuffer.ToString());
if (!match.Success)
{
foreach (var output in outputs)
output.builder.Append(thinkingBuffer);
thinkingBuffer.Clear();
return;
}
}
if (thinkingBuffer.Length == 0)
{
foreach (var output in outputs)
{
output.builder.Append(update);
output.callback?.Invoke(output.builder.ToString());
}
}
}
private Models.OpenAIService _service;
private string _repo;
private List<Models.Change> _changes;
private CancellationToken _cancelToken;
private Action<string> _onResponse;
private static readonly Regex REG_COT = new(@"^<(think|thought|thinking|thought_chain)>(.*?)</\1>", RegexOptions.Singleline);
private static readonly Regex REG_THINK_START = new(@"^<(think|thought|thinking|thought_chain)>", RegexOptions.Singleline);
}
}

View file

@ -19,7 +19,15 @@ namespace SourceGit.Models
public string Server
{
get => _server;
set => SetProperty(ref _server, value);
set
{
// migrate old server value
if (!string.IsNullOrEmpty(value) && value.EndsWith("/chat/completions", StringComparison.Ordinal))
{
value = value.Substring(0, value.Length - "/chat/completions".Length);
}
SetProperty(ref _server, value);
}
}
public string ApiKey