Skip to content

Commit c70fb62

Browse files
committed
feat(translate): remove reasoning from LLM reply (supports Qwen, DeepSeek) [Fixes #98]
1 parent 7f7c670 commit c70fb62

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

FlyleafLib/MediaPlayer/Translation/Services/OpenAIBaseTranslateService.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ private static async Task<string> SendChatRequest(
189189

190190
OpenAIResponse? chatResponse = JsonSerializer.Deserialize<OpenAIResponse>(jsonResultString);
191191
string reply = chatResponse!.choices[0].message.content;
192+
if (settings.ReasonStripRequired)
193+
{
194+
reply = ChatReplyParser.StripReasoning(reply);
195+
}
192196

193197
return reply.Trim();
194198
}
@@ -276,3 +280,57 @@ public class OpenAIChoice
276280
{
277281
public OpenAIMessage message { get; set; }
278282
}
283+
284+
public static class ChatReplyParser
285+
{
286+
// Target tag names to remove (lowercase)
287+
private static readonly string[] Tags = ["think", "reason", "reasoning", "thought"];
288+
289+
// open/close tag strings from tag names
290+
private static readonly string[] OpenTags;
291+
private static readonly string[] CloseTags;
292+
293+
static ChatReplyParser()
294+
{
295+
OpenTags = new string[Tags.Length];
296+
CloseTags = new string[Tags.Length];
297+
for (int i = 0; i < Tags.Length; i++)
298+
{
299+
OpenTags[i] = $"<{Tags[i]}>"; // e.g. "<think>"
300+
CloseTags[i] = $"</{Tags[i]}>"; // e.g. "</think>"
301+
}
302+
}
303+
304+
/// <summary>
305+
/// Removes a leading reasoning tag if present and returns only the generated message portion.
306+
/// </summary>
307+
public static string StripReasoning(string input)
308+
{
309+
// Return immediately if it doesn't start with a tag
310+
if (string.IsNullOrEmpty(input) || input[0] != '<')
311+
return input;
312+
313+
var span = input.AsSpan();
314+
315+
for (int i = 0; i < OpenTags.Length; i++)
316+
{
317+
if (span.StartsWith(OpenTags[i], StringComparison.OrdinalIgnoreCase))
318+
{
319+
int endIdx = span.IndexOf(CloseTags[i], StringComparison.OrdinalIgnoreCase);
320+
if (endIdx >= 0)
321+
{
322+
int next = endIdx + CloseTags[i].Length;
323+
// Skip over any consecutive line breaks and whitespace
324+
while (next < span.Length && char.IsWhiteSpace(span[next]))
325+
{
326+
next++;
327+
}
328+
return span.Slice(next).ToString();
329+
}
330+
}
331+
}
332+
333+
// Return original string if no tag matched
334+
return input;
335+
}
336+
}

0 commit comments

Comments
 (0)