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:
“BeeRad”
2025-12-15 16:14:28 +11:00
commit 733d1c3407
226 changed files with 46231 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
import { getSQLiteClient } from '@/services/database/sqlite-client';
import { getAutoContextSettings } from '@/services/settings/autoContextSettings';
interface AutoContextRow {
id: number;
title: string | null;
updated_at: string;
edge_count: number | null;
}
export interface AutoContextSummary {
id: number;
title: string;
edgeCount: number;
updatedAt: string;
}
function fetchAutoContextRows(limit: number): AutoContextSummary[] {
const db = getSQLiteClient();
const rows = db
.query<AutoContextRow>(
`
SELECT n.id,
n.title,
n.updated_at,
COUNT(DISTINCT e.id) AS edge_count
FROM nodes n
LEFT JOIN edges e
ON (e.from_node_id = n.id OR e.to_node_id = n.id)
WHERE n.type IS NULL OR n.type != 'memory'
GROUP BY n.id
ORDER BY edge_count DESC, n.updated_at DESC
LIMIT ?
`,
[limit]
)
.rows;
return rows.map((row) => ({
id: row.id,
title: row.title || 'Untitled node',
updatedAt: row.updated_at,
edgeCount: Number(row.edge_count ?? 0),
}));
}
export function getAutoContextSummaries(limit = 10): AutoContextSummary[] {
const settings = getAutoContextSettings();
if (!settings.autoContextEnabled) {
return [];
}
return fetchAutoContextRows(limit);
}
export function buildAutoContextBlock(limit = 10): string | null {
const summaries = getAutoContextSummaries(limit);
if (summaries.length === 0) {
return null;
}
const lines: string[] = [
'=== BACKGROUND CONTEXT ===',
'Top 10 most-connected nodes (important knowledge hubs). Use queryNodes/getNodesById if relevant.',
'',
];
for (const summary of summaries) {
lines.push(`[NODE:${summary.id}:"${summary.title}"] (edges: ${summary.edgeCount})`);
}
return lines.join('\n');
}
+37
View File
@@ -0,0 +1,37 @@
import type { Node } from '@/types/database';
interface RequestContext {
traceId?: string;
parentChatId?: number;
workflowKey?: string;
workflowNodeId?: number;
openTabs?: Node[];
activeTabId?: number | null;
mode?: 'easy' | 'hard';
apiKeys?: {
openai?: string;
anthropic?: string;
};
}
let currentContext: RequestContext = {};
export const RequestContext = {
set(context: Partial<RequestContext>) {
Object.entries(context).forEach(([key, value]) => {
if (value === undefined) {
delete (currentContext as Record<string, unknown>)[key];
} else {
(currentContext as Record<string, unknown>)[key] = value;
}
});
},
get(): RequestContext {
return currentContext;
},
clear() {
currentContext = {};
},
};