Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/services/__tests__/current-state-ranking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ describe('isCurrentStateQuery', () => {
expect(isCurrentStateQuery('How much weight have I lost so far?')).toBe(true);
});

it('rejects temporal comparison quantity queries', () => {
expect(isCurrentStateQuery(
"How many months lapsed between Sam's first and second doctor's appointment?",
)).toBe(false);
expect(isCurrentStateQuery(
'How long did James and Samantha date before deciding to move in together?',
)).toBe(false);
});

it('blocks quantity starters that contain historical markers', () => {
expect(isCurrentStateQuery('How many things did I previously own?')).toBe(false);
expect(isCurrentStateQuery('How often did I used to run?')).toBe(false);
Expand Down
2 changes: 1 addition & 1 deletion src/services/__tests__/iterative-retrieval.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ describe('applyIterativeRetrieval', () => {

expect(result.triggered).toBe(true);
expect(result.memories.some((memory) => memory.id === 'neighbor')).toBe(true);
expect(result.seedIds).toEqual(['seed-1', 'seed-2']);
expect(result.seedIds).toEqual(['seed-2', 'seed-1']);
});
});
11 changes: 11 additions & 0 deletions src/services/__tests__/quick-extraction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,15 @@ describe('quickExtractFacts', () => {
expect(facts.some((fact) => fact.fact.includes('Nate has had the turtles for 3 years now'))).toBe(true);
expect(facts.some((fact) => fact.fact.includes('Sam had a check-up with Sam\'s doctor a few days ago'))).toBe(true);
});

it('captures tournament wins from conversational first-person event sentences', () => {
const facts = quickExtractFacts(
[
'[Session date: 2022-08-22]',
'Nate: Woah Joanna, I won an international tournament yesterday! It was wild.',
].join('\n'),
);

expect(facts.some((fact) => fact.fact.includes('won an international tournament yesterday (on August 21, 2022)'))).toBe(true);
});
});
27 changes: 25 additions & 2 deletions src/services/__tests__/retrieval-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,29 @@ describe('formatTieredInjection', () => {
expect(result).toContain('Repeated event endpoints:');
expect(result).toContain('elapsed between endpoints: ~3 months (83 days)');
});

it('suppresses the generic timeline summary when query-aware temporal evidence is present', () => {
const memories = [
makeResult({ id: 'first', content: "Sam had a doctor's appointment as a wake-up call.", created_at: new Date('2023-05-24T00:00:00Z') }),
makeResult({ id: 'second', content: 'Sam had another doctor appointment after changing diet.', created_at: new Date('2023-08-15T00:00:00Z') }),
makeResult({ id: 'plan', content: 'Sam decided to make a new appointment in January.', created_at: new Date('2024-01-10T00:00:00Z') }),
];
const assignments = [
{ memoryId: 'first', tier: 'L2' as const, estimatedTokens: 5 },
{ memoryId: 'second', tier: 'L2' as const, estimatedTokens: 5 },
{ memoryId: 'plan', tier: 'L2' as const, estimatedTokens: 5 },
];
const result = formatTieredInjection(
memories,
assignments,
"How many months lapsed between Sam's first and second doctor's appointment?",
);

expect(result).toContain('Repeated event endpoints:');
expect(result).not.toContain('Timeline:');
expect(result).not.toContain('Key temporal evidence:');
expect(result).not.toContain('2024-01-10 →');
});
});

describe('formatSimpleInjection', () => {
Expand Down Expand Up @@ -301,7 +324,7 @@ describe('formatSimpleInjection', () => {
});

describe('buildInjection query-term visibility', () => {
it('promotes a compressed memory when L0 hides an exact query term', () => {
it('keeps the exact query term visible in the final temporal injection', () => {
const result = buildInjection([
makeResult({
id: 'workshop',
Expand All @@ -312,8 +335,8 @@ describe('buildInjection query-term visibility', () => {
}),
], 'What workshop did Caroline attend recently?', 'tiered', 35);

expect(result.injectionText).toContain('[L1]');
expect(result.injectionText).toContain('workshop');
expect(result.injectionText).toContain('Temporal evidence candidates:');
});
});

Expand Down
55 changes: 53 additions & 2 deletions src/services/__tests__/subject-aware-ranking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ describe('applySubjectAwareRanking', () => {
]);

expect(ranked.subjects).toEqual(['Gina']);
expect(ranked.keywords).toEqual(['lost', 'job', 'door', 'dash']);
expect(ranked.keywords).toEqual(expect.arrayContaining([
'lost', 'job', 'door', 'dash', 'door dash',
]));
expect(ranked.protectedFingerprints).toHaveLength(1);
expect(ranked.results[0].id).toBe('gina');
});
Expand All @@ -49,6 +51,55 @@ describe('applySubjectAwareRanking', () => {

it('extracts subject and event anchors for exact keyword expansion', () => {
expect(extractSubjectQueryAnchors('When Gina lost her job at Door Dash?'))
.toEqual(['Gina', 'lost', 'job', 'door', 'dash']);
.toEqual(['Gina', 'lost', 'job', 'door', 'dash', 'lost job', 'job door', 'door dash']);
});

it('drops temporal filler anchors but keeps high-signal bigrams', () => {
const anchors = extractSubjectQueryAnchors(
"How many months lapsed between Sam's first and second doctor's appointment?",
);

expect(anchors).toContain('Sams');
expect(anchors).toContain('appointment');
expect(anchors).toContain('doctor appointment');
expect(anchors).not.toContain('many');
expect(anchors).not.toContain('months');
expect(anchors).not.toContain('between');
expect(anchors).not.toContain('first');
expect(anchors).not.toContain('second');
});

it('adds normalized event variants for temporal subject anchors', () => {
const anchors = extractSubjectQueryAnchors(
'How many weeks passed between Maria adopting Coco and Shadow?',
);

expect(anchors).toContain('adopting');
expect(anchors).toContain('adopt');
});

it('penalizes planning-like later memories for temporal event queries', () => {
const ranked = applySubjectAwareRanking(
"How many months lapsed between Sam's first and second doctor's appointment?",
[
buildResult('plan', 'Sam decided to make a new appointment in January.', 1.1),
buildResult('done', 'Sam had a second doctor appointment after changing diet.', 0.6),
],
);

expect(ranked.results[0].id).toBe('done');
expect(ranked.results[1].id).toBe('plan');
});

it('prefers memories that mention more of the requested endpoint anchors', () => {
const ranked = applySubjectAwareRanking(
'How many weeks passed between Maria adopting Coco and Shadow?',
[
buildResult('generic', 'Maria adopted a dog earlier this year.', 0.9),
buildResult('specific', 'Maria adopted Coco and felt instantly attached.', 0.4),
],
);

expect(ranked.results[0].id).toBe('specific');
});
});
155 changes: 155 additions & 0 deletions src/services/__tests__/supplemental-extraction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,159 @@ describe('mergeSupplementalFacts', () => {
expect(merged.some((fact) => fact.fact.includes('Nate has had the turtles for 3 years now'))).toBe(true);
expect(merged.some((fact) => fact.fact.includes('Sam had a check-up with Sam\'s doctor a few days ago'))).toBe(true);
});

it('keeps affect inventory facts even when other no-entity literal facts exist', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2022-05-04]',
'James: By the way, today I decided to spend time with my beloved pets again.',
'John: What else brings you happiness?',
'James: My pets, computer games, travel and pizza are all that bring me happiness in life.',
].join('\n'),
);

expect(merged.some((fact) => fact.fact.includes('computer games, travel and pizza are all that bring me happiness'))).toBe(true);
});

it('resolves pronoun-based pet joy evidence for affect questions', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2022-05-04]',
'James: One of them, Daisy, is a Labrador. She loves to play with her toys.',
'John: Cool, what about the other two? Judging by the photo, shepherds?',
'James: Exactly! You would know how much joy they bring me. They are so loyal.',
].join('\n'),
);

expect(merged.some((fact) => fact.fact === 'James\'s dogs bring James joy.')).toBe(true);
});

it('resolves pronoun-based animal motivation evidence for shared-like questions', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2022-11-04]',
'Joanna: It was about a brave little turtle who was scared but explored the world anyway.',
'Nate: Their resilience is so inspiring!',
'Joanna: They make me think of strength and perseverance. They help motivate me in tough times.',
].join('\n'),
);

expect(merged.some((fact) => fact.fact === 'Joanna likes the animal turtles and finds them motivating.')).toBe(true);
});

it('backfills shared elementary-school history from class memories', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2022-07-22]',
'John: Your support means a lot to me. Remember this photo from elementary school?',
'James: Indeed, I remember this moment. We loved skateboards back then, sometimes we even left class early to do it.',
].join('\n'),
);

expect(merged.some((fact) => fact.fact === 'John and James attended elementary school and class together.')).toBe(true);
});

it('upgrades weaker same-shape school facts with shared class evidence', () => {
const primary = baseFact({
fact: 'John and James are friends who knew each other since elementary school.',
headline: 'John and James knew each other in school',
type: 'person',
keywords: ['john', 'james', 'elementary', 'school'],
entities: [
{ name: 'John', type: 'person' },
{ name: 'James', type: 'person' },
],
relations: [{ source: 'John', target: 'James', type: 'knows' }],
});

const merged = mergeSupplementalFacts(
[primary],
[
'[Session date: 2022-07-22]',
'John: Remember this photo from elementary school?',
'James: Indeed, I remember this moment. We loved skateboards back then, sometimes we even left class early to do it.',
].join('\n'),
);

expect(merged.some((fact) => fact.fact === 'John and James attended elementary school and class together.')).toBe(true);
expect(merged.some((fact) => fact.fact === primary.fact)).toBe(false);
});

it('backfills tournament-win facts when the primary extractor misses them', () => {
const merged = mergeSupplementalFacts(
[baseFact({ fact: 'As of August 22 2022, Nate makes a living as a professional gamer and is passionate about his career.' })],
[
'[Session date: 2022-08-22]',
'Nate: Woah Joanna, I won an international tournament yesterday! It was wild.',
].join('\n'),
);

expect(merged.some((fact) => fact.fact.includes('won an international tournament yesterday (on August 21, 2022)'))).toBe(true);
});

it('keeps competition-win facts that are embedded before a follow-up question', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2023-01-20T16:04:00.000Z]',
'Jon: Woah, that pic\'s from when my dance crew took home first in a local comp last year. It was amazing up on that stage! Gina, you ever been in any dance comps or shows?',
].join('\n'),
);

expect(merged.some((fact) => fact.fact.includes('Jon\'s dance crew won first place in a local competition last year'))).toBe(true);
});

it('preserves image captions and visual tags as searchable evidence', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2023-07-05T18:59:00.000Z]',
'John: Oh, and here\'s a pic I got from my walk last week.',
' Image caption: a photo of a sunset over the ocean with a sailboat in the distance',
' Image query: sunset beach colorful ocean',
].join('\n'),
);

const visualFact = merged.find((fact) => fact.fact.includes('visual tags "sunset beach colorful ocean"'));
expect(visualFact?.fact).toContain('John shared image evidence');
expect(visualFact?.fact).toContain('a photo of a sunset over the ocean');
expect(visualFact?.keywords).toContain('beach');
});

it('derives beach-walk evidence from visual tags and walk text', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2023-07-05T18:59:00.000Z]',
'John: Here\'s a pic I got from my walk last week.',
' Image caption: a photo of a sunset over the ocean with a sailboat in the distance',
' Image query: sunset beach colorful ocean',
].join('\n'),
);

expect(merged.some((fact) => fact.fact.includes('John went for a walk by the beach or ocean'))).toBe(true);
});

it('keeps multiple unique visual facts from the same speaker', () => {
const merged = mergeSupplementalFacts(
[],
[
'[Session date: 2023-05-01T18:24:00.000Z]',
'Dave: I opened my own car maintenance shop. Take a look.',
' Image caption: a photo of a car dealership with cars parked in front of it',
' Image query: car maintenance shop exterior',
'Dave: This is a photo of my shop. Come by sometime.',
' Image caption: a photo of a group of people standing in front of a car',
' Image query: car maintenance shop grand opening',
].join('\n'),
);

expect(merged.some((fact) => fact.fact.includes('car maintenance shop exterior'))).toBe(true);
expect(merged.some((fact) => fact.fact.includes('group of people standing in front of a car'))).toBe(true);
expect(merged.some((fact) => fact.fact.includes('car maintenance shop grand opening'))).toBe(true);
});
});
53 changes: 51 additions & 2 deletions src/services/__tests__/temporal-endpoint-evidence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

import { describe, expect, it } from 'vitest';
import { createSearchResult } from './test-fixtures.js';
import { buildRepeatedEventEndpointBlock } from '../temporal-endpoint-evidence.js';
import {
buildRepeatedEventEndpointBlock,
buildTemporalEvidenceBlock,
} from '../temporal-endpoint-evidence.js';

function makeMemory(id: string, content: string, date: string) {
return createSearchResult({
Expand Down Expand Up @@ -39,7 +42,7 @@ describe('buildRepeatedEventEndpointBlock', () => {
expect(block).toBe('');
});

it('does not emit for non-repeated-event temporal queries', () => {
it('keeps the repeated-event block narrow for non-repeated temporal queries', () => {
const block = buildRepeatedEventEndpointBlock([
makeMemory('first', 'James met Samantha.', '2022-08-10'),
makeMemory('second', 'James and Samantha decided to move in.', '2022-10-31'),
Expand All @@ -48,6 +51,52 @@ describe('buildRepeatedEventEndpointBlock', () => {
expect(block).toBe('');
});

it('emits a compact general temporal block for non-repeated temporal queries', () => {
const block = buildTemporalEvidenceBlock([
makeMemory('first', 'James met Samantha during a beach outing.', '2022-08-10'),
makeMemory('second', 'James and Samantha decided to move in together.', '2022-10-31'),
makeMemory('noise', 'James took his dog to the park.', '2022-09-01'),
], 'How long did James and Samantha date before moving in?');

expect(block).toContain('Temporal evidence candidates:');
expect(block).toContain('earliest matching event: 2022-08-10');
expect(block).toContain('latest matching event: 2022-10-31');
expect(block).toContain('elapsed between endpoints: ~3 months (82 days)');
});

it('normalizes common temporal verb forms when selecting general evidence', () => {
const block = buildTemporalEvidenceBlock([
makeMemory('winner', 'Nate won an international gaming tournament.', '2022-08-21'),
makeMemory('adoption', 'Andrew adopted Buddy after adopting Toby earlier in the year.', '2022-10-29'),
], 'When did Nate win an international tournament?');

expect(block).toContain('matching event: 2022-08-21');
});

it('prefers completed repeated events over later planning-like events', () => {
const block = buildTemporalEvidenceBlock([
makeMemory('first', "Sam had a doctor's appointment as a wake-up call.", '2023-05-24'),
makeMemory('second', 'Sam had another doctor appointment after improving diet and exercise.', '2023-08-15'),
makeMemory('plan', 'Sam decided to make a new appointment in January after the holidays.', '2024-01-10'),
], "How many months lapsed between Sam's first and second doctor's appointment?");

expect(block).toContain('first matching event: 2023-05-24');
expect(block).toContain('second matching event: 2023-08-15');
expect(block).not.toContain('2024-01-10');
});

it('penalizes planning-like later events for general duration questions', () => {
const block = buildTemporalEvidenceBlock([
makeMemory('first', 'Sam had a doctor appointment as a wake-up call.', '2023-05-24'),
makeMemory('second', 'Sam had a second doctor appointment after changing diet.', '2023-08-15'),
makeMemory('plan', 'Sam is going to make a new doctor appointment in January.', '2024-01-10'),
], "How long was it between Sam's doctor appointments?");

expect(block).toContain('earliest matching event: 2023-05-24');
expect(block).toContain('latest matching event: 2023-08-15');
expect(block).not.toContain('2024-01-10');
});

it('rejects partial-match endpoints (one memory hits "doctor", another hits "appointment")', () => {
const block = buildRepeatedEventEndpointBlock([
makeMemory('doc-only', 'Sam saw the doctor about a sore knee.', '2023-05-24'),
Expand Down
Loading
Loading