haroo 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/index.js +84883 -0
- package/package.json +73 -0
- package/src/__tests__/e2e/EventService.test.ts +211 -0
- package/src/__tests__/unit/Event.test.ts +89 -0
- package/src/__tests__/unit/Memory.test.ts +130 -0
- package/src/application/graph/builder.ts +106 -0
- package/src/application/graph/edges.ts +37 -0
- package/src/application/graph/nodes/addEvent.ts +113 -0
- package/src/application/graph/nodes/chat.ts +128 -0
- package/src/application/graph/nodes/extractMemory.ts +135 -0
- package/src/application/graph/nodes/index.ts +8 -0
- package/src/application/graph/nodes/query.ts +194 -0
- package/src/application/graph/nodes/respond.ts +26 -0
- package/src/application/graph/nodes/router.ts +82 -0
- package/src/application/graph/nodes/toolExecutor.ts +79 -0
- package/src/application/graph/nodes/types.ts +2 -0
- package/src/application/index.ts +4 -0
- package/src/application/services/DiaryService.ts +188 -0
- package/src/application/services/EventService.ts +61 -0
- package/src/application/services/index.ts +2 -0
- package/src/application/tools/calendarTool.ts +179 -0
- package/src/application/tools/diaryTool.ts +182 -0
- package/src/application/tools/index.ts +68 -0
- package/src/config/env.ts +33 -0
- package/src/config/index.ts +1 -0
- package/src/domain/entities/DiaryEntry.ts +16 -0
- package/src/domain/entities/Event.ts +13 -0
- package/src/domain/entities/Memory.ts +20 -0
- package/src/domain/index.ts +5 -0
- package/src/domain/interfaces/IDiaryRepository.ts +21 -0
- package/src/domain/interfaces/IEventsRepository.ts +12 -0
- package/src/domain/interfaces/ILanguageModel.ts +23 -0
- package/src/domain/interfaces/IMemoriesRepository.ts +15 -0
- package/src/domain/interfaces/IMemory.ts +19 -0
- package/src/domain/interfaces/index.ts +4 -0
- package/src/domain/state/AgentState.ts +30 -0
- package/src/index.ts +5 -0
- package/src/infrastructure/database/factory.ts +52 -0
- package/src/infrastructure/database/index.ts +21 -0
- package/src/infrastructure/database/sqlite-checkpointer.ts +179 -0
- package/src/infrastructure/database/sqlite-client.ts +69 -0
- package/src/infrastructure/database/sqlite-diary-repository.ts +209 -0
- package/src/infrastructure/database/sqlite-events-repository.ts +167 -0
- package/src/infrastructure/database/sqlite-memories-repository.ts +284 -0
- package/src/infrastructure/database/sqlite-schema.ts +98 -0
- package/src/infrastructure/index.ts +3 -0
- package/src/infrastructure/llm/base.ts +14 -0
- package/src/infrastructure/llm/gemini.ts +139 -0
- package/src/infrastructure/llm/index.ts +22 -0
- package/src/infrastructure/llm/ollama.ts +126 -0
- package/src/infrastructure/llm/openai.ts +148 -0
- package/src/infrastructure/memory/checkpointer.ts +19 -0
- package/src/infrastructure/memory/index.ts +2 -0
- package/src/infrastructure/settings/index.ts +96 -0
- package/src/interface/cli/calendar.ts +120 -0
- package/src/interface/cli/chat.ts +185 -0
- package/src/interface/cli/commands.ts +337 -0
- package/src/interface/cli/printer.ts +65 -0
- package/src/interface/index.ts +1 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ToolDefinition } from "../../domain/interfaces/ILanguageModel";
|
|
3
|
+
import type { ToolExecutor } from "../graph/nodes";
|
|
4
|
+
import type { IDiaryRepository } from "../../domain/interfaces/IDiaryRepository";
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Schemas
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export const QueryDiarySchema = z.object({
|
|
11
|
+
type: z.enum(["today", "yesterday", "week", "date", "range"]),
|
|
12
|
+
date: z.string().optional().describe("Specific date (YYYY-MM-DD) for 'date' type"),
|
|
13
|
+
startDate: z.string().optional().describe("Start date for 'range' type"),
|
|
14
|
+
endDate: z.string().optional().describe("End date for 'range' type"),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type QueryDiaryInput = z.infer<typeof QueryDiarySchema>;
|
|
18
|
+
|
|
19
|
+
export const GetMoodTrendSchema = z.object({
|
|
20
|
+
days: z.number().optional().describe("Number of days for trend analysis (default: 30)"),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type GetMoodTrendInput = z.infer<typeof GetMoodTrendSchema>;
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Tool Definitions (for LLM binding)
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
export const queryDiaryToolDefinition: ToolDefinition = {
|
|
30
|
+
name: "query_diary",
|
|
31
|
+
description:
|
|
32
|
+
"Query diary/journal entries by date or range. Returns conversation summaries, mood, and therapeutic advice from past days.",
|
|
33
|
+
parameters: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
type: {
|
|
37
|
+
type: "string",
|
|
38
|
+
enum: ["today", "yesterday", "week", "date", "range"],
|
|
39
|
+
description: "Query type",
|
|
40
|
+
},
|
|
41
|
+
date: { type: "string", description: "Specific date (YYYY-MM-DD) for 'date' type" },
|
|
42
|
+
startDate: { type: "string", description: "Start date for 'range' type (YYYY-MM-DD)" },
|
|
43
|
+
endDate: { type: "string", description: "End date for 'range' type (YYYY-MM-DD)" },
|
|
44
|
+
},
|
|
45
|
+
required: ["type"],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const getMoodTrendToolDefinition: ToolDefinition = {
|
|
50
|
+
name: "get_mood_trend",
|
|
51
|
+
description:
|
|
52
|
+
"Get mood trend analysis over time to understand emotional patterns. Returns mood scores and labels for recent days.",
|
|
53
|
+
parameters: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
days: { type: "number", description: "Number of days for trend analysis (default: 30)" },
|
|
57
|
+
},
|
|
58
|
+
required: [],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const diaryToolDefinitions: ToolDefinition[] = [
|
|
63
|
+
queryDiaryToolDefinition,
|
|
64
|
+
getMoodTrendToolDefinition,
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Tool Executor Factory - Creates executors with database repository
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
export function createDiaryToolExecutors(
|
|
72
|
+
diaryRepository: IDiaryRepository
|
|
73
|
+
): Record<string, ToolExecutor> {
|
|
74
|
+
const queryDiaryExecutor: ToolExecutor = async (args) => {
|
|
75
|
+
const result = QueryDiarySchema.safeParse(args);
|
|
76
|
+
if (!result.success) {
|
|
77
|
+
throw new Error(`Validation Failed: ${result.error.message}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const input = result.data;
|
|
81
|
+
const now = new Date();
|
|
82
|
+
let startDate: Date;
|
|
83
|
+
let endDate: Date;
|
|
84
|
+
|
|
85
|
+
switch (input.type) {
|
|
86
|
+
case "today":
|
|
87
|
+
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
88
|
+
endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
89
|
+
break;
|
|
90
|
+
case "yesterday": {
|
|
91
|
+
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
|
92
|
+
startDate = yesterday;
|
|
93
|
+
endDate = yesterday;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case "week":
|
|
97
|
+
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
|
|
98
|
+
endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
99
|
+
break;
|
|
100
|
+
case "date":
|
|
101
|
+
if (!input.date) {
|
|
102
|
+
throw new Error("Validation Failed: 'date' field is required for type 'date'");
|
|
103
|
+
}
|
|
104
|
+
startDate = new Date(input.date);
|
|
105
|
+
endDate = new Date(input.date);
|
|
106
|
+
break;
|
|
107
|
+
case "range":
|
|
108
|
+
if (!input.startDate || !input.endDate) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"Validation Failed: 'startDate' and 'endDate' are required for type 'range'"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
startDate = new Date(input.startDate);
|
|
114
|
+
endDate = new Date(input.endDate);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const entries = await diaryRepository.getByDateRange(startDate, endDate);
|
|
119
|
+
|
|
120
|
+
return JSON.stringify({
|
|
121
|
+
success: true,
|
|
122
|
+
query: {
|
|
123
|
+
type: input.type,
|
|
124
|
+
startDate: startDate.toISOString().split("T")[0],
|
|
125
|
+
endDate: endDate.toISOString().split("T")[0],
|
|
126
|
+
},
|
|
127
|
+
entries: entries.map((e) => ({
|
|
128
|
+
id: e.id,
|
|
129
|
+
date: e.entryDate.toISOString().split("T")[0],
|
|
130
|
+
summary: e.summary,
|
|
131
|
+
mood: e.mood,
|
|
132
|
+
moodScore: e.moodScore,
|
|
133
|
+
therapeuticAdvice: e.therapeuticAdvice,
|
|
134
|
+
messageCount: e.messageCount,
|
|
135
|
+
})),
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const getMoodTrendExecutor: ToolExecutor = async (args) => {
|
|
140
|
+
const result = GetMoodTrendSchema.safeParse(args);
|
|
141
|
+
if (!result.success) {
|
|
142
|
+
throw new Error(`Validation Failed: ${result.error.message}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const input = result.data;
|
|
146
|
+
const days = input.days ?? 30;
|
|
147
|
+
|
|
148
|
+
const trend = await diaryRepository.getMoodTrend(days);
|
|
149
|
+
|
|
150
|
+
// Calculate average mood score
|
|
151
|
+
const avgMoodScore =
|
|
152
|
+
trend.length > 0 ? trend.reduce((sum, t) => sum + t.moodScore, 0) / trend.length : null;
|
|
153
|
+
|
|
154
|
+
// Find most common mood
|
|
155
|
+
const moodCounts = trend.reduce(
|
|
156
|
+
(acc, t) => {
|
|
157
|
+
acc[t.mood] = (acc[t.mood] || 0) + 1;
|
|
158
|
+
return acc;
|
|
159
|
+
},
|
|
160
|
+
{} as Record<string, number>
|
|
161
|
+
);
|
|
162
|
+
const dominantMood = Object.entries(moodCounts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
163
|
+
|
|
164
|
+
return JSON.stringify({
|
|
165
|
+
success: true,
|
|
166
|
+
days,
|
|
167
|
+
entryCount: trend.length,
|
|
168
|
+
averageMoodScore: avgMoodScore ? Math.round(avgMoodScore * 10) / 10 : null,
|
|
169
|
+
dominantMood,
|
|
170
|
+
trend: trend.map((t) => ({
|
|
171
|
+
date: t.date.toISOString().split("T")[0],
|
|
172
|
+
mood: t.mood,
|
|
173
|
+
moodScore: t.moodScore,
|
|
174
|
+
})),
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
query_diary: queryDiaryExecutor,
|
|
180
|
+
get_mood_trend: getMoodTrendExecutor,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ToolRegistry, ToolExecutor } from "../graph/nodes";
|
|
2
|
+
import type { IEventsRepository } from "../../domain/interfaces/IEventsRepository";
|
|
3
|
+
import type { IDiaryRepository } from "../../domain/interfaces/IDiaryRepository";
|
|
4
|
+
import {
|
|
5
|
+
createCalendarToolExecutors,
|
|
6
|
+
calendarToolDefinitions,
|
|
7
|
+
AddEventSchema,
|
|
8
|
+
QueryEventsSchema,
|
|
9
|
+
type AddEventInput,
|
|
10
|
+
type QueryEventsInput,
|
|
11
|
+
} from "./calendarTool";
|
|
12
|
+
import {
|
|
13
|
+
createDiaryToolExecutors,
|
|
14
|
+
diaryToolDefinitions,
|
|
15
|
+
QueryDiarySchema,
|
|
16
|
+
GetMoodTrendSchema,
|
|
17
|
+
type QueryDiaryInput,
|
|
18
|
+
type GetMoodTrendInput,
|
|
19
|
+
} from "./diaryTool";
|
|
20
|
+
|
|
21
|
+
export interface ToolRegistryDeps {
|
|
22
|
+
eventsRepository: IEventsRepository;
|
|
23
|
+
diaryRepository?: IDiaryRepository;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createToolRegistry(deps: ToolRegistryDeps): ToolRegistry {
|
|
27
|
+
const registry: ToolRegistry = new Map();
|
|
28
|
+
|
|
29
|
+
// Register calendar tools with database repository
|
|
30
|
+
const calendarExecutors = createCalendarToolExecutors(deps.eventsRepository);
|
|
31
|
+
for (const [name, executor] of Object.entries(calendarExecutors)) {
|
|
32
|
+
registry.set(name, executor);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Register diary tools if repository provided
|
|
36
|
+
if (deps.diaryRepository) {
|
|
37
|
+
const diaryExecutors = createDiaryToolExecutors(deps.diaryRepository);
|
|
38
|
+
for (const [name, executor] of Object.entries(diaryExecutors)) {
|
|
39
|
+
registry.set(name, executor);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return registry;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Re-export calendar tool components
|
|
47
|
+
export {
|
|
48
|
+
createCalendarToolExecutors,
|
|
49
|
+
calendarToolDefinitions,
|
|
50
|
+
AddEventSchema,
|
|
51
|
+
QueryEventsSchema,
|
|
52
|
+
type AddEventInput,
|
|
53
|
+
type QueryEventsInput,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Re-export diary tool components
|
|
57
|
+
export {
|
|
58
|
+
createDiaryToolExecutors,
|
|
59
|
+
diaryToolDefinitions,
|
|
60
|
+
QueryDiarySchema,
|
|
61
|
+
GetMoodTrendSchema,
|
|
62
|
+
type QueryDiaryInput,
|
|
63
|
+
type GetMoodTrendInput,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export function registerTool(registry: ToolRegistry, name: string, executor: ToolExecutor): void {
|
|
67
|
+
registry.set(name, executor);
|
|
68
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
|
|
4
|
+
const envSchema = z.object({
|
|
5
|
+
// LLM Providers
|
|
6
|
+
OPENAI_API_KEY: z.string().optional(),
|
|
7
|
+
GEMINI_API_KEY: z.string().optional(),
|
|
8
|
+
OLLAMA_BASE_URL: z.string().url().default("http://localhost:11434"),
|
|
9
|
+
|
|
10
|
+
// Default provider
|
|
11
|
+
DEFAULT_PROVIDER: z.enum(["openai", "gemini", "ollama"]).default("ollama"),
|
|
12
|
+
DEFAULT_MODEL: z.string().optional(),
|
|
13
|
+
|
|
14
|
+
// App settings
|
|
15
|
+
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
|
16
|
+
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type Env = z.infer<typeof envSchema>;
|
|
20
|
+
|
|
21
|
+
function validateEnv(): Env {
|
|
22
|
+
const result = envSchema.safeParse(process.env);
|
|
23
|
+
|
|
24
|
+
if (!result.success) {
|
|
25
|
+
console.error("Environment validation failed:");
|
|
26
|
+
console.error(result.error.format());
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result.data;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const env = validateEnv();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type Env, env } from "./env";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const DiaryEntrySchema = z.object({
|
|
4
|
+
id: z.string().uuid(),
|
|
5
|
+
entryDate: z.coerce.date(),
|
|
6
|
+
summary: z.string(),
|
|
7
|
+
mood: z.string(),
|
|
8
|
+
moodScore: z.number().min(1).max(10),
|
|
9
|
+
therapeuticAdvice: z.string(),
|
|
10
|
+
sessionIds: z.array(z.string()).default([]),
|
|
11
|
+
messageCount: z.number().default(0),
|
|
12
|
+
createdAt: z.coerce.date(),
|
|
13
|
+
updatedAt: z.coerce.date(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type DiaryEntry = z.infer<typeof DiaryEntrySchema>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const EventSchema = z.object({
|
|
4
|
+
id: z.string().uuid(),
|
|
5
|
+
title: z.string().min(1),
|
|
6
|
+
datetime: z.coerce.date(),
|
|
7
|
+
endTime: z.coerce.date().optional(),
|
|
8
|
+
notes: z.string().optional(),
|
|
9
|
+
tags: z.array(z.string()).default([]),
|
|
10
|
+
createdAt: z.coerce.date().default(() => new Date()),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export type Event = z.infer<typeof EventSchema>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const MemorySchema = z.object({
|
|
4
|
+
id: z.string().uuid(),
|
|
5
|
+
type: z.enum([
|
|
6
|
+
"fact",
|
|
7
|
+
"preference",
|
|
8
|
+
"routine",
|
|
9
|
+
"relationship",
|
|
10
|
+
"communication_style",
|
|
11
|
+
"interest",
|
|
12
|
+
]),
|
|
13
|
+
content: z.string(),
|
|
14
|
+
source: z.string().optional(),
|
|
15
|
+
importance: z.number().min(1).max(10).default(5),
|
|
16
|
+
lastAccessed: z.coerce.date(),
|
|
17
|
+
createdAt: z.coerce.date(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type Memory = z.infer<typeof MemorySchema>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DiaryEntry } from "../entities/DiaryEntry";
|
|
2
|
+
|
|
3
|
+
export interface MoodTrendEntry {
|
|
4
|
+
date: Date;
|
|
5
|
+
mood: string;
|
|
6
|
+
moodScore: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface IDiaryRepository {
|
|
10
|
+
create(entry: DiaryEntry): Promise<DiaryEntry>;
|
|
11
|
+
getById(id: string): Promise<DiaryEntry | null>;
|
|
12
|
+
getByDate(date: Date): Promise<DiaryEntry | null>;
|
|
13
|
+
getByDateRange(startDate: Date, endDate: Date): Promise<DiaryEntry[]>;
|
|
14
|
+
getRecent(limit?: number): Promise<DiaryEntry[]>;
|
|
15
|
+
update(
|
|
16
|
+
id: string,
|
|
17
|
+
updates: Partial<Omit<DiaryEntry, "id" | "createdAt">>
|
|
18
|
+
): Promise<DiaryEntry | null>;
|
|
19
|
+
delete(id: string): Promise<boolean>;
|
|
20
|
+
getMoodTrend(days?: number): Promise<MoodTrendEntry[]>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Event } from "../entities/Event";
|
|
2
|
+
|
|
3
|
+
export interface IEventsRepository {
|
|
4
|
+
create(event: Event): Promise<Event>;
|
|
5
|
+
getById(id: string): Promise<Event | null>;
|
|
6
|
+
getByDateRange(startDate: Date, endDate: Date): Promise<Event[]>;
|
|
7
|
+
getToday(): Promise<Event[]>;
|
|
8
|
+
getUpcoming(limit?: number): Promise<Event[]>;
|
|
9
|
+
update(id: string, updates: Partial<Omit<Event, "id" | "createdAt">>): Promise<Event | null>;
|
|
10
|
+
delete(id: string): Promise<boolean>;
|
|
11
|
+
searchByTitle(query: string): Promise<Event[]>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
|
|
3
|
+
export interface ToolDefinition {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ToolCall {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
arguments: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LLMResponse {
|
|
16
|
+
content: string;
|
|
17
|
+
toolCalls?: ToolCall[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ILanguageModel {
|
|
21
|
+
generate(messages: BaseMessage[], tools?: ToolDefinition[]): Promise<LLMResponse>;
|
|
22
|
+
withStructuredOutput<_T>(schema: unknown): ILanguageModel;
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Memory } from "../entities/Memory";
|
|
2
|
+
|
|
3
|
+
export type EmbeddingFunction = (text: string) => Promise<number[]>;
|
|
4
|
+
|
|
5
|
+
export interface IMemoriesRepository {
|
|
6
|
+
create(memory: Memory, embedding?: number[]): Promise<Memory>;
|
|
7
|
+
getById(id: string): Promise<Memory | null>;
|
|
8
|
+
getByType(type: Memory["type"]): Promise<Memory[]>;
|
|
9
|
+
getAll(limit?: number): Promise<Memory[]>;
|
|
10
|
+
semanticSearch(embedding: number[], limit?: number, threshold?: number): Promise<Memory[]>;
|
|
11
|
+
update(id: string, updates: Partial<Omit<Memory, "id" | "createdAt">>): Promise<Memory | null>;
|
|
12
|
+
delete(id: string): Promise<boolean>;
|
|
13
|
+
updateLastAccessed(id: string): Promise<void>;
|
|
14
|
+
getRecent(limit?: number): Promise<Memory[]>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { GraphStateType } from "../state/AgentState";
|
|
2
|
+
|
|
3
|
+
export interface CheckpointMetadata {
|
|
4
|
+
createdAt: Date;
|
|
5
|
+
updatedAt: Date;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ICheckpointer {
|
|
9
|
+
save(sessionId: string, state: GraphStateType): Promise<void>;
|
|
10
|
+
load(sessionId: string): Promise<GraphStateType | null>;
|
|
11
|
+
list(): Promise<string[]>;
|
|
12
|
+
getMetadata?(sessionId: string): Promise<CheckpointMetadata | null>;
|
|
13
|
+
getSessionsForDate?(date: Date): Promise<string[]>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IVectorStore {
|
|
17
|
+
addDocuments(docs: { content: string; metadata?: Record<string, unknown> }[]): Promise<void>;
|
|
18
|
+
search(query: string, limit?: number): Promise<{ content: string; score: number }[]>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
import { Annotation } from "@langchain/langgraph";
|
|
3
|
+
import type { Event } from "../entities/Event";
|
|
4
|
+
import type { Memory } from "../entities/Memory";
|
|
5
|
+
|
|
6
|
+
export type Intent = "add_event" | "query" | "chat" | null;
|
|
7
|
+
|
|
8
|
+
export const GraphState = Annotation.Root({
|
|
9
|
+
messages: Annotation<BaseMessage[]>({
|
|
10
|
+
reducer: (prev, next) => [...prev, ...next],
|
|
11
|
+
default: () => [],
|
|
12
|
+
}),
|
|
13
|
+
intent: Annotation<Intent>(),
|
|
14
|
+
pendingEvent: Annotation<Event | null>(),
|
|
15
|
+
queryResult: Annotation<string | null>(),
|
|
16
|
+
relevantMemories: Annotation<Memory[]>({
|
|
17
|
+
reducer: (_, next) => next,
|
|
18
|
+
default: () => [],
|
|
19
|
+
}),
|
|
20
|
+
todayEvents: Annotation<Event[]>({
|
|
21
|
+
reducer: (_, next) => next,
|
|
22
|
+
default: () => [],
|
|
23
|
+
}),
|
|
24
|
+
response: Annotation<string>({
|
|
25
|
+
reducer: (_, next) => next,
|
|
26
|
+
default: () => "",
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export type GraphStateType = typeof GraphState.State;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { IEventsRepository } from "../../domain/interfaces/IEventsRepository";
|
|
3
|
+
import type { IMemoriesRepository } from "../../domain/interfaces/IMemoriesRepository";
|
|
4
|
+
import type { IDiaryRepository } from "../../domain/interfaces/IDiaryRepository";
|
|
5
|
+
import type { ICheckpointer } from "../../domain/interfaces/IMemory";
|
|
6
|
+
|
|
7
|
+
import { SqliteCheckpointer } from "./sqlite-checkpointer";
|
|
8
|
+
import { SqliteEventsRepository } from "./sqlite-events-repository";
|
|
9
|
+
import { SqliteMemoriesRepository } from "./sqlite-memories-repository";
|
|
10
|
+
import { SqliteDiaryRepository } from "./sqlite-diary-repository";
|
|
11
|
+
|
|
12
|
+
import { createSqliteClient } from "./sqlite-client";
|
|
13
|
+
import { initializeSqliteSchema } from "./sqlite-schema";
|
|
14
|
+
|
|
15
|
+
export interface RepositoryBundle {
|
|
16
|
+
checkpointer: ICheckpointer;
|
|
17
|
+
events: IEventsRepository;
|
|
18
|
+
memories: IMemoriesRepository & {
|
|
19
|
+
setEmbeddingFunction?: (fn: (text: string) => Promise<number[]>) => void;
|
|
20
|
+
};
|
|
21
|
+
diary: IDiaryRepository;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates the complete repository bundle using SQLite backend.
|
|
26
|
+
*/
|
|
27
|
+
export function createRepositories(client: Database): RepositoryBundle {
|
|
28
|
+
const memories = new SqliteMemoriesRepository(client);
|
|
29
|
+
return {
|
|
30
|
+
checkpointer: new SqliteCheckpointer(client),
|
|
31
|
+
events: new SqliteEventsRepository(client),
|
|
32
|
+
memories,
|
|
33
|
+
diary: new SqliteDiaryRepository(client),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create SQLite backend client with schema initialized.
|
|
39
|
+
*/
|
|
40
|
+
export function createBackend(): Database {
|
|
41
|
+
const client = createSqliteClient();
|
|
42
|
+
initializeSqliteSchema(client);
|
|
43
|
+
return client;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Convenience function to create repositories with auto-initialized SQLite.
|
|
48
|
+
*/
|
|
49
|
+
export function createRepositoriesFromEnv(): RepositoryBundle {
|
|
50
|
+
const client = createBackend();
|
|
51
|
+
return createRepositories(client);
|
|
52
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Re-export interfaces from domain
|
|
2
|
+
export type { IEventsRepository } from "../../domain/interfaces/IEventsRepository";
|
|
3
|
+
export type {
|
|
4
|
+
EmbeddingFunction,
|
|
5
|
+
IMemoriesRepository,
|
|
6
|
+
} from "../../domain/interfaces/IMemoriesRepository";
|
|
7
|
+
export type { ICheckpointer } from "../../domain/interfaces/IMemory";
|
|
8
|
+
|
|
9
|
+
// Factory with auto-detection
|
|
10
|
+
export type { RepositoryBundle } from "./factory";
|
|
11
|
+
export { createBackend, createRepositories, createRepositoriesFromEnv } from "./factory";
|
|
12
|
+
|
|
13
|
+
// SQLite implementations
|
|
14
|
+
export { SqliteCheckpointer } from "./sqlite-checkpointer";
|
|
15
|
+
export { SqliteEventsRepository } from "./sqlite-events-repository";
|
|
16
|
+
export { SqliteMemoriesRepository } from "./sqlite-memories-repository";
|
|
17
|
+
export { SqliteDiaryRepository } from "./sqlite-diary-repository";
|
|
18
|
+
|
|
19
|
+
// SQLite client
|
|
20
|
+
export { createSqliteClient, getDefaultDbPath } from "./sqlite-client";
|
|
21
|
+
export { initializeSqliteSchema } from "./sqlite-schema";
|