fix(userMemories,database): should not fail if extra structure mismatch (#12686)

This commit is contained in:
Neko
2026-03-04 19:09:13 +08:00
committed by GitHub
parent bf5d6ce2f8
commit 3f1473d65f
2 changed files with 56 additions and 4 deletions

View File

@@ -1463,6 +1463,23 @@ describe('UserMemoryModel', () => {
type: UserMemoryContextObjectType.Person,
});
});
it('should not throw on non-JSON extra and preserve raw text', () => {
const result = UserMemoryModel.parseAssociatedObjects([
{
name: 'Policy Doc',
type: UserMemoryContextObjectType.Other,
extra: 'plain text metadata note',
},
]);
expect(result).toHaveLength(1);
expect(result[0]).toMatchObject({
extra: { raw: 'plain text metadata note' },
name: 'Policy Doc',
type: UserMemoryContextObjectType.Other,
});
});
});
describe('parseAssociatedSubjects - valid schema', () => {
@@ -1482,6 +1499,22 @@ describe('UserMemoryModel', () => {
extra: { role: 'admin' },
});
});
it('should not throw on plain text subject extra', () => {
const result = UserMemoryModel.parseAssociatedSubjects([
{
name: 'Runtime Agent',
type: 'person',
extra: 'subject plain text metadata',
},
]);
expect(result).toHaveLength(1);
expect(result[0]).toMatchObject({
extra: { raw: 'subject plain text metadata' },
name: 'Runtime Agent',
});
});
});
// ========== getMemoryDetail edge cases ==========

View File

@@ -89,6 +89,23 @@ const coerceDate = (input: unknown): Date | null => {
return null;
};
const parseAssociationExtra = (
value: string | null | undefined,
): Record<string, unknown> | null => {
if (!value) return null;
try {
const parsed = JSON.parse(value);
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
return parsed as Record<string, unknown>;
}
return { value: parsed };
} catch {
return { raw: value };
}
};
export interface BaseCreateUserMemoryParams {
capturedAt?: Date;
details: string;
@@ -372,8 +389,9 @@ export class UserMemoryModel {
value.forEach((item) => {
const parsed = AssociatedObjectSchema.safeParse(item);
if (parsed.success) {
const extra = JSON.parse(parsed.data.extra || '{}');
parsed.data.extra = extra;
const extra = parseAssociationExtra(parsed.data.extra);
if (extra) parsed.data.extra = extra as any;
else delete (parsed.data as any).extra;
associations.push(parsed.data);
return;
}
@@ -399,8 +417,9 @@ export class UserMemoryModel {
value.forEach((item) => {
const parsed = AssociatedObjectSchema.safeParse(item);
if (parsed.success) {
const extra = JSON.parse(parsed.data.extra || '{}');
parsed.data.extra = extra;
const extra = parseAssociationExtra(parsed.data.extra);
if (extra) parsed.data.extra = extra as any;
else delete (parsed.data as any).extra;
associations.push(parsed.data);
return;
}