mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-08 18:44:22 +01:00
🎬 fix: Code Session Context In Event Driven Mode (#11673)
* fix: Update parseTextParts to handle undefined content parts - Modified the parseTextParts function to accept an array of content parts that may include undefined values. - Implemented optional chaining to safely check for the type of each part, preventing potential runtime errors when accessing properties of undefined elements. * refactor: Tool Call Configuration with Session Context - Added support for including session ID and injected files in the tool call configuration when a code session context is present. - Improved handling of tool call configurations to accommodate additional context data, enhancing the functionality of the tool execution handler. * chore: Update @librechat/agents to version 3.1.37 in package.json and package-lock.json * test: Add unit tests for createToolExecuteHandler - Introduced a new test suite for the createToolExecuteHandler function, validating the handling of session context in tool calls. - Added tests to ensure correct passing of session IDs and injected files based on the presence of codeSessionContext. - Included scenarios for handling multiple tool calls and ensuring non-code execution tools are unaffected by session context. * test: Update createToolExecuteHandler tests for session context handling - Renamed test to clarify that it checks for the absence of session context in non-code-execution tools. - Updated assertions to ensure that session_id and _injected_files are undefined when non-code-execution tools are invoked, enhancing test accuracy.
This commit is contained in:
parent
968e97b4d2
commit
a771d70b10
7 changed files with 294 additions and 17 deletions
|
|
@ -1,6 +1,8 @@
|
|||
import { replaceSpecialVars, parseCompactConvo } from '../src/parsers';
|
||||
import { replaceSpecialVars, parseCompactConvo, parseTextParts } from '../src/parsers';
|
||||
import { specialVariables } from '../src/config';
|
||||
import { EModelEndpoint } from '../src/schemas';
|
||||
import { ContentTypes } from '../src/types/runs';
|
||||
import type { TMessageContentParts } from '../src/types/assistants';
|
||||
import type { TUser, TConversation } from '../src/types';
|
||||
|
||||
// Mock dayjs module with consistent date/time values regardless of environment
|
||||
|
|
@ -141,7 +143,7 @@ describe('parseCompactConvo', () => {
|
|||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.iconURL).toBeUndefined();
|
||||
expect(result?.['iconURL']).toBeUndefined();
|
||||
expect(result?.model).toBe('gpt-4');
|
||||
});
|
||||
|
||||
|
|
@ -159,7 +161,7 @@ describe('parseCompactConvo', () => {
|
|||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.iconURL).toBeUndefined();
|
||||
expect(result?.['iconURL']).toBeUndefined();
|
||||
expect(result?.agent_id).toBe('agent_123');
|
||||
});
|
||||
|
||||
|
|
@ -177,7 +179,7 @@ describe('parseCompactConvo', () => {
|
|||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.iconURL).toBeUndefined();
|
||||
expect(result?.['iconURL']).toBeUndefined();
|
||||
expect(result?.model).toBe('claude-3-opus');
|
||||
});
|
||||
|
||||
|
|
@ -195,7 +197,7 @@ describe('parseCompactConvo', () => {
|
|||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.iconURL).toBeUndefined();
|
||||
expect(result?.['iconURL']).toBeUndefined();
|
||||
expect(result?.model).toBe('gemini-pro');
|
||||
});
|
||||
|
||||
|
|
@ -213,7 +215,7 @@ describe('parseCompactConvo', () => {
|
|||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.iconURL).toBeUndefined();
|
||||
expect(result?.['iconURL']).toBeUndefined();
|
||||
expect(result?.assistant_id).toBe('asst_123');
|
||||
});
|
||||
|
||||
|
|
@ -234,7 +236,7 @@ describe('parseCompactConvo', () => {
|
|||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.iconURL).toBeUndefined();
|
||||
expect(result?.['iconURL']).toBeUndefined();
|
||||
expect(result?.model).toBe('gpt-4');
|
||||
expect(result?.temperature).toBe(0.7);
|
||||
expect(result?.top_p).toBe(0.9);
|
||||
|
|
@ -254,8 +256,94 @@ describe('parseCompactConvo', () => {
|
|||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.iconURL).toBeUndefined();
|
||||
expect(result?.['iconURL']).toBeUndefined();
|
||||
expect(result?.model).toBe('gpt-4');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseTextParts', () => {
|
||||
test('should concatenate text parts', () => {
|
||||
const parts: TMessageContentParts[] = [
|
||||
{ type: ContentTypes.TEXT, text: 'Hello' },
|
||||
{ type: ContentTypes.TEXT, text: 'World' },
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('Hello World');
|
||||
});
|
||||
|
||||
test('should handle text parts with object-style text values', () => {
|
||||
const parts: TMessageContentParts[] = [
|
||||
{ type: ContentTypes.TEXT, text: { value: 'structured text' } },
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('structured text');
|
||||
});
|
||||
|
||||
test('should include think parts by default', () => {
|
||||
const parts: TMessageContentParts[] = [
|
||||
{ type: ContentTypes.TEXT, text: 'Answer:' },
|
||||
{ type: ContentTypes.THINK, think: 'reasoning step' },
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('Answer: reasoning step');
|
||||
});
|
||||
|
||||
test('should skip think parts when skipReasoning is true', () => {
|
||||
const parts: TMessageContentParts[] = [
|
||||
{ type: ContentTypes.THINK, think: 'internal reasoning' },
|
||||
{ type: ContentTypes.TEXT, text: 'visible answer' },
|
||||
];
|
||||
expect(parseTextParts(parts, true)).toBe('visible answer');
|
||||
});
|
||||
|
||||
test('should skip non-text/think part types', () => {
|
||||
const parts: TMessageContentParts[] = [
|
||||
{ type: ContentTypes.TEXT, text: 'before' },
|
||||
{ type: ContentTypes.IMAGE_FILE } as TMessageContentParts,
|
||||
{ type: ContentTypes.TEXT, text: 'after' },
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('before after');
|
||||
});
|
||||
|
||||
test('should handle undefined elements in the content parts array', () => {
|
||||
const parts: Array<TMessageContentParts | undefined> = [
|
||||
{ type: ContentTypes.TEXT, text: 'first' },
|
||||
undefined,
|
||||
{ type: ContentTypes.TEXT, text: 'third' },
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('first third');
|
||||
});
|
||||
|
||||
test('should handle multiple consecutive undefined elements', () => {
|
||||
const parts: Array<TMessageContentParts | undefined> = [
|
||||
undefined,
|
||||
undefined,
|
||||
{ type: ContentTypes.TEXT, text: 'only text' },
|
||||
undefined,
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('only text');
|
||||
});
|
||||
|
||||
test('should handle an array of all undefined elements', () => {
|
||||
const parts: Array<TMessageContentParts | undefined> = [undefined, undefined, undefined];
|
||||
expect(parseTextParts(parts)).toBe('');
|
||||
});
|
||||
|
||||
test('should handle parts with missing type property', () => {
|
||||
const parts: Array<TMessageContentParts | undefined> = [
|
||||
{ text: 'no type field' } as unknown as TMessageContentParts,
|
||||
{ type: ContentTypes.TEXT, text: 'valid' },
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('valid');
|
||||
});
|
||||
|
||||
test('should return empty string for empty array', () => {
|
||||
expect(parseTextParts([])).toBe('');
|
||||
});
|
||||
|
||||
test('should not add extra spaces when parts already have spacing', () => {
|
||||
const parts: TMessageContentParts[] = [
|
||||
{ type: ContentTypes.TEXT, text: 'Hello ' },
|
||||
{ type: ContentTypes.TEXT, text: 'World' },
|
||||
];
|
||||
expect(parseTextParts(parts)).toBe('Hello World');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -349,13 +349,13 @@ export const parseCompactConvo = ({
|
|||
};
|
||||
|
||||
export function parseTextParts(
|
||||
contentParts: a.TMessageContentParts[],
|
||||
contentParts: Array<a.TMessageContentParts | undefined>,
|
||||
skipReasoning: boolean = false,
|
||||
): string {
|
||||
let result = '';
|
||||
|
||||
for (const part of contentParts) {
|
||||
if (!part.type) {
|
||||
if (!part?.type) {
|
||||
continue;
|
||||
}
|
||||
if (part.type === ContentTypes.TEXT) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue