Initial commit: RA-H Open Source Edition
Local-first knowledge management system with BYO API keys. Features: - 3-panel UI (Nodes | Focus | Helpers) - SQLite + sqlite-vec for vector search - Agent system (Easy/Hard mode orchestrators) - Content extraction (YouTube, PDF, web) - Integrate workflow for connection discovery - Dimension system with auto-assignment Tech stack: - Next.js 15 + TypeScript + Tailwind CSS - Anthropic (Claude) + OpenAI (GPT) via Vercel AI SDK Setup: npm install && npm rebuild better-sqlite3 scripts/dev/bootstrap-local.sh npm run dev MIT License
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
export interface UsageData {
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
totalTokens: number;
|
||||
cacheWriteTokens?: number;
|
||||
cacheReadTokens?: number;
|
||||
cacheHit?: boolean;
|
||||
cacheSavingsPct?: number;
|
||||
estimatedCostUsd: number;
|
||||
modelUsed: string;
|
||||
provider: 'anthropic' | 'openai';
|
||||
toolsUsed?: string[];
|
||||
toolCallsCount?: number;
|
||||
traceId?: string;
|
||||
parentChatId?: number;
|
||||
workflowKey?: string;
|
||||
workflowNodeId?: number;
|
||||
capsuleVersion?: number;
|
||||
contextSourcesUsed?: number[];
|
||||
validationStatus?: 'ok' | 'failed';
|
||||
validationMessage?: string;
|
||||
fallbackAction?: string;
|
||||
mode?: 'easy' | 'hard';
|
||||
}
|
||||
|
||||
export interface EnhancedChatMetadata {
|
||||
timestamp: string;
|
||||
session_id: string;
|
||||
current_view: 'nodes' | 'memory';
|
||||
open_tab_count: number;
|
||||
has_focused_node: boolean;
|
||||
message_count: number;
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
total_tokens?: number;
|
||||
cache_write_tokens?: number;
|
||||
cache_read_tokens?: number;
|
||||
cache_hit?: boolean;
|
||||
cache_savings_pct?: number;
|
||||
estimated_cost_usd?: number;
|
||||
model_used?: string;
|
||||
provider?: 'anthropic' | 'openai';
|
||||
tools_used?: string[];
|
||||
tool_calls_count?: number;
|
||||
trace_id?: string;
|
||||
parent_chat_id?: number;
|
||||
voice_tts_chars?: number;
|
||||
voice_tts_cost_usd?: number;
|
||||
voice_tts_chars_total?: number;
|
||||
voice_tts_cost_usd_total?: number;
|
||||
voice_request_id?: string;
|
||||
voice_tts_request_count?: number;
|
||||
voice_usage?: Array<{
|
||||
request_id: string;
|
||||
message_id?: string | null;
|
||||
chars: number;
|
||||
cost_usd: number;
|
||||
voice?: string;
|
||||
model?: string;
|
||||
duration_ms?: number | null;
|
||||
logged_at?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface CostReport {
|
||||
periodStart: string;
|
||||
periodEnd: string;
|
||||
totalCostUsd: number;
|
||||
totalChats: number;
|
||||
totalTokens: number;
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
cacheReadTokens: number;
|
||||
cacheWriteTokens: number;
|
||||
cacheHitRate: number;
|
||||
cacheSavingsUsd: number;
|
||||
avgCostPerChat: number;
|
||||
avgTokensPerChat: number;
|
||||
costByAgent: {
|
||||
[agentName: string]: {
|
||||
costUsd: number;
|
||||
chats: number;
|
||||
tokens: number;
|
||||
};
|
||||
};
|
||||
costByModel: {
|
||||
[modelId: string]: {
|
||||
costUsd: number;
|
||||
chats: number;
|
||||
tokens: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface TraceCostSummary {
|
||||
traceId: string;
|
||||
totalCostUsd: number;
|
||||
chatCount: number;
|
||||
orchestratorCost: number;
|
||||
executorCost: number;
|
||||
plannerCost: number;
|
||||
totalTokens: number;
|
||||
interactions: Array<{
|
||||
chatId: number;
|
||||
agentName: string;
|
||||
costUsd: number;
|
||||
tokens: number;
|
||||
createdAt: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ModelPricing {
|
||||
provider: 'anthropic' | 'openai';
|
||||
inputPer1M: number;
|
||||
outputPer1M: number;
|
||||
cacheWritePer1M?: number;
|
||||
cacheReadPer1M?: number;
|
||||
}
|
||||
|
||||
export interface CacheEffectiveness {
|
||||
totalRequests: number;
|
||||
cacheHits: number;
|
||||
cacheMisses: number;
|
||||
hitRate: number;
|
||||
totalCacheSavingsUsd: number;
|
||||
avgSavingsPerHit: number;
|
||||
totalTokensSaved: number;
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// New Node-based type system replacing rigid Item categorization
|
||||
export interface Node {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
content?: string; // Consolidated content from description + abstract + notes
|
||||
link?: string;
|
||||
type?: string;
|
||||
dimensions: string[]; // Flexible dimensions replacing type + stage + segment + tags
|
||||
embedding?: Buffer; // Node-level embedding (BLOB data)
|
||||
chunk?: string;
|
||||
metadata?: any; // Flexible metadata storage from extras + chunk_status + sub_type
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
is_pinned?: number; // Legacy pin flag (read-only, slated for removal)
|
||||
edge_count?: number; // Derived count of edges, included in some queries
|
||||
|
||||
// Optional embedding fields (restored from migration)
|
||||
embedding_updated_at?: string;
|
||||
embedding_text?: string;
|
||||
chunk_status?: 'not_chunked' | 'chunking' | 'chunked' | 'error' | null;
|
||||
}
|
||||
|
||||
// Legacy Item interface - DEPRECATED, use Node instead
|
||||
// Kept temporarily for migration compatibility
|
||||
export interface Item extends Node {
|
||||
// Legacy fields for backwards compatibility during transition
|
||||
description?: string;
|
||||
abstract?: string;
|
||||
type?: string;
|
||||
legacyType?: string[];
|
||||
stage?: string;
|
||||
segment?: string[];
|
||||
tags?: string[];
|
||||
sub_type?: any;
|
||||
notes?: any;
|
||||
extras?: any;
|
||||
score?: number;
|
||||
content_embedding?: number[];
|
||||
embedding_updated_at?: string;
|
||||
chunk_status?: 'not_chunked' | 'chunking' | 'chunked' | 'error';
|
||||
chunk_updated_at?: string;
|
||||
}
|
||||
|
||||
export interface Chunk {
|
||||
id: number;
|
||||
node_id: number; // Updated from item_id to node_id
|
||||
chunk_idx?: number;
|
||||
text: string;
|
||||
embedding?: number[];
|
||||
embedding_type: string;
|
||||
metadata?: any; // Updated from extras to metadata
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface Edge {
|
||||
id: number;
|
||||
from_node_id: number;
|
||||
to_node_id: number;
|
||||
context?: any;
|
||||
source: 'user' | 'ai_similarity' | 'helper_name';
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface Chat {
|
||||
id: number;
|
||||
user_message?: string;
|
||||
assistant_message?: string;
|
||||
thread_id: string;
|
||||
focused_node_id?: number; // Updated from focused_item_id
|
||||
metadata?: any;
|
||||
embedding?: number[]; // Renamed from content_embedding
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface SessionContext {
|
||||
id: number;
|
||||
session_id: string;
|
||||
focused_node_id: number; // Updated from focused_item_id
|
||||
context_data: any;
|
||||
created_at: string;
|
||||
last_accessed: string;
|
||||
expires_at: string;
|
||||
}
|
||||
|
||||
export interface SessionCache {
|
||||
id: number;
|
||||
session_id: string;
|
||||
cache_key: string;
|
||||
cache_data: any;
|
||||
expires_at: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// New NodeFilters interface replacing rigid ItemFilters
|
||||
export interface NodeFilters {
|
||||
dimensions?: string[]; // Filter by dimensions (replaces stage/type filtering)
|
||||
search?: string; // Text search in title/content
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
sortBy?: 'updated' | 'edges'; // Sort by updated_at or edge count
|
||||
}
|
||||
|
||||
// Legacy filters - DEPRECATED, use NodeFilters instead
|
||||
export interface ItemFilters extends NodeFilters {
|
||||
stage?: string;
|
||||
type?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface ChunkData {
|
||||
node_id: number; // Updated from item_id
|
||||
chunk_idx?: number;
|
||||
text: string;
|
||||
embedding?: number[];
|
||||
embedding_type: string;
|
||||
metadata?: any; // Updated from extras
|
||||
}
|
||||
|
||||
export interface EdgeData {
|
||||
from_node_id: number;
|
||||
to_node_id: number;
|
||||
context?: any;
|
||||
source: 'user' | 'ai_similarity' | 'helper_name';
|
||||
}
|
||||
|
||||
export interface ChatData {
|
||||
user_message?: string;
|
||||
assistant_message?: string;
|
||||
thread_id: string;
|
||||
focused_node_id?: number; // Updated from focused_item_id
|
||||
metadata?: any;
|
||||
embedding?: number[]; // Renamed from content_embedding
|
||||
}
|
||||
|
||||
export interface CachedContext {
|
||||
sessionId: string;
|
||||
focusedNodeId: number; // Updated from focusedItemId
|
||||
contextData: any;
|
||||
expiresAt: string;
|
||||
}
|
||||
|
||||
// New NodeConnection interface
|
||||
export interface NodeConnection {
|
||||
id: number;
|
||||
connected_node: Node; // Updated from connected_item
|
||||
edge: Edge;
|
||||
}
|
||||
|
||||
// Legacy connection - DEPRECATED, use NodeConnection instead
|
||||
export interface ItemConnection {
|
||||
id: number;
|
||||
connected_item: Item;
|
||||
edge: Edge;
|
||||
}
|
||||
|
||||
export interface DatabaseConfig {
|
||||
host: string;
|
||||
port: number;
|
||||
database: string;
|
||||
user: string;
|
||||
password: string;
|
||||
ssl?: boolean;
|
||||
connectionTimeoutMillis?: number;
|
||||
idleTimeoutMillis?: number;
|
||||
max?: number;
|
||||
}
|
||||
|
||||
export interface DatabaseError {
|
||||
message: string;
|
||||
code?: string;
|
||||
details?: any;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export interface MemoryContext {
|
||||
insights: {
|
||||
user_patterns: string[];
|
||||
preferences: string[];
|
||||
common_queries: string[];
|
||||
knowledge_domains: string[];
|
||||
};
|
||||
context: string;
|
||||
}
|
||||
|
||||
export interface HelperConfig {
|
||||
name: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
system_prompt: string;
|
||||
component_key: string;
|
||||
available_tools: string[];
|
||||
enabled: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
memory?: MemoryContext;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export interface LogEntry {
|
||||
id: number;
|
||||
ts: string;
|
||||
table_name: string;
|
||||
action: string;
|
||||
row_id: number;
|
||||
summary: string | null;
|
||||
snapshot_json: string | null;
|
||||
enriched_summary: string | null;
|
||||
}
|
||||
|
||||
export interface LogsResponse {
|
||||
logs: LogEntry[];
|
||||
page: number;
|
||||
limit: number;
|
||||
threadId?: string;
|
||||
traceId?: string;
|
||||
table?: string;
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
declare module 'pdf-parse' {
|
||||
interface PDFInfo {
|
||||
Title?: string;
|
||||
Author?: string;
|
||||
Creator?: string;
|
||||
Producer?: string;
|
||||
CreationDate?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface PDFData {
|
||||
numpages: number;
|
||||
numrender: number;
|
||||
info: PDFInfo;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
metadata: any;
|
||||
text: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function pdf(dataBuffer: Buffer, options?: any): Promise<PDFData>;
|
||||
|
||||
export = pdf;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Types for Anthropic prompt caching
|
||||
* https://docs.claude.com/en/docs/build-with-claude/prompt-caching
|
||||
*/
|
||||
|
||||
export type CacheControl = { type: 'ephemeral' };
|
||||
|
||||
export interface CacheableBlock {
|
||||
type: 'text';
|
||||
text: string;
|
||||
cache_control?: CacheControl;
|
||||
}
|
||||
|
||||
export interface SystemPromptResult {
|
||||
blocks: CacheableBlock[];
|
||||
cacheHit: boolean;
|
||||
}
|
||||
|
||||
export interface CacheStats {
|
||||
cacheCreationInputTokens: number;
|
||||
cacheReadInputTokens: number;
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
savingsPercentage: number;
|
||||
}
|
||||
Reference in New Issue
Block a user