style: run prettier on TS SDK files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
3ae22be8a2
commit
e1e033daef
@@ -120,7 +120,6 @@
|
|||||||
"better-sqlite3": "^12.6.2",
|
"better-sqlite3": "^12.6.2",
|
||||||
"cloudflare": "^4.2.0",
|
"cloudflare": "^4.2.0",
|
||||||
"groq-sdk": "0.3.0",
|
"groq-sdk": "0.3.0",
|
||||||
|
|
||||||
"ollama": "^0.5.14",
|
"ollama": "^0.5.14",
|
||||||
"pg": "8.11.3",
|
"pg": "8.11.3",
|
||||||
"redis": "^4.6.13",
|
"redis": "^4.6.13",
|
||||||
|
|||||||
@@ -168,7 +168,10 @@ export class Memory {
|
|||||||
};
|
};
|
||||||
// For file-based stores (memory/SQLite), use a separate DB path for entities
|
// For file-based stores (memory/SQLite), use a separate DB path for entities
|
||||||
if (entityConfig.dbPath) {
|
if (entityConfig.dbPath) {
|
||||||
entityConfig.dbPath = entityConfig.dbPath.replace(/\.db$/, "_entities.db");
|
entityConfig.dbPath = entityConfig.dbPath.replace(
|
||||||
|
/\.db$/,
|
||||||
|
"_entities.db",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this._entityStore = VectorStoreFactory.create(
|
this._entityStore = VectorStoreFactory.create(
|
||||||
this.config.vectorStore.provider,
|
this.config.vectorStore.provider,
|
||||||
@@ -359,8 +362,7 @@ export class Memory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Phase 2: LLM extraction (single call)
|
// Phase 2: LLM extraction (single call)
|
||||||
const isAgentScoped =
|
const isAgentScoped = !!filters.agentId && !filters.userId;
|
||||||
!!filters.agentId && !filters.userId;
|
|
||||||
let systemPrompt = ADDITIVE_EXTRACTION_PROMPT;
|
let systemPrompt = ADDITIVE_EXTRACTION_PROMPT;
|
||||||
if (isAgentScoped) {
|
if (isAgentScoped) {
|
||||||
systemPrompt += AGENT_CONTEXT_SUFFIX;
|
systemPrompt += AGENT_CONTEXT_SUFFIX;
|
||||||
@@ -404,8 +406,7 @@ export class Memory {
|
|||||||
extractedMemories = parsed.memory;
|
extractedMemories = parsed.memory;
|
||||||
} catch {
|
} catch {
|
||||||
const fallbackJson = extractJson(cleanResponse);
|
const fallbackJson = extractJson(cleanResponse);
|
||||||
extractedMemories =
|
extractedMemories = JSON.parse(fallbackJson)?.memory ?? [];
|
||||||
JSON.parse(fallbackJson)?.memory ?? [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -565,9 +566,7 @@ export class Memory {
|
|||||||
hr.createdAt,
|
hr.createdAt,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(
|
console.error(`Failed to add history for ${hr.memoryId}: ${e}`);
|
||||||
`Failed to add history for ${hr.memoryId}: ${e}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -582,9 +581,7 @@ export class Memory {
|
|||||||
hr.createdAt,
|
hr.createdAt,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(
|
console.error(`Failed to add history for ${hr.memoryId}: ${e}`);
|
||||||
`Failed to add history for ${hr.memoryId}: ${e}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,8 +598,7 @@ export class Memory {
|
|||||||
> = {};
|
> = {};
|
||||||
for (let idx = 0; idx < records.length; idx++) {
|
for (let idx = 0; idx < records.length; idx++) {
|
||||||
const memoryId = records[idx].memoryId;
|
const memoryId = records[idx].memoryId;
|
||||||
const entities =
|
const entities = idx < allEntities.length ? allEntities[idx] : [];
|
||||||
idx < allEntities.length ? allEntities[idx] : [];
|
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
const key = entity.text.trim().toLowerCase();
|
const key = entity.text.trim().toLowerCase();
|
||||||
if (key in globalEntities) {
|
if (key in globalEntities) {
|
||||||
@@ -656,8 +652,7 @@ export class Memory {
|
|||||||
const toInsertPayloads: Record<string, any>[] = [];
|
const toInsertPayloads: Record<string, any>[] = [];
|
||||||
|
|
||||||
for (const { index: j, key } of valid) {
|
for (const { index: j, key } of valid) {
|
||||||
const { entityType, entityText, memoryIds } =
|
const { entityType, entityText, memoryIds } = globalEntities[key];
|
||||||
globalEntities[key];
|
|
||||||
const entityVec = entityEmbeddings[j]!;
|
const entityVec = entityEmbeddings[j]!;
|
||||||
|
|
||||||
let matches: Array<{
|
let matches: Array<{
|
||||||
@@ -666,31 +661,20 @@ export class Memory {
|
|||||||
payload: Record<string, any>;
|
payload: Record<string, any>;
|
||||||
}> = [];
|
}> = [];
|
||||||
try {
|
try {
|
||||||
matches = await entityStore.search(
|
matches = await entityStore.search(entityVec, 1, searchFilters);
|
||||||
entityVec,
|
|
||||||
1,
|
|
||||||
searchFilters,
|
|
||||||
);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
if (
|
if (matches.length > 0 && (matches[0].score ?? 0) >= 0.95) {
|
||||||
matches.length > 0 &&
|
|
||||||
(matches[0].score ?? 0) >= 0.95
|
|
||||||
) {
|
|
||||||
// Update existing entity
|
// Update existing entity
|
||||||
const match = matches[0];
|
const match = matches[0];
|
||||||
const payload = match.payload || {};
|
const payload = match.payload || {};
|
||||||
const linked = new Set<string>(
|
const linked = new Set<string>(payload.linkedMemoryIds ?? []);
|
||||||
payload.linkedMemoryIds ?? [],
|
|
||||||
);
|
|
||||||
for (const mid of memoryIds) linked.add(mid);
|
for (const mid of memoryIds) linked.add(mid);
|
||||||
payload.linkedMemoryIds = Array.from(linked).sort();
|
payload.linkedMemoryIds = Array.from(linked).sort();
|
||||||
try {
|
try {
|
||||||
await entityStore.update(match.id, entityVec, payload);
|
await entityStore.update(match.id, entityVec, payload);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.debug(
|
console.debug(`Entity update failed for '${entityText}': ${e}`);
|
||||||
`Entity update failed for '${entityText}': ${e}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// New entity — collect for batch insert
|
// New entity — collect for batch insert
|
||||||
@@ -857,10 +841,7 @@ export class Memory {
|
|||||||
// Step 5: Compute BM25 scores from keyword results
|
// Step 5: Compute BM25 scores from keyword results
|
||||||
const bm25Scores: Record<string, number> = {};
|
const bm25Scores: Record<string, number> = {};
|
||||||
if (keywordResults) {
|
if (keywordResults) {
|
||||||
const [midpoint, steepness] = getBm25Params(
|
const [midpoint, steepness] = getBm25Params(query, queryLemmatized);
|
||||||
query,
|
|
||||||
queryLemmatized,
|
|
||||||
);
|
|
||||||
for (const mem of keywordResults) {
|
for (const mem of keywordResults) {
|
||||||
const memId = String(mem.id);
|
const memId = String(mem.id);
|
||||||
const rawScore = mem.score ?? 0;
|
const rawScore = mem.score ?? 0;
|
||||||
@@ -888,17 +869,14 @@ export class Memory {
|
|||||||
if (deduped.length > 0) {
|
if (deduped.length > 0) {
|
||||||
const searchFilters: SearchFilters = {};
|
const searchFilters: SearchFilters = {};
|
||||||
if (filters.userId) searchFilters.userId = filters.userId;
|
if (filters.userId) searchFilters.userId = filters.userId;
|
||||||
if (filters.agentId)
|
if (filters.agentId) searchFilters.agentId = filters.agentId;
|
||||||
searchFilters.agentId = filters.agentId;
|
|
||||||
if (filters.runId) searchFilters.runId = filters.runId;
|
if (filters.runId) searchFilters.runId = filters.runId;
|
||||||
|
|
||||||
const entityStore = await this.getEntityStore();
|
const entityStore = await this.getEntityStore();
|
||||||
|
|
||||||
for (const entity of deduped) {
|
for (const entity of deduped) {
|
||||||
try {
|
try {
|
||||||
const entityEmbedding = await this.embedder.embed(
|
const entityEmbedding = await this.embedder.embed(entity.text);
|
||||||
entity.text,
|
|
||||||
);
|
|
||||||
const matches = await entityStore.search(
|
const matches = await entityStore.search(
|
||||||
entityEmbedding,
|
entityEmbedding,
|
||||||
500,
|
500,
|
||||||
@@ -910,22 +888,15 @@ export class Memory {
|
|||||||
if (similarity < 0.5) continue;
|
if (similarity < 0.5) continue;
|
||||||
|
|
||||||
const payload = match.payload || {};
|
const payload = match.payload || {};
|
||||||
const linkedMemoryIds =
|
const linkedMemoryIds = payload.linkedMemoryIds ?? [];
|
||||||
payload.linkedMemoryIds ?? [];
|
|
||||||
if (!Array.isArray(linkedMemoryIds)) continue;
|
if (!Array.isArray(linkedMemoryIds)) continue;
|
||||||
|
|
||||||
// Spread-attenuated boost
|
// Spread-attenuated boost
|
||||||
const numLinked = Math.max(
|
const numLinked = Math.max(linkedMemoryIds.length, 1);
|
||||||
linkedMemoryIds.length,
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
const memoryCountWeight =
|
const memoryCountWeight =
|
||||||
1.0 /
|
1.0 / (1.0 + 0.001 * (numLinked - 1) ** 2);
|
||||||
(1.0 + 0.001 * (numLinked - 1) ** 2);
|
|
||||||
const boost =
|
const boost =
|
||||||
similarity *
|
similarity * ENTITY_BOOST_WEIGHT * memoryCountWeight;
|
||||||
ENTITY_BOOST_WEIGHT *
|
|
||||||
memoryCountWeight;
|
|
||||||
|
|
||||||
for (const memoryId of linkedMemoryIds) {
|
for (const memoryId of linkedMemoryIds) {
|
||||||
if (memoryId) {
|
if (memoryId) {
|
||||||
@@ -990,10 +961,7 @@ export class Memory {
|
|||||||
metadata: {
|
metadata: {
|
||||||
...Object.entries(payload)
|
...Object.entries(payload)
|
||||||
.filter(([key]) => !excludedKeys.has(key))
|
.filter(([key]) => !excludedKeys.has(key))
|
||||||
.reduce(
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
|
||||||
(acc, [key, value]) => ({ ...acc, [key]: value }),
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
scoreBreakdown: scored.scoreBreakdown,
|
scoreBreakdown: scored.scoreBreakdown,
|
||||||
},
|
},
|
||||||
...(payload.userId && { userId: payload.userId }),
|
...(payload.userId && { userId: payload.userId }),
|
||||||
|
|||||||
@@ -425,7 +425,10 @@ function extractProper(text: string): ExtractedEntity[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isLabel = i + 1 < tokens.length && tokens[i + 1] === ":";
|
const isLabel = i + 1 < tokens.length && tokens[i + 1] === ":";
|
||||||
const isCap = tok.length > 0 && tok.charAt(0) === tok.charAt(0).toUpperCase() && /[A-Z]/.test(tok.charAt(0));
|
const isCap =
|
||||||
|
tok.length > 0 &&
|
||||||
|
tok.charAt(0) === tok.charAt(0).toUpperCase() &&
|
||||||
|
/[A-Z]/.test(tok.charAt(0));
|
||||||
|
|
||||||
if (isCap && !isLabel) {
|
if (isCap && !isLabel) {
|
||||||
const seq: Array<{ token: string; idx: number }> = [
|
const seq: Array<{ token: string; idx: number }> = [
|
||||||
@@ -508,7 +511,9 @@ function extractCompoundsWithNlp(text: string): ExtractedEntity[] {
|
|||||||
if (GENERIC_HEADS.has(head)) {
|
if (GENERIC_HEADS.has(head)) {
|
||||||
// Check if there's a specific modifier
|
// Check if there's a specific modifier
|
||||||
const hasSpecificMod = words.some(
|
const hasSpecificMod = words.some(
|
||||||
(w) => !NON_SPECIFIC_ADJ.has(w.toLowerCase()) && w !== words[words.length - 1],
|
(w) =>
|
||||||
|
!NON_SPECIFIC_ADJ.has(w.toLowerCase()) &&
|
||||||
|
w !== words[words.length - 1],
|
||||||
);
|
);
|
||||||
if (!hasSpecificMod) {
|
if (!hasSpecificMod) {
|
||||||
continue;
|
continue;
|
||||||
@@ -516,7 +521,9 @@ function extractCompoundsWithNlp(text: string): ExtractedEntity[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter non-specific adjectives from the beginning
|
// Filter non-specific adjectives from the beginning
|
||||||
const filtered = words.filter((w) => !NON_SPECIFIC_ADJ.has(w.toLowerCase()));
|
const filtered = words.filter(
|
||||||
|
(w) => !NON_SPECIFIC_ADJ.has(w.toLowerCase()),
|
||||||
|
);
|
||||||
const cleaned = stripGenericEnding(filtered);
|
const cleaned = stripGenericEnding(filtered);
|
||||||
|
|
||||||
if (cleaned.length >= 2) {
|
if (cleaned.length >= 2) {
|
||||||
@@ -560,16 +567,11 @@ function extractCompoundsRegex(text: string): ExtractedEntity[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Also try lowercase compound patterns (e.g., "machine learning", "deep learning")
|
// Also try lowercase compound patterns (e.g., "machine learning", "deep learning")
|
||||||
const lowerCompoundRe =
|
const lowerCompoundRe = /\b([a-z]+(?:\s+[a-z]+){1,3})\b/g;
|
||||||
/\b([a-z]+(?:\s+[a-z]+){1,3})\b/g;
|
|
||||||
while ((match = lowerCompoundRe.exec(text)) !== null) {
|
while ((match = lowerCompoundRe.exec(text)) !== null) {
|
||||||
const phrase = match[1].trim();
|
const phrase = match[1].trim();
|
||||||
const words = phrase.split(/\s+/);
|
const words = phrase.split(/\s+/);
|
||||||
if (
|
if (words.length >= 2 && words.length <= 4 && phrase.length > 5) {
|
||||||
words.length >= 2 &&
|
|
||||||
words.length <= 4 &&
|
|
||||||
phrase.length > 5
|
|
||||||
) {
|
|
||||||
const head = words[words.length - 1].toLowerCase();
|
const head = words[words.length - 1].toLowerCase();
|
||||||
const allGeneric = words.every(
|
const allGeneric = words.every(
|
||||||
(w) =>
|
(w) =>
|
||||||
|
|||||||
@@ -269,11 +269,7 @@ export function lemmatizeForBm25(text: string): string {
|
|||||||
|
|
||||||
// Also add original if it ends in -ing and differs from stem.
|
// Also add original if it ends in -ing and differs from stem.
|
||||||
// This handles noun/verb ambiguity (meeting/meet, attending/attend).
|
// This handles noun/verb ambiguity (meeting/meet, attending/attend).
|
||||||
if (
|
if (word.endsWith("ing") && word !== stemmed && /^[a-z0-9]+$/.test(word)) {
|
||||||
word.endsWith("ing") &&
|
|
||||||
word !== stemmed &&
|
|
||||||
/^[a-z0-9]+$/.test(word)
|
|
||||||
) {
|
|
||||||
tokens.push(word);
|
tokens.push(word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,10 +111,9 @@ describe("Memory - add()", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("result item has a memory string field", async () => {
|
test("result item has a memory string field", async () => {
|
||||||
const result: SearchResult = await memory.add(
|
const result: SearchResult = await memory.add("My favorite color is blue", {
|
||||||
"My favorite color is blue",
|
userId,
|
||||||
{ userId },
|
});
|
||||||
);
|
|
||||||
expect(typeof result.results[0].memory).toBe("string");
|
expect(typeof result.results[0].memory).toBe("string");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ jest.mock("../src/llms/openai", () => ({
|
|||||||
const newMsgMatch = content.match(
|
const newMsgMatch = content.match(
|
||||||
/## New Messages\n([\s\S]*?)(?=\n##|$)/,
|
/## New Messages\n([\s\S]*?)(?=\n##|$)/,
|
||||||
);
|
);
|
||||||
const extracted = newMsgMatch
|
const extracted = newMsgMatch ? newMsgMatch[1].trim() : "stored fact";
|
||||||
? newMsgMatch[1].trim()
|
|
||||||
: "stored fact";
|
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
memory: [
|
memory: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ jest.mock("../src/llms/openai", () => ({
|
|||||||
const newMsgMatch = content.match(
|
const newMsgMatch = content.match(
|
||||||
/## New Messages\n([\s\S]*?)(?=\n##|$)/,
|
/## New Messages\n([\s\S]*?)(?=\n##|$)/,
|
||||||
);
|
);
|
||||||
const extracted = newMsgMatch
|
const extracted = newMsgMatch ? newMsgMatch[1].trim() : "test fact";
|
||||||
? newMsgMatch[1].trim()
|
|
||||||
: "test fact";
|
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
memory: [
|
memory: [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user