feat: implement CustomAIMessageChunk class and enhance reasoning handling in useMessageOption hook

This commit is contained in:
n4ze3m
2025-02-08 20:50:48 +05:30
parent 8381f1c996
commit 926f4e1a4a
8 changed files with 1220 additions and 105 deletions

View File

@@ -25,7 +25,7 @@ export const getAllOpenAIModels = async (baseUrl: string, apiKey?: string) => {
clearTimeout(timeoutId)
// if Google API fails to return models, try another approach
if (res.status === 401 && res.url == 'https://generativelanguage.googleapis.com/v1beta/openai/models') {
if (res.url == 'https://generativelanguage.googleapis.com/v1beta/openai/models') {
const urlGoogle = `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`
const resGoogle = await fetch(urlGoogle, {
signal: controller.signal

View File

@@ -1,109 +1,100 @@
const tags = ["think", "reason", "reasoning", "thought"]
export function parseReasoning(
text: string
): {
type: "reasoning" | "text"
content: string
reasoning_running?: boolean
export function parseReasoning(text: string): {
type: "reasoning" | "text"
content: string
reasoning_running?: boolean
}[] {
try {
const result: {
type: "reasoning" | "text"
content: string
reasoning_running?: boolean
}[] = []
const tagPattern = new RegExp(`<(${tags.join("|")})>`, "i")
const closeTagPattern = new RegExp(`</(${tags.join("|")})>`, "i")
try {
const result: {
type: "reasoning" | "text"
content: string
reasoning_running?: boolean
}[] = []
const tagPattern = new RegExp(`<(${tags.join("|")})>`, "i")
const closeTagPattern = new RegExp(`</(${tags.join("|")})>`, "i")
let currentIndex = 0
let isReasoning = false
let currentIndex = 0
let isReasoning = false
while (currentIndex < text.length) {
const openTagMatch = text.slice(currentIndex).match(tagPattern)
const closeTagMatch = text.slice(currentIndex).match(closeTagPattern)
while (currentIndex < text.length) {
const openTagMatch = text.slice(currentIndex).match(tagPattern)
const closeTagMatch = text.slice(currentIndex).match(closeTagPattern)
if (!isReasoning && openTagMatch) {
const beforeText = text.slice(
currentIndex,
currentIndex + openTagMatch.index
)
if (beforeText.trim()) {
result.push({ type: "text", content: beforeText.trim() })
}
isReasoning = true
currentIndex += openTagMatch.index! + openTagMatch[0].length
continue
}
if (isReasoning && closeTagMatch) {
const reasoningContent = text.slice(
currentIndex,
currentIndex + closeTagMatch.index
)
if (reasoningContent.trim()) {
result.push({ type: "reasoning", content: reasoningContent.trim() })
}
isReasoning = false
currentIndex += closeTagMatch.index! + closeTagMatch[0].length
continue
}
if (currentIndex < text.length) {
const remainingText = text.slice(currentIndex)
result.push({
type: isReasoning ? "reasoning" : "text",
content: remainingText.trim(),
reasoning_running: isReasoning
})
break
}
if (!isReasoning && openTagMatch) {
const beforeText = text.slice(
currentIndex,
currentIndex + openTagMatch.index
)
if (beforeText.trim()) {
result.push({ type: "text", content: beforeText.trim() })
}
return result
} catch (e) {
console.error(`Error parsing reasoning: ${e}`)
return [
{
type: "text",
content: text
}
]
isReasoning = true
currentIndex += openTagMatch.index! + openTagMatch[0].length
continue
}
if (isReasoning && closeTagMatch) {
const reasoningContent = text.slice(
currentIndex,
currentIndex + closeTagMatch.index
)
if (reasoningContent.trim()) {
result.push({ type: "reasoning", content: reasoningContent.trim() })
}
isReasoning = false
currentIndex += closeTagMatch.index! + closeTagMatch[0].length
continue
}
if (currentIndex < text.length) {
const remainingText = text.slice(currentIndex)
result.push({
type: isReasoning ? "reasoning" : "text",
content: remainingText.trim(),
reasoning_running: isReasoning
})
break
}
}
return result
} catch (e) {
console.error(`Error parsing reasoning: ${e}`)
return [
{
type: "text",
content: text
}
]
}
}
export function isReasoningStarted(text: string): boolean {
const tagPattern = new RegExp(`<(${tags.join("|")})>`, "i")
return tagPattern.test(text)
const tagPattern = new RegExp(`<(${tags.join("|")})>`, "i")
return tagPattern.test(text)
}
export function isReasoningEnded(text: string): boolean {
const closeTagPattern = new RegExp(`</(${tags.join("|")})>`, "i")
return closeTagPattern.test(text)
const closeTagPattern = new RegExp(`</(${tags.join("|")})>`, "i")
return closeTagPattern.test(text)
}
export function removeReasoning(text: string): string {
const tagPattern = new RegExp(
`<(${tags.join("|")})>.*?</(${tags.join("|")})>`,
"gis"
)
return text.replace(tagPattern, "").trim()
const tagPattern = new RegExp(
`<(${tags.join("|")})>.*?</(${tags.join("|")})>`,
"gis"
)
return text.replace(tagPattern, "").trim()
}
export function mergeReasoningContent(originalText: string, reasoning: string): string {
const defaultReasoningTag = "think"
const tagPattern = new RegExp(`<(${tags.join("|")})>(.*?)</(${tags.join("|")})>`, "is")
const hasReasoningTag = tagPattern.test(originalText)
export function mergeReasoningContent(
originalText: string,
reasoning: string
): string {
const reasoningTag = "<think>"
if (hasReasoningTag) {
const match = originalText.match(tagPattern)
if (match) {
const [fullMatch, tag, existingContent] = match
const remainingText = originalText.replace(fullMatch, '').trim()
const newContent = `${existingContent.trim()}${reasoning}`
return `<${tag}>${newContent}</${tag}> ${remainingText}`
}
}
originalText = originalText.replace(reasoningTag, "")
return `<${defaultReasoningTag}>${reasoning}</${defaultReasoningTag}> ${originalText.trim()}`.trim()
}
return `${reasoningTag}${originalText + reasoning}`.trim()
}