mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 09:20:15 +01:00
🛠️ fix: RunManager, AssistantService and useContentHandler Issues (#1920)
* fix(useContentHandler): retain undefined parts and handle them within `ContentParts` rendering * fix(AssistantService/in_progress): skip empty messages * refactor(RunManager): create highly specific `seenSteps` Set keys for RunSteps with use of `getDetailsSignature` and `getToolCallSignature`,to ensure changes from polling are always captured
This commit is contained in:
parent
057fcf6274
commit
388dc1789b
4 changed files with 60 additions and 10 deletions
|
|
@ -286,6 +286,9 @@ function createInProgressHandler(openai, thread_id, messages) {
|
||||||
openai.seenCompletedMessages.add(message_id);
|
openai.seenCompletedMessages.add(message_id);
|
||||||
|
|
||||||
const message = await openai.beta.threads.messages.retrieve(thread_id, message_id);
|
const message = await openai.beta.threads.messages.retrieve(thread_id, message_id);
|
||||||
|
if (!message?.content?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
|
|
||||||
let messageIndex = openai.mappedOrder.get(step.id);
|
let messageIndex = openai.mappedOrder.get(step.id);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
const { ToolCallTypes } = require('librechat-data-provider');
|
||||||
const { logger } = require('~/config');
|
const { logger } = require('~/config');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,6 +19,53 @@ const { logger } = require('~/config');
|
||||||
* @property {Function} handleStep - Handles a run step based on its status.
|
* @property {Function} handleStep - Handles a run step based on its status.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a signature string for a given tool call object. This signature includes
|
||||||
|
* the tool call's id, type, and other distinguishing features based on its type.
|
||||||
|
*
|
||||||
|
* @param {ToolCall} toolCall The tool call object for which to generate a signature.
|
||||||
|
* @returns {string} The generated signature for the tool call.
|
||||||
|
*/
|
||||||
|
function getToolCallSignature(toolCall) {
|
||||||
|
if (toolCall.type === ToolCallTypes.CODE_INTERPRETER) {
|
||||||
|
const inputLength = toolCall.code_interpreter?.input?.length ?? 0;
|
||||||
|
const outputsLength = toolCall.code_interpreter?.outputs?.length ?? 0;
|
||||||
|
return `${toolCall.id}-${toolCall.type}-${inputLength}-${outputsLength}`;
|
||||||
|
}
|
||||||
|
if (toolCall.type === ToolCallTypes.RETRIEVAL) {
|
||||||
|
return `${toolCall.id}-${toolCall.type}`;
|
||||||
|
}
|
||||||
|
if (toolCall.type === ToolCallTypes.FUNCTION) {
|
||||||
|
const argsLength = toolCall.function?.arguments?.length ?? 0;
|
||||||
|
const hasOutput = toolCall.function?.output ? 1 : 0;
|
||||||
|
return `${toolCall.id}-${toolCall.type}-${argsLength}-${hasOutput}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${toolCall.id}-unknown-type`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a signature based on the specifics of the step details.
|
||||||
|
* This function supports 'message_creation' and 'tool_calls' types, and returns a default signature
|
||||||
|
* for any other type or in case the details are undefined.
|
||||||
|
*
|
||||||
|
* @param {MessageCreationStepDetails | ToolCallsStepDetails | undefined} details - The detailed content of the step, which can be undefined.
|
||||||
|
* @returns {string} A signature string derived from the content of step details.
|
||||||
|
*/
|
||||||
|
function getDetailsSignature(details) {
|
||||||
|
if (!details) {
|
||||||
|
return 'undefined-details';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.type === 'message_creation') {
|
||||||
|
return `${details.type}-${details.message_creation.message_id}`;
|
||||||
|
} else if (details.type === 'tool_calls') {
|
||||||
|
const toolCallsSignature = details.tool_calls.map(getToolCallSignature).join('|');
|
||||||
|
return `${details.type}-${toolCallsSignature}`;
|
||||||
|
}
|
||||||
|
return 'unknown-type';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the retrieval and processing of run steps based on run status.
|
* Manages the retrieval and processing of run steps based on run status.
|
||||||
*/
|
*/
|
||||||
|
|
@ -55,12 +103,14 @@ class RunManager {
|
||||||
);
|
);
|
||||||
const steps = _steps.sort((a, b) => a.created_at - b.created_at);
|
const steps = _steps.sort((a, b) => a.created_at - b.created_at);
|
||||||
for (const [i, step] of steps.entries()) {
|
for (const [i, step] of steps.entries()) {
|
||||||
if (!final && this.seenSteps.has(`${step.id}-${step.status}`)) {
|
const detailsSignature = getDetailsSignature(step.step_details);
|
||||||
|
const stepKey = `${step.id}-${step.status}-${detailsSignature}`;
|
||||||
|
if (!final && this.seenSteps.has(stepKey)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLast = i === steps.length - 1;
|
const isLast = i === steps.length - 1;
|
||||||
this.seenSteps.add(`${step.id}-${step.status}`);
|
this.seenSteps.add(stepKey);
|
||||||
this.stepsByStatus[runStatus] = this.stepsByStatus[runStatus] || [];
|
this.stepsByStatus[runStatus] = this.stepsByStatus[runStatus] || [];
|
||||||
|
|
||||||
const currentStepPromise = (async () => {
|
const currentStepPromise = (async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
// import type { ContentPart } from 'librechat-data-provider';
|
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||||
import { UnfinishedMessage } from './MessageContent';
|
import { UnfinishedMessage } from './MessageContent';
|
||||||
import { DelayedRender } from '~/components/ui';
|
import { DelayedRender } from '~/components/ui';
|
||||||
import Part from './Part';
|
import Part from './Part';
|
||||||
|
|
||||||
// Content Component
|
|
||||||
const ContentParts = ({
|
const ContentParts = ({
|
||||||
edit,
|
|
||||||
error,
|
error,
|
||||||
unfinished,
|
unfinished,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
|
@ -17,15 +15,16 @@ const ContentParts = ({
|
||||||
any) => {
|
any) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
// return <ErrorMessage text={text} />;
|
// return <ErrorMessage text={text} />;
|
||||||
} else if (edit) {
|
|
||||||
// return <EditMessage text={text} isSubmitting={isSubmitting} {...props} />;
|
|
||||||
} else {
|
} else {
|
||||||
const { message } = props;
|
const { message } = props;
|
||||||
const { messageId } = message;
|
const { messageId } = message;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{content.map((part, idx) => {
|
{content.map((part: TMessageContentParts | undefined, idx: number) => {
|
||||||
|
if (!part) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Part
|
<Part
|
||||||
key={`display-${messageId}-${idx}`}
|
key={`display-${messageId}-${idx}`}
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,6 @@ export default function useContentHandler({ setMessages, getMessages }: TUseCont
|
||||||
response.content.push(initialResponse.content[0]);
|
response.content.push(initialResponse.content[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.content = response.content.filter((p) => p !== undefined);
|
|
||||||
|
|
||||||
setMessages([...messages, response]);
|
setMessages([...messages, response]);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue