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
+92
View File
@@ -0,0 +1,92 @@
// Tool group definitions and utilities
export interface ToolGroup {
id: string;
name: string;
description: string;
icon: string;
color: string;
}
export const TOOL_GROUPS: Record<string, ToolGroup> = {
core: {
id: 'core',
name: 'Core Graph',
description: 'Read-only knowledge graph queries and search (all agents)',
icon: '●',
color: '#3b82f6'
},
orchestration: {
id: 'orchestration',
name: 'Orchestration',
description: 'Workflows, web search, and reasoning (orchestrator only)',
icon: '●',
color: '#8b5cf6'
},
execution: {
id: 'execution',
name: 'Execution',
description: 'Write operations, extraction, and embeddings (workers only)',
icon: '●',
color: '#10b981'
}
};
// Tool group assignments
export const TOOL_GROUP_ASSIGNMENTS: Record<string, string> = {
// Core: Read-only graph operations (all agents)
queryNodes: 'core',
getNodesById: 'core',
queryEdge: 'core',
searchContentEmbeddings: 'core',
// Orchestration: Delegation and reasoning (orchestrator only)
webSearch: 'orchestration',
think: 'orchestration',
delegateToMiniRAH: 'orchestration',
delegateNodeQuotes: 'orchestration',
delegateNodeComparison: 'orchestration',
delegateToWiseRAH: 'orchestration',
executeWorkflow: 'orchestration',
// Execution: Write operations and extraction (workers only)
createNode: 'execution',
updateNode: 'execution',
createEdge: 'execution',
updateEdge: 'execution',
embedContent: 'execution',
youtubeExtract: 'execution',
websiteExtract: 'execution',
paperExtract: 'execution'
};
/**
* Get tool group by tool ID
*/
export function getToolGroup(toolId: string): ToolGroup | null {
const groupId = TOOL_GROUP_ASSIGNMENTS[toolId];
return groupId ? TOOL_GROUPS[groupId] : null;
}
/**
* Group tools by their assigned groups
*/
export function groupTools(toolIds: string[]): Record<string, string[]> {
const grouped: Record<string, string[]> = {};
toolIds.forEach(toolId => {
const groupId = TOOL_GROUP_ASSIGNMENTS[toolId] || 'other';
if (!grouped[groupId]) {
grouped[groupId] = [];
}
grouped[groupId].push(toolId);
});
return grouped;
}
/**
* Get all available tools organized by groups
*/
export function getAllToolsByGroup(): Record<string, string[]> {
return groupTools(Object.keys(TOOL_GROUP_ASSIGNMENTS));
}
+7
View File
@@ -0,0 +1,7 @@
// Tool registry exports - main interface for the tool system
export { TOOLS, getTool, getTools, getAllTools, getToolSchemas, executeTool, getHelperTools, getToolGroup, groupTools, getAllToolsByGroup } from './registry';
// Tool types
export type { Tool, ToolContext, ToolSchema } from './types';
export type { ToolGroup } from './groups';
export { TOOL_GROUPS } from './groups';
+53
View File
@@ -0,0 +1,53 @@
/**
* Node Formatter Utility
* Wraps node data in special markers for UI rendering as labels
* Format: [NODE:id:"title"]
*/
export interface NodeData {
id: number;
title: string;
dimensions?: string[];
}
/**
* Formats a node for display in chat streams
* @param node - Node data to format
* @returns Formatted string with node markers
*/
export function formatNodeForChat(node: NodeData): string {
return `[NODE:${node.id}:"${node.title}"]`;
}
/**
* Formats multiple nodes for display
* @param nodes - Array of nodes to format
* @returns Formatted string with each node on a new line
*/
export function formatNodesForChat(nodes: NodeData[]): string {
return nodes.map(formatNodeForChat).join('\n');
}
/**
* Extracts node data from a formatted string
* Used by UI components to parse node markers
* @param text - Text potentially containing node markers
* @returns Array of parsed node data
*/
export function parseNodeMarkers(text: string): Array<NodeData & { raw: string }> {
const regex = /\[NODE:\s*(\d+)\s*:\s*["“”']([^"“”']+)["“”']\s*\]/g;
const nodes: Array<NodeData & { raw: string }> = [];
let match;
while ((match = regex.exec(text)) !== null) {
const [raw, id, title] = match;
nodes.push({
raw,
id: parseInt(id, 10),
title,
dimensions: []
});
}
return nodes;
}
+203
View File
@@ -0,0 +1,203 @@
import { getToolGroup, groupTools, getAllToolsByGroup } from './groups';
import { queryNodesTool } from '../database/queryNodes';
import { getNodesByIdTool } from '../database/getNodesById';
import { createNodeTool } from '../database/createNode';
import { updateNodeTool } from '../database/updateNode';
import { createEdgeTool } from '../database/createEdge';
import { queryEdgeTool } from '../database/queryEdge';
import { updateEdgeTool } from '../database/updateEdge';
import { createDimensionTool } from '../database/createDimension';
import { updateDimensionTool } from '../database/updateDimension';
import { lockDimensionTool } from '../database/lockDimension';
import { unlockDimensionTool } from '../database/unlockDimension';
import { deleteDimensionTool } from '../database/deleteDimension';
import { searchContentEmbeddingsTool } from '../other/searchContentEmbeddings';
import { webSearchTool } from '../other/webSearch';
import { thinkTool } from '../other/think';
import { delegateToMiniRAHTool } from '../orchestration/delegateToMiniRAH';
import { delegateNodeQuotesTool, delegateNodeComparisonTool } from '../orchestration/delegationHelpers';
import { delegateToWiseRAHTool } from '../orchestration/delegateToWiseRAH';
import { executeWorkflowTool } from '../orchestration/executeWorkflow';
import { youtubeExtractTool } from '../other/youtubeExtract';
import { websiteExtractTool } from '../other/websiteExtract';
import { paperExtractTool } from '../other/paperExtract';
// Core tools available to all agents (read-only graph operations)
const CORE_TOOLS: Record<string, any> = {
queryNodes: queryNodesTool,
getNodesById: getNodesByIdTool,
queryEdge: queryEdgeTool,
searchContentEmbeddings: searchContentEmbeddingsTool,
};
const ORCHESTRATION_TOOLS: Record<string, any> = {
webSearch: webSearchTool,
think: thinkTool,
delegateToMiniRAH: delegateToMiniRAHTool,
delegateNodeQuotes: delegateNodeQuotesTool,
delegateNodeComparison: delegateNodeComparisonTool,
delegateToWiseRAH: delegateToWiseRAHTool,
executeWorkflow: executeWorkflowTool,
};
// Execution tools for worker agents (includes write operations)
const EXECUTION_TOOLS: Record<string, any> = {
createNode: createNodeTool,
updateNode: updateNodeTool,
createEdge: createEdgeTool,
updateEdge: updateEdgeTool,
createDimension: createDimensionTool,
updateDimension: updateDimensionTool,
lockDimension: lockDimensionTool,
unlockDimension: unlockDimensionTool,
deleteDimension: deleteDimensionTool,
youtubeExtract: youtubeExtractTool,
websiteExtract: websiteExtractTool,
paperExtract: paperExtractTool,
};
export const TOOL_SETS = {
core: CORE_TOOLS,
orchestration: ORCHESTRATION_TOOLS,
execution: EXECUTION_TOOLS,
};
export const TOOLS: Record<string, any> = {
...CORE_TOOLS,
...ORCHESTRATION_TOOLS,
...EXECUTION_TOOLS,
};
const ORCHESTRATOR_TOOL_NAMES = Array.from(new Set([
...Object.keys(CORE_TOOLS),
'webSearch',
'think',
'executeWorkflow',
'createNode',
'updateNode',
'createEdge',
'updateEdge',
'createDimension',
'updateDimension',
'lockDimension',
'unlockDimension',
'deleteDimension',
'youtubeExtract',
'websiteExtract',
'paperExtract',
]));
const EXECUTOR_TOOL_NAMES = [
...Object.keys(CORE_TOOLS),
...Object.keys(ORCHESTRATION_TOOLS).filter(name =>
name !== 'delegateToMiniRAH' &&
name !== 'delegateToWiseRAH' &&
name !== 'executeWorkflow' &&
name !== 'delegateNodeQuotes' &&
name !== 'delegateNodeComparison'
),
...Object.keys(EXECUTION_TOOLS),
];
const PLANNER_TOOL_NAMES = [
...Object.keys(CORE_TOOLS),
'webSearch',
'think',
'delegateToMiniRAH',
'updateNode', // For workflow execution (integrate workflow needs direct write access)
];
/**
* Get tool by ID
*/
export function getTool(toolId: string): any | null {
return TOOLS[toolId] || null;
}
/**
* Get tools by IDs (for helper's available_tools)
*/
export function getTools(toolIds: string[]): any[] {
if (!Array.isArray(toolIds)) {
console.error('getTools received non-array:', toolIds);
return [];
}
return toolIds.map(id => TOOLS[id]).filter(Boolean);
}
/**
* Get all available tools
*/
export function getAllTools(): any[] {
return Object.values(TOOLS);
}
/**
* Get tool schemas for OpenAI function calling
*/
export function getToolSchemas(toolIds: string[]) {
return getTools(toolIds).map(tool => tool.schema);
}
/**
* Get tools for a specific helper by tool names
* This is the main function used by helper APIs to get their assigned tools
*/
export function getHelperTools(availableToolNames: string[]): Record<string, any> {
if (!Array.isArray(availableToolNames)) {
console.error('getHelperTools received non-array:', availableToolNames);
return {};
}
return availableToolNames.reduce((tools, name) => {
if (TOOLS[name]) {
tools[name] = TOOLS[name];
} else {
console.warn(`Tool '${name}' not found in registry`);
}
return tools;
}, {} as Record<string, any>);
}
export function getDefaultToolNamesForRole(role: 'orchestrator' | 'executor' | 'planner'): string[] {
if (role === 'orchestrator') {
return [...ORCHESTRATOR_TOOL_NAMES];
}
if (role === 'planner') {
return [...PLANNER_TOOL_NAMES];
}
return [...EXECUTOR_TOOL_NAMES];
}
export function getToolsForRole(role: 'orchestrator' | 'executor' | 'planner'): Record<string, any> {
const names = getDefaultToolNamesForRole(role);
return getHelperTools(names);
}
/**
* Execute a tool with given parameters and context
*/
export async function executeTool(toolId: string, params: any, context: any) {
const tool = getTool(toolId);
if (!tool) {
return {
success: false,
error: `Tool '${toolId}' not found`,
data: null
};
}
try {
return await tool.execute(params, context);
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : `Tool '${toolId}' execution failed`,
data: null
};
}
}
// Export group utilities
export { getToolGroup, groupTools, getAllToolsByGroup };
+28
View File
@@ -0,0 +1,28 @@
import { Node } from '@/types/database';
import { SQLiteClient } from '@/services/database/sqlite-client';
export interface ToolContext {
selectedNodes: Node[];
database: SQLiteClient;
}
export interface ToolSchema {
type: 'function';
function: {
name: string;
description: string;
parameters: {
type: 'object';
properties: Record<string, any>;
required: string[];
};
};
}
export interface Tool {
id: string;
name: string;
description: string;
schema: ToolSchema;
execute: (params: any, context: ToolContext) => Promise<any>;
}