initial 2
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-06-26 09:26:50 +01:00
parent 194330fb47
commit 3b37368e7d
213 changed files with 14688 additions and 1 deletions

View File

@@ -0,0 +1,89 @@
import { z } from "zod";
import type { MemoryCategory, MemorySourceType } from "@adventureos/shared";
import { SENSITIVE_MEMORY_CATEGORIES } from "@adventureos/shared";
import { parseAiJson } from "@/lib/ai/parse-json";
import { getProvider } from "@/lib/ai/provider-registry";
import { getAiBehaviorConfig, getAiProviderConfig, buildSystemPrompt } from "./ai-config";
import { createSuggestion, getMemoryLearningSettings } from "./ai-memory";
const candidateSchema = z.object({
candidates: z
.array(
z.object({
category: z.string(),
title: z.string(),
content: z.string(),
confidence: z.coerce.number(),
sensitivity: z.enum(["normal", "private", "sensitive"]).optional(),
})
)
.max(2),
});
export async function extractMemoryCandidates(
userId: string,
sourceType: MemorySourceType,
sourceText: string,
sourceRef?: { type: string; id?: string; date?: string }
) {
const settings = await getMemoryLearningSettings(userId);
if (!settings.learningEnabled || !settings.autoSuggestEnabled) return [];
const behavior = await getAiBehaviorConfig(userId);
const providerConfig = await getAiProviderConfig(userId);
if (!behavior.enabled || !providerConfig.enabled) return [];
const prompt = `From this user text, suggest 0-2 personal memory facts the app could remember (only clear patterns, not guesses).
User text:
"""
${sourceText.slice(0, 1500)}
"""
Return JSON: { "candidates": [{ "category": "likes|motivators|current_goals|reading_preferences|learning_interests|worries|...", "title": "short label", "content": "one sentence fact", "confidence": 0.0-1.0, "sensitivity": "normal|private|sensitive" }] }
If nothing clear, return { "candidates": [] }.`;
try {
const coreSystem = await getTemplateBody(userId);
const provider = getProvider(providerConfig.type);
const res = await provider.generateText(providerConfig, {
model: providerConfig.model,
prompt,
system: buildSystemPrompt(behavior, coreSystem),
format: "json",
temperature: 0.3,
maxTokens: 400,
timeoutMs: providerConfig.timeoutMs,
});
const parsed = candidateSchema.safeParse(parseAiJson(res.text));
if (!parsed.success) return [];
const results = [];
for (const c of parsed.data.candidates) {
if (c.confidence < 0.6) continue;
if (!settings.allowedCategories.includes(c.category as MemoryCategory)) continue;
if (
SENSITIVE_MEMORY_CATEGORIES.includes(c.category as MemoryCategory) &&
!settings.allowSensitiveCategories
) {
continue;
}
const row = await createSuggestion(userId, {
category: c.category as MemoryCategory,
title: c.title,
content: c.content,
sourceType,
sourceRef,
confidence: c.confidence,
});
if (row) results.push(row);
}
return results;
} catch {
return [];
}
}
async function getTemplateBody(userId: string) {
const { getTemplateBody: getTpl } = await import("./ai-templates");
return getTpl(userId, "system_core");
}