Skip to content

Commit b3aee55

Browse files
ferhimedamineSDK LeadPaperclip-Paperclip
authored
fix(recall): prefer smart_score as ranking key in flattenRecalledMemory (#194)
Server sorts recall results by smart_score (fallback weighted_score → score) but flattenRecalledMemory only copied item.score, causing .score to differ from true rank order. Fix resolves score as smart_score ?? weighted_score ?? score. Exposes smart_score and weighted_score as optional fields on RecalledMemory type. Tests: 3 new tests covering smart_score priority, weighted_score fallback, and raw score fallback in nested recall envelope. Co-authored-by: SDK Lead <sdk-lead@dakera.ai> Co-authored-by: Paperclip <noreply@paperclip.ing>
1 parent 4c383cf commit b3aee55

3 files changed

Lines changed: 78 additions & 3 deletions

File tree

src/client.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,6 +1721,69 @@ describe('DakeraClient', () => {
17211721
expect(body.routing).toBe('hybrid');
17221722
expect(body.rerank).toBe(true);
17231723
});
1724+
1725+
it('should use smart_score as .score when server returns nested recall with smart_score', async () => {
1726+
mockFetch.mockResolvedValueOnce({
1727+
ok: true,
1728+
headers: new Headers({ 'content-type': 'application/json' }),
1729+
json: async () => ({
1730+
memories: [
1731+
{
1732+
memory: { id: 'mem_1', content: 'ranked memory', memory_type: 'episodic', importance: 0.8 },
1733+
score: 0.5,
1734+
weighted_score: 0.7,
1735+
smart_score: 0.9,
1736+
},
1737+
],
1738+
}),
1739+
});
1740+
1741+
const result = await client.recall('agent-1', 'test');
1742+
const m = result.memories[0];
1743+
expect(m.score).toBeCloseTo(0.9);
1744+
expect(m.smart_score).toBeCloseTo(0.9);
1745+
expect(m.weighted_score).toBeCloseTo(0.7);
1746+
});
1747+
1748+
it('should fall back to weighted_score when smart_score absent', async () => {
1749+
mockFetch.mockResolvedValueOnce({
1750+
ok: true,
1751+
headers: new Headers({ 'content-type': 'application/json' }),
1752+
json: async () => ({
1753+
memories: [
1754+
{
1755+
memory: { id: 'mem_2', content: 'mem', memory_type: 'episodic', importance: 0.5 },
1756+
score: 0.4,
1757+
weighted_score: 0.65,
1758+
},
1759+
],
1760+
}),
1761+
});
1762+
1763+
const result = await client.recall('agent-1', 'test');
1764+
expect(result.memories[0].score).toBeCloseTo(0.65);
1765+
expect(result.memories[0].smart_score).toBeUndefined();
1766+
expect(result.memories[0].weighted_score).toBeCloseTo(0.65);
1767+
});
1768+
1769+
it('should fall back to raw score when neither smart_score nor weighted_score present', async () => {
1770+
mockFetch.mockResolvedValueOnce({
1771+
ok: true,
1772+
headers: new Headers({ 'content-type': 'application/json' }),
1773+
json: async () => ({
1774+
memories: [
1775+
{
1776+
memory: { id: 'mem_3', content: 'mem', memory_type: 'episodic', importance: 0.5 },
1777+
score: 0.55,
1778+
},
1779+
],
1780+
}),
1781+
});
1782+
1783+
const result = await client.recall('agent-1', 'test');
1784+
expect(result.memories[0].score).toBeCloseTo(0.55);
1785+
expect(result.memories[0].smart_score).toBeUndefined();
1786+
});
17241787
});
17251788

17261789
// ---------------------------------------------------------------------------

src/client.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,19 @@ function parseErrorCode(raw: unknown): ErrorCode {
2525
return ErrorCode.UNKNOWN;
2626
}
2727

28-
/** Flatten server's nested recall item `{memory: {...}, score}` to flat `RecalledMemory`. */
28+
/** Flatten server's nested recall item `{memory: {...}, score, weighted_score, smart_score}` to flat `RecalledMemory`. */
2929
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3030
function flattenRecalledMemory(item: any): any {
3131
if (item && typeof item === 'object' && item.memory && typeof item.memory === 'object') {
32-
return { ...item.memory, score: item.score, depth: item.depth };
32+
// smart_score is the server's ranking key; prefer it so .score reflects actual rank order.
33+
const score = item.smart_score ?? item.weighted_score ?? item.score;
34+
return {
35+
...item.memory,
36+
score,
37+
smart_score: item.smart_score,
38+
weighted_score: item.weighted_score,
39+
depth: item.depth,
40+
};
3341
}
3442
return item;
3543
}

src/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,12 @@ export interface RecalledMemory {
563563
memory_type: string;
564564
/** Importance score */
565565
importance: number;
566-
/** Similarity score */
566+
/** Ranking score — equals smart_score when present, then weighted_score, then raw score. */
567567
score: number;
568+
/** Raw smart_score from the server (the primary ranking key). */
569+
smart_score?: number;
570+
/** Raw weighted_score from the server. */
571+
weighted_score?: number;
568572
/** Optional metadata */
569573
metadata?: Record<string, unknown>;
570574
/** Creation timestamp */

0 commit comments

Comments
 (0)