show reasoning in frontend and some other stuff
This commit is contained in:
+25
-14
@@ -19,6 +19,7 @@ import {
|
||||
} from "./chatMessageHelpers.js";
|
||||
|
||||
import {SearXNGSearchProvider, WebSearchError, buildWebSearchContext, buildWebSearchQuery, decideWebSearch, selectSearchResults, MAX_CANDIDATES} from "./webSearchService.js";
|
||||
import {supportsReasoning} from "../../public/app/modelCapabilities.js";
|
||||
|
||||
export function createChatService(repositories, modelService, config, webSearchProvider = new SearXNGSearchProvider(config)) {
|
||||
const chatGenerationQueues = new Map();
|
||||
@@ -192,21 +193,25 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
], {onThinking});
|
||||
};
|
||||
|
||||
const generateValidatedRoleplayReply = async ({selectedModel, messagesPayload, userPersona, signal = null, generation = {}, onThinking = null}) => {
|
||||
let reply = await modelService.generateReply(selectedModel, messagesPayload, {signal, generation, onThinking});
|
||||
const generateValidatedRoleplayReply = async ({selectedModel, messagesPayload, userPersona, signal = null, generation = {}, onThinking = null, onChunk = null, reasoning = false}) => {
|
||||
const generate = (messages) => onChunk
|
||||
? modelService.streamReply(selectedModel, messages, onChunk, {signal, generation, onThinking, reasoning})
|
||||
: modelService.generateReply(selectedModel, messages, {signal, generation, onThinking, reasoning});
|
||||
let reply = await generate(messagesPayload);
|
||||
if (reply && !containsUserVoiceInRoleplayOpener(reply, userPersona)) {
|
||||
return reply;
|
||||
}
|
||||
|
||||
onThinking?.("", "");
|
||||
reply = await modelService.generateReply(selectedModel, [
|
||||
onChunk?.("", "");
|
||||
reply = await generate([
|
||||
...messagesPayload,
|
||||
{role: "assistant", content: reply || ""},
|
||||
{
|
||||
role: "user",
|
||||
content: "Rewrite your last reply. Hard rule: only the assistant character may speak or act. Never write or imply user speech, thoughts, feelings, reactions, decisions, or actions. Stop before the user's reply."
|
||||
}
|
||||
], {signal, generation, onThinking});
|
||||
]);
|
||||
|
||||
if (!reply || containsUserVoiceInRoleplayOpener(reply, userPersona)) {
|
||||
return null;
|
||||
@@ -320,7 +325,7 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
return {fullReply, thinking, promptMessageId: promptMessage.id};
|
||||
};
|
||||
|
||||
const generateChatTurn = ({user, chatId, message, model, webSearch = false, onStatus = null, onChunk = null, onThinking = null, signal = null, parentMessageId = undefined}) =>
|
||||
const generateChatTurn = ({user, chatId, message, model, webSearch = false, reasoning = false, onStatus = null, onChunk = null, onThinking = null, signal = null, parentMessageId = undefined}) =>
|
||||
withChatGenerationLock(chatId, async () => {
|
||||
await (summaryQueues.get(chatId) || Promise.resolve());
|
||||
const session = repositories.getChat(chatId, user);
|
||||
@@ -337,17 +342,22 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
return {error: "Branch point not found", status: 404};
|
||||
}
|
||||
const {model: effectiveModel, generation} = getGenerationSettings(session, model);
|
||||
const useReasoning = reasoning === true && supportsReasoning(effectiveModel);
|
||||
signal?.throwIfAborted();
|
||||
const inserted = repositories.insertBranchChatMessage(chatId, "user", message, null, null, "pending", null, effectiveParentId);
|
||||
const userMessageId = Number(inserted?.lastInsertRowid);
|
||||
try {
|
||||
let sources = null;
|
||||
let thinking = "";
|
||||
const captureThinking = (chunk, fullThinking) => {
|
||||
if (!useReasoning) return;
|
||||
thinking = fullThinking;
|
||||
onThinking?.(chunk, fullThinking);
|
||||
};
|
||||
let payload;
|
||||
const fullReply = await withActiveGeneration(chatId, signal, async (activeSignal) => {
|
||||
activeSignal.throwIfAborted();
|
||||
onStatus?.({status: "accepted"});
|
||||
if (webSearch) {
|
||||
const searchDecision = decideWebSearch(message);
|
||||
let searchQuery = null;
|
||||
@@ -364,7 +374,6 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
candidateCount = candidates.length;
|
||||
sources = selectSearchResults(message, candidates, searchDecision);
|
||||
selectedCount = sources.length;
|
||||
onStatus?.({status: "generating", sourceCount: selectedCount});
|
||||
}
|
||||
if (config.structuredRequestLoggingEnabled) {
|
||||
console.log(JSON.stringify({
|
||||
@@ -379,6 +388,7 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
}));
|
||||
}
|
||||
}
|
||||
onStatus?.({status: "generating", ...(sources !== null ? {sourceCount: sources.length} : {})});
|
||||
payload = buildConversationContext({user, chatId, session, message: null, sources});
|
||||
return payload.activePersona
|
||||
? generateValidatedRoleplayReply({
|
||||
@@ -387,18 +397,19 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
userPersona: payload.activeUserPersona,
|
||||
signal: activeSignal,
|
||||
generation,
|
||||
onThinking: captureThinking
|
||||
onThinking: captureThinking,
|
||||
onChunk,
|
||||
reasoning: useReasoning
|
||||
})
|
||||
: onChunk
|
||||
? modelService.streamReply(effectiveModel, payload.messagesPayload, onChunk, {signal: activeSignal, generation, onThinking: captureThinking})
|
||||
: modelService.generateReply(effectiveModel, payload.messagesPayload, {signal: activeSignal, generation, onThinking: captureThinking});
|
||||
? modelService.streamReply(effectiveModel, payload.messagesPayload, onChunk, {signal: activeSignal, generation, onThinking: captureThinking, reasoning: useReasoning})
|
||||
: modelService.generateReply(effectiveModel, payload.messagesPayload, {signal: activeSignal, generation, onThinking: captureThinking, reasoning: useReasoning});
|
||||
});
|
||||
|
||||
if (!fullReply) {
|
||||
repositories.failChatTurn(chatId, userMessageId, "Failed to generate a valid character reply", webSearch ? userMessageId : effectiveParentId);
|
||||
return {error: "Failed to generate a valid character reply", status: 500};
|
||||
}
|
||||
if (payload.activePersona && onChunk) onChunk(fullReply, fullReply);
|
||||
|
||||
const retryState = {
|
||||
retryVariants: [fullReply],
|
||||
@@ -508,11 +519,11 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
containsUserVoiceInRoleplayOpener,
|
||||
persistChatMessageRetryState,
|
||||
buildMessagesPayload: buildConversationContext,
|
||||
async sendChatMessage({user, chatId, message, model, webSearch = false}) {
|
||||
return generateChatTurn({user, chatId, message, model, webSearch});
|
||||
async sendChatMessage({user, chatId, message, model, webSearch = false, reasoning = false}) {
|
||||
return generateChatTurn({user, chatId, message, model, webSearch, reasoning});
|
||||
},
|
||||
async streamChatMessage({user, chatId, message, model, webSearch = false, onStatus, onChunk, onThinking, signal = null}) {
|
||||
return generateChatTurn({user, chatId, message, model, webSearch, onStatus, onChunk, onThinking, signal});
|
||||
async streamChatMessage({user, chatId, message, model, webSearch = false, reasoning = false, onStatus, onChunk, onThinking, signal = null}) {
|
||||
return generateChatTurn({user, chatId, message, model, webSearch, reasoning, onStatus, onChunk, onThinking, signal});
|
||||
},
|
||||
async retryMessage({user, chatId, selectedModel, targetMessage, retryStyle = null}) {
|
||||
return withChatGenerationLock(chatId, async () => {
|
||||
|
||||
Reference in New Issue
Block a user