mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 11:50:14 +01:00
🧠 feat: Enforce Token Limit for Memory Usage (#8401)
This commit is contained in:
parent
2e1874e596
commit
8e869f2274
10 changed files with 765 additions and 31 deletions
|
|
@ -13,14 +13,25 @@ export default function MemoryArtifacts({ attachments }: { attachments?: TAttach
|
|||
const [isAnimating, setIsAnimating] = useState(false);
|
||||
const prevShowInfoRef = useRef<boolean>(showInfo);
|
||||
|
||||
const memoryArtifacts = useMemo(() => {
|
||||
const { hasErrors, memoryArtifacts } = useMemo(() => {
|
||||
let hasErrors = false;
|
||||
const result: MemoryArtifact[] = [];
|
||||
for (const attachment of attachments ?? []) {
|
||||
|
||||
if (!attachments || attachments.length === 0) {
|
||||
return { hasErrors, memoryArtifacts: result };
|
||||
}
|
||||
|
||||
for (const attachment of attachments) {
|
||||
if (attachment?.[Tools.memory] != null) {
|
||||
result.push(attachment[Tools.memory]);
|
||||
|
||||
if (!hasErrors && attachment[Tools.memory].type === 'error') {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
return { hasErrors, memoryArtifacts: result };
|
||||
}, [attachments]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
|
@ -75,7 +86,12 @@ export default function MemoryArtifacts({ attachments }: { attachments?: TAttach
|
|||
<div className="flex items-center">
|
||||
<div className="inline-block">
|
||||
<button
|
||||
className="outline-hidden my-1 flex items-center gap-1 text-sm font-semibold text-text-secondary-alt transition-colors hover:text-text-primary"
|
||||
className={cn(
|
||||
'outline-hidden my-1 flex items-center gap-1 text-sm font-semibold transition-colors',
|
||||
hasErrors
|
||||
? 'text-red-500 hover:text-red-600 dark:text-red-400 dark:hover:text-red-500'
|
||||
: 'text-text-secondary-alt hover:text-text-primary',
|
||||
)}
|
||||
type="button"
|
||||
onClick={() => setShowInfo((prev) => !prev)}
|
||||
aria-expanded={showInfo}
|
||||
|
|
@ -102,7 +118,7 @@ export default function MemoryArtifacts({ attachments }: { attachments?: TAttach
|
|||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
{localize('com_ui_memory_updated')}
|
||||
{hasErrors ? localize('com_ui_memory_error') : localize('com_ui_memory_updated')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,47 @@
|
|||
import type { MemoryArtifact } from 'librechat-data-provider';
|
||||
import { useMemo } from 'react';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export default function MemoryInfo({ memoryArtifacts }: { memoryArtifacts: MemoryArtifact[] }) {
|
||||
const localize = useLocalize();
|
||||
|
||||
const { updatedMemories, deletedMemories, errorMessages } = useMemo(() => {
|
||||
const updated = memoryArtifacts.filter((art) => art.type === 'update');
|
||||
const deleted = memoryArtifacts.filter((art) => art.type === 'delete');
|
||||
const errors = memoryArtifacts.filter((art) => art.type === 'error');
|
||||
|
||||
const messages = errors.map((artifact) => {
|
||||
try {
|
||||
const errorData = JSON.parse(artifact.value as string);
|
||||
|
||||
if (errorData.errorType === 'already_exceeded') {
|
||||
return localize('com_ui_memory_already_exceeded', {
|
||||
tokens: errorData.tokenCount,
|
||||
});
|
||||
} else if (errorData.errorType === 'would_exceed') {
|
||||
return localize('com_ui_memory_would_exceed', {
|
||||
tokens: errorData.tokenCount,
|
||||
});
|
||||
} else {
|
||||
return localize('com_ui_memory_error');
|
||||
}
|
||||
} catch {
|
||||
return localize('com_ui_memory_error');
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
updatedMemories: updated,
|
||||
deletedMemories: deleted,
|
||||
errorMessages: messages,
|
||||
};
|
||||
}, [memoryArtifacts, localize]);
|
||||
|
||||
if (memoryArtifacts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Group artifacts by type
|
||||
const updatedMemories = memoryArtifacts.filter((artifact) => artifact.type === 'update');
|
||||
const deletedMemories = memoryArtifacts.filter((artifact) => artifact.type === 'delete');
|
||||
|
||||
if (updatedMemories.length === 0 && deletedMemories.length === 0) {
|
||||
if (updatedMemories.length === 0 && deletedMemories.length === 0 && errorMessages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -23,8 +53,8 @@ export default function MemoryInfo({ memoryArtifacts }: { memoryArtifacts: Memor
|
|||
{localize('com_ui_memory_updated_items')}
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
{updatedMemories.map((artifact, index) => (
|
||||
<div key={`update-${index}`} className="rounded-lg p-3">
|
||||
{updatedMemories.map((artifact) => (
|
||||
<div key={`update-${artifact.key}`} className="rounded-lg p-3">
|
||||
<div className="mb-1 text-xs font-medium uppercase tracking-wide text-text-secondary">
|
||||
{artifact.key}
|
||||
</div>
|
||||
|
|
@ -43,8 +73,8 @@ export default function MemoryInfo({ memoryArtifacts }: { memoryArtifacts: Memor
|
|||
{localize('com_ui_memory_deleted_items')}
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
{deletedMemories.map((artifact, index) => (
|
||||
<div key={`delete-${index}`} className="rounded-lg p-3 opacity-60">
|
||||
{deletedMemories.map((artifact) => (
|
||||
<div key={`delete-${artifact.key}`} className="rounded-lg p-3 opacity-60">
|
||||
<div className="mb-1 text-xs font-medium uppercase tracking-wide text-text-secondary">
|
||||
{artifact.key}
|
||||
</div>
|
||||
|
|
@ -56,6 +86,24 @@ export default function MemoryInfo({ memoryArtifacts }: { memoryArtifacts: Memor
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{errorMessages.length > 0 && (
|
||||
<div>
|
||||
<h4 className="mb-2 text-sm font-semibold text-red-500">
|
||||
{localize('com_ui_memory_storage_full')}
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
{errorMessages.map((errorMessage) => (
|
||||
<div
|
||||
key={errorMessage}
|
||||
className="rounded-md bg-red-50 p-3 text-sm text-red-800 dark:bg-red-900/20 dark:text-red-400"
|
||||
>
|
||||
{errorMessage}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,197 @@
|
|||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import MemoryArtifacts from '../MemoryArtifacts';
|
||||
import type { TAttachment, MemoryArtifact } from 'librechat-data-provider';
|
||||
import { Tools } from 'librechat-data-provider';
|
||||
|
||||
// Mock the localize hook
|
||||
jest.mock('~/hooks', () => ({
|
||||
useLocalize: () => (key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
com_ui_memory_updated: 'Updated saved memory',
|
||||
com_ui_memory_error: 'Memory Error',
|
||||
};
|
||||
return translations[key] || key;
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock the MemoryInfo component
|
||||
jest.mock('../MemoryInfo', () => ({
|
||||
__esModule: true,
|
||||
default: ({ memoryArtifacts }: { memoryArtifacts: MemoryArtifact[] }) => (
|
||||
<div data-testid="memory-info">
|
||||
{memoryArtifacts.map((artifact, index) => (
|
||||
<div key={index} data-testid={`memory-artifact-${artifact.type}`}>
|
||||
{artifact.type}: {artifact.key}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
describe('MemoryArtifacts', () => {
|
||||
const createMemoryAttachment = (type: 'update' | 'delete' | 'error', key: string): TAttachment =>
|
||||
({
|
||||
type: Tools.memory,
|
||||
[Tools.memory]: {
|
||||
type,
|
||||
key,
|
||||
value:
|
||||
type === 'error'
|
||||
? JSON.stringify({ errorType: 'exceeded', tokenCount: 100 })
|
||||
: 'test value',
|
||||
} as MemoryArtifact,
|
||||
}) as TAttachment;
|
||||
|
||||
describe('Error State Handling', () => {
|
||||
test('displays error styling when memory artifacts contain errors', () => {
|
||||
const attachments = [
|
||||
createMemoryAttachment('error', 'system'),
|
||||
createMemoryAttachment('update', 'memory1'),
|
||||
];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toHaveClass('text-red-500');
|
||||
expect(button).toHaveClass('hover:text-red-600');
|
||||
expect(button).toHaveClass('dark:text-red-400');
|
||||
expect(button).toHaveClass('dark:hover:text-red-500');
|
||||
});
|
||||
|
||||
test('displays normal styling when no errors present', () => {
|
||||
const attachments = [
|
||||
createMemoryAttachment('update', 'memory1'),
|
||||
createMemoryAttachment('delete', 'memory2'),
|
||||
];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toHaveClass('text-text-secondary-alt');
|
||||
expect(button).toHaveClass('hover:text-text-primary');
|
||||
expect(button).not.toHaveClass('text-red-500');
|
||||
});
|
||||
|
||||
test('displays error message when errors are present', () => {
|
||||
const attachments = [createMemoryAttachment('error', 'system')];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
expect(screen.getByText('Memory Error')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Updated saved memory')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('displays normal message when no errors are present', () => {
|
||||
const attachments = [createMemoryAttachment('update', 'memory1')];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
expect(screen.getByText('Updated saved memory')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Memory Error')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Memory Artifacts Filtering', () => {
|
||||
test('filters and passes only memory-type attachments to MemoryInfo', () => {
|
||||
const attachments = [
|
||||
createMemoryAttachment('update', 'memory1'),
|
||||
{ type: 'file' } as TAttachment, // Non-memory attachment
|
||||
createMemoryAttachment('error', 'system'),
|
||||
];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
// Click to expand
|
||||
fireEvent.click(screen.getByRole('button'));
|
||||
|
||||
// Check that only memory artifacts are passed to MemoryInfo
|
||||
expect(screen.getByTestId('memory-artifact-update')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('memory-artifact-error')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('correctly identifies multiple error artifacts', () => {
|
||||
const attachments = [
|
||||
createMemoryAttachment('error', 'system1'),
|
||||
createMemoryAttachment('error', 'system2'),
|
||||
createMemoryAttachment('update', 'memory1'),
|
||||
];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toHaveClass('text-red-500');
|
||||
expect(screen.getByText('Memory Error')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Collapse/Expand Functionality', () => {
|
||||
test('toggles memory info visibility on button click', () => {
|
||||
const attachments = [createMemoryAttachment('update', 'memory1')];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
// Initially collapsed
|
||||
expect(screen.queryByTestId('memory-info')).not.toBeInTheDocument();
|
||||
|
||||
// Click to expand
|
||||
fireEvent.click(screen.getByRole('button'));
|
||||
expect(screen.getByTestId('memory-info')).toBeInTheDocument();
|
||||
|
||||
// Click to collapse
|
||||
fireEvent.click(screen.getByRole('button'));
|
||||
expect(screen.queryByTestId('memory-info')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('updates aria-expanded attribute correctly', () => {
|
||||
const attachments = [createMemoryAttachment('update', 'memory1')];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(button).toHaveAttribute('aria-expanded', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
test('handles empty attachments array', () => {
|
||||
render(<MemoryArtifacts attachments={[]} />);
|
||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('handles undefined attachments', () => {
|
||||
render(<MemoryArtifacts />);
|
||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('handles attachments with no memory artifacts', () => {
|
||||
const attachments = [{ type: 'file' } as TAttachment, { type: 'image' } as TAttachment];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('handles malformed memory artifacts gracefully', () => {
|
||||
const attachments = [
|
||||
{
|
||||
type: Tools.memory,
|
||||
[Tools.memory]: {
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
// Missing value
|
||||
},
|
||||
} as TAttachment,
|
||||
];
|
||||
|
||||
render(<MemoryArtifacts attachments={attachments} />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toHaveClass('text-red-500');
|
||||
expect(screen.getByText('Memory Error')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import MemoryInfo from '../MemoryInfo';
|
||||
import type { MemoryArtifact } from 'librechat-data-provider';
|
||||
|
||||
// Mock the localize hook
|
||||
jest.mock('~/hooks', () => ({
|
||||
useLocalize: () => (key: string, params?: Record<string, any>) => {
|
||||
const translations: Record<string, string> = {
|
||||
com_ui_memory_updated_items: 'Updated Memories',
|
||||
com_ui_memory_deleted_items: 'Deleted Memories',
|
||||
com_ui_memory_already_exceeded: `Memory storage already full - exceeded by ${params?.tokens || 0} tokens. Delete existing memories before adding new ones.`,
|
||||
com_ui_memory_would_exceed: `Cannot save - would exceed limit by ${params?.tokens || 0} tokens. Delete existing memories to make space.`,
|
||||
com_ui_memory_deleted: 'This memory has been deleted',
|
||||
com_ui_memory_storage_full: 'Memory Storage Full',
|
||||
com_ui_memory_error: 'Memory Error',
|
||||
com_ui_updated_successfully: 'Updated successfully',
|
||||
com_ui_none_selected: 'None selected',
|
||||
};
|
||||
return translations[key] || key;
|
||||
},
|
||||
}));
|
||||
|
||||
describe('MemoryInfo', () => {
|
||||
const createMemoryArtifact = (
|
||||
type: 'update' | 'delete' | 'error',
|
||||
key: string,
|
||||
value?: string,
|
||||
): MemoryArtifact => ({
|
||||
type,
|
||||
key,
|
||||
value: value || `test value for ${key}`,
|
||||
});
|
||||
|
||||
describe('Error Memory Display', () => {
|
||||
test('displays error section when memory is already exceeded', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
value: JSON.stringify({ errorType: 'already_exceeded', tokenCount: 150 }),
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
expect(screen.getByText('Memory Storage Full')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Memory storage already full - exceeded by 150 tokens. Delete existing memories before adding new ones.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('displays error when memory would exceed limit', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
value: JSON.stringify({ errorType: 'would_exceed', tokenCount: 50 }),
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
expect(screen.getByText('Memory Storage Full')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Cannot save - would exceed limit by 50 tokens. Delete existing memories to make space.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('displays multiple error messages', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system1',
|
||||
value: JSON.stringify({ errorType: 'already_exceeded', tokenCount: 100 }),
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system2',
|
||||
value: JSON.stringify({ errorType: 'would_exceed', tokenCount: 25 }),
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Memory storage already full - exceeded by 100 tokens. Delete existing memories before adding new ones.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Cannot save - would exceed limit by 25 tokens. Delete existing memories to make space.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('applies correct styling to error messages', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
value: JSON.stringify({ errorType: 'would_exceed', tokenCount: 50 }),
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
const errorMessage = screen.getByText(
|
||||
'Cannot save - would exceed limit by 50 tokens. Delete existing memories to make space.',
|
||||
);
|
||||
const errorContainer = errorMessage.closest('div');
|
||||
|
||||
expect(errorContainer).toHaveClass('rounded-md');
|
||||
expect(errorContainer).toHaveClass('bg-red-50');
|
||||
expect(errorContainer).toHaveClass('p-3');
|
||||
expect(errorContainer).toHaveClass('text-sm');
|
||||
expect(errorContainer).toHaveClass('text-red-800');
|
||||
expect(errorContainer).toHaveClass('dark:bg-red-900/20');
|
||||
expect(errorContainer).toHaveClass('dark:text-red-400');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mixed Memory Types', () => {
|
||||
test('displays all sections when different memory types are present', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
createMemoryArtifact('update', 'memory1', 'Updated content'),
|
||||
createMemoryArtifact('delete', 'memory2'),
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
value: JSON.stringify({ errorType: 'would_exceed', tokenCount: 200 }),
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
// Check all sections are present
|
||||
expect(screen.getByText('Updated Memories')).toBeInTheDocument();
|
||||
expect(screen.getByText('Deleted Memories')).toBeInTheDocument();
|
||||
expect(screen.getByText('Memory Storage Full')).toBeInTheDocument();
|
||||
|
||||
// Check content
|
||||
expect(screen.getByText('memory1')).toBeInTheDocument();
|
||||
expect(screen.getByText('Updated content')).toBeInTheDocument();
|
||||
expect(screen.getByText('memory2')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Cannot save - would exceed limit by 200 tokens. Delete existing memories to make space.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('only displays sections with content', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
value: JSON.stringify({ errorType: 'already_exceeded', tokenCount: 10 }),
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
// Only error section should be present
|
||||
expect(screen.getByText('Memory Storage Full')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Updated Memories')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Deleted Memories')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
test('handles empty memory artifacts array', () => {
|
||||
const { container } = render(<MemoryInfo memoryArtifacts={[]} />);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
test('handles malformed error data gracefully', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
value: 'invalid json',
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
// Should render generic error message
|
||||
expect(screen.getByText('Memory Storage Full')).toBeInTheDocument();
|
||||
expect(screen.getByText('Memory Error')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('handles missing value in error artifact', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
// value is undefined
|
||||
} as MemoryArtifact,
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
expect(screen.getByText('Memory Storage Full')).toBeInTheDocument();
|
||||
expect(screen.getByText('Memory Error')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('handles unknown errorType gracefully', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
{
|
||||
type: 'error',
|
||||
key: 'system',
|
||||
value: JSON.stringify({ errorType: 'unknown_type', tokenCount: 30 }),
|
||||
},
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
// Should show generic error message for unknown types
|
||||
expect(screen.getByText('Memory Storage Full')).toBeInTheDocument();
|
||||
expect(screen.getByText('Memory Error')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('returns null when no memories of any type exist', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [{ type: 'unknown' as any, key: 'test' }];
|
||||
|
||||
const { container } = render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update and Delete Memory Display', () => {
|
||||
test('displays updated memories correctly', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
createMemoryArtifact('update', 'preferences', 'User prefers dark mode'),
|
||||
createMemoryArtifact('update', 'location', 'Lives in San Francisco'),
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
expect(screen.getByText('Updated Memories')).toBeInTheDocument();
|
||||
expect(screen.getByText('preferences')).toBeInTheDocument();
|
||||
expect(screen.getByText('User prefers dark mode')).toBeInTheDocument();
|
||||
expect(screen.getByText('location')).toBeInTheDocument();
|
||||
expect(screen.getByText('Lives in San Francisco')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('displays deleted memories correctly', () => {
|
||||
const memoryArtifacts: MemoryArtifact[] = [
|
||||
createMemoryArtifact('delete', 'old_preference'),
|
||||
createMemoryArtifact('delete', 'outdated_info'),
|
||||
];
|
||||
|
||||
render(<MemoryInfo memoryArtifacts={memoryArtifacts} />);
|
||||
|
||||
expect(screen.getByText('Deleted Memories')).toBeInTheDocument();
|
||||
expect(screen.getByText('old_preference')).toBeInTheDocument();
|
||||
expect(screen.getByText('outdated_info')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue