feat: add heartbeat monitor and vector memory search (Tier 2)
Heartbeat: - HeartbeatMonitor with 5 checks: gateway, model, channels, memory, disk - Configurable interval, failure threshold, notification channel - Recovery notifications when health restores - 25 new tests Vector Memory Search: - EmbeddingProvider interface with OpenAI, Gemini, Ollama, LlamaCpp backends - SQLite-backed VectorStore with cosine similarity search - Text chunker with paragraph-aware splitting and overlap - HybridSearch merging keyword + vector results with configurable weight - Background indexer with dirty-namespace tracking - Graceful fallback to keyword search when embeddings unavailable - 51 new tests Config: automation.heartbeat + memory.embedding schema sections Total: 950 tests passing, all types clean
This commit is contained in:
@@ -119,9 +119,24 @@ const webhookSchema = z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
});
|
||||
|
||||
const heartbeatCheckSchema = z.enum(['gateway', 'model', 'channels', 'memory', 'disk']);
|
||||
|
||||
const heartbeatSchema = z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
interval: z.string().default('5m'),
|
||||
checks: z.array(heartbeatCheckSchema).default(['gateway', 'model', 'channels', 'memory', 'disk']),
|
||||
notify: z.object({
|
||||
channel: z.string().min(1),
|
||||
peer: z.string().min(1),
|
||||
}).optional(),
|
||||
failure_threshold: z.number().min(1).max(10).default(2),
|
||||
disk_threshold_mb: z.number().min(10).default(100),
|
||||
}).default({});
|
||||
|
||||
const automationSchema = z.object({
|
||||
cron: z.array(cronJobSchema).default([]),
|
||||
webhooks: z.array(webhookSchema).default([]),
|
||||
heartbeat: heartbeatSchema,
|
||||
}).default({});
|
||||
|
||||
const agentsSchema = z.object({
|
||||
@@ -143,11 +158,27 @@ const agentsSchema = z.object({
|
||||
max_delegation_depth: z.number().min(1).max(10).default(3),
|
||||
}).default({});
|
||||
|
||||
const embeddingProviderSchema = z.enum(['openai', 'gemini', 'ollama', 'llamacpp']);
|
||||
|
||||
const embeddingSchema = z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
provider: embeddingProviderSchema.default('openai'),
|
||||
model: z.string().default('text-embedding-3-small'),
|
||||
endpoint: z.string().optional(),
|
||||
api_key: z.string().optional(),
|
||||
dimensions: z.number().optional(),
|
||||
chunk_size: z.number().min(64).max(8192).default(512),
|
||||
chunk_overlap: z.number().min(0).max(1024).default(50),
|
||||
top_k: z.number().min(1).max(50).default(5),
|
||||
hybrid_weight: z.number().min(0).max(1).default(0.7),
|
||||
}).default({});
|
||||
|
||||
const memorySchema = z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
dir: z.string().optional(), // Default: ~/.local/share/flynn/memory
|
||||
auto_extract: z.boolean().default(true),
|
||||
max_context_tokens: z.number().min(100).max(10000).default(2000),
|
||||
embedding: embeddingSchema,
|
||||
}).default({});
|
||||
|
||||
const compactionSchema = z.object({
|
||||
@@ -333,3 +364,7 @@ export type RoutingConfig = z.infer<typeof routingSchema>;
|
||||
export type ServerConfig = z.infer<typeof serverSchema>;
|
||||
export type SessionsConfig = z.infer<typeof sessionsSchema>;
|
||||
export type ThinkingConfig = z.infer<typeof thinkingSchema>;
|
||||
export type HeartbeatConfig = z.infer<typeof heartbeatSchema>;
|
||||
export type HeartbeatCheck = z.infer<typeof heartbeatCheckSchema>;
|
||||
export type EmbeddingConfig = z.infer<typeof embeddingSchema>;
|
||||
export type EmbeddingProvider = z.infer<typeof embeddingProviderSchema>;
|
||||
|
||||
Reference in New Issue
Block a user