family-ai-agent 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/.env.example +49 -0
- package/README.md +161 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +336 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/index.d.ts +37 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +68 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/models.d.ts +17 -0
- package/dist/config/models.d.ts.map +1 -0
- package/dist/config/models.js +128 -0
- package/dist/config/models.js.map +1 -0
- package/dist/core/agents/agent-factory.d.ts +31 -0
- package/dist/core/agents/agent-factory.d.ts.map +1 -0
- package/dist/core/agents/agent-factory.js +151 -0
- package/dist/core/agents/agent-factory.js.map +1 -0
- package/dist/core/agents/base-agent.d.ts +51 -0
- package/dist/core/agents/base-agent.d.ts.map +1 -0
- package/dist/core/agents/base-agent.js +245 -0
- package/dist/core/agents/base-agent.js.map +1 -0
- package/dist/core/agents/index.d.ts +8 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +9 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/agents/personalities/automation.d.ts +14 -0
- package/dist/core/agents/personalities/automation.d.ts.map +1 -0
- package/dist/core/agents/personalities/automation.js +146 -0
- package/dist/core/agents/personalities/automation.js.map +1 -0
- package/dist/core/agents/personalities/chat.d.ts +10 -0
- package/dist/core/agents/personalities/chat.d.ts.map +1 -0
- package/dist/core/agents/personalities/chat.js +132 -0
- package/dist/core/agents/personalities/chat.js.map +1 -0
- package/dist/core/agents/personalities/coding.d.ts +16 -0
- package/dist/core/agents/personalities/coding.d.ts.map +1 -0
- package/dist/core/agents/personalities/coding.js +166 -0
- package/dist/core/agents/personalities/coding.js.map +1 -0
- package/dist/core/agents/personalities/research.d.ts +13 -0
- package/dist/core/agents/personalities/research.d.ts.map +1 -0
- package/dist/core/agents/personalities/research.js +133 -0
- package/dist/core/agents/personalities/research.js.map +1 -0
- package/dist/core/agents/types.d.ts +102 -0
- package/dist/core/agents/types.d.ts.map +1 -0
- package/dist/core/agents/types.js +2 -0
- package/dist/core/agents/types.js.map +1 -0
- package/dist/core/orchestrator/graph.d.ts +118 -0
- package/dist/core/orchestrator/graph.d.ts.map +1 -0
- package/dist/core/orchestrator/graph.js +233 -0
- package/dist/core/orchestrator/graph.js.map +1 -0
- package/dist/database/client.d.ts +19 -0
- package/dist/database/client.d.ts.map +1 -0
- package/dist/database/client.js +95 -0
- package/dist/database/client.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/openrouter-client.d.ts +45 -0
- package/dist/llm/openrouter-client.d.ts.map +1 -0
- package/dist/llm/openrouter-client.js +155 -0
- package/dist/llm/openrouter-client.js.map +1 -0
- package/dist/memory/conversation/index.d.ts +37 -0
- package/dist/memory/conversation/index.d.ts.map +1 -0
- package/dist/memory/conversation/index.js +196 -0
- package/dist/memory/conversation/index.js.map +1 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/knowledge-base/index.d.ts +51 -0
- package/dist/memory/knowledge-base/index.d.ts.map +1 -0
- package/dist/memory/knowledge-base/index.js +222 -0
- package/dist/memory/knowledge-base/index.js.map +1 -0
- package/dist/memory/longterm/vector-store.d.ts +44 -0
- package/dist/memory/longterm/vector-store.d.ts.map +1 -0
- package/dist/memory/longterm/vector-store.js +229 -0
- package/dist/memory/longterm/vector-store.js.map +1 -0
- package/dist/safety/audit-logger.d.ts +68 -0
- package/dist/safety/audit-logger.d.ts.map +1 -0
- package/dist/safety/audit-logger.js +215 -0
- package/dist/safety/audit-logger.js.map +1 -0
- package/dist/safety/guardrails/input-guardrail.d.ts +21 -0
- package/dist/safety/guardrails/input-guardrail.d.ts.map +1 -0
- package/dist/safety/guardrails/input-guardrail.js +145 -0
- package/dist/safety/guardrails/input-guardrail.js.map +1 -0
- package/dist/safety/guardrails/output-guardrail.d.ts +18 -0
- package/dist/safety/guardrails/output-guardrail.d.ts.map +1 -0
- package/dist/safety/guardrails/output-guardrail.js +125 -0
- package/dist/safety/guardrails/output-guardrail.js.map +1 -0
- package/dist/safety/index.d.ts +4 -0
- package/dist/safety/index.d.ts.map +1 -0
- package/dist/safety/index.js +5 -0
- package/dist/safety/index.js.map +1 -0
- package/dist/utils/errors.d.ts +36 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +94 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker/init-db.sql +149 -0
- package/docker/sandbox/Dockerfile.sandbox +29 -0
- package/docker-compose.yml +61 -0
- package/package.json +80 -0
- package/src/cli/index.ts +392 -0
- package/src/config/index.ts +85 -0
- package/src/config/models.ts +156 -0
- package/src/core/agents/agent-factory.ts +192 -0
- package/src/core/agents/base-agent.ts +333 -0
- package/src/core/agents/index.ts +27 -0
- package/src/core/agents/personalities/automation.ts +202 -0
- package/src/core/agents/personalities/chat.ts +159 -0
- package/src/core/agents/personalities/coding.ts +227 -0
- package/src/core/agents/personalities/research.ts +177 -0
- package/src/core/agents/types.ts +124 -0
- package/src/core/orchestrator/graph.ts +305 -0
- package/src/database/client.ts +109 -0
- package/src/index.ts +104 -0
- package/src/llm/openrouter-client.ts +218 -0
- package/src/memory/conversation/index.ts +313 -0
- package/src/memory/index.ts +23 -0
- package/src/memory/knowledge-base/index.ts +357 -0
- package/src/memory/longterm/vector-store.ts +364 -0
- package/src/safety/audit-logger.ts +357 -0
- package/src/safety/guardrails/input-guardrail.ts +191 -0
- package/src/safety/guardrails/output-guardrail.ts +160 -0
- package/src/safety/index.ts +21 -0
- package/src/utils/errors.ts +120 -0
- package/src/utils/logger.ts +74 -0
- package/tsconfig.json +37 -0
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import boxen from 'boxen';
|
|
8
|
+
|
|
9
|
+
import { runOrchestrator, streamOrchestrator } from '../core/orchestrator/graph.js';
|
|
10
|
+
import { getVectorStore } from '../memory/longterm/vector-store.js';
|
|
11
|
+
import { getConversationMemory } from '../memory/conversation/index.js';
|
|
12
|
+
import { getKnowledgeBase } from '../memory/knowledge-base/index.js';
|
|
13
|
+
import { validateInput } from '../safety/guardrails/input-guardrail.js';
|
|
14
|
+
import { validateOutput } from '../safety/guardrails/output-guardrail.js';
|
|
15
|
+
import { initDatabase, closePool } from '../database/client.js';
|
|
16
|
+
import { config, isDevelopment } from '../config/index.js';
|
|
17
|
+
import { createLogger } from '../utils/logger.js';
|
|
18
|
+
import { nanoid } from 'nanoid';
|
|
19
|
+
|
|
20
|
+
const logger = createLogger('CLI');
|
|
21
|
+
const program = new Command();
|
|
22
|
+
|
|
23
|
+
// Display welcome banner
|
|
24
|
+
function showBanner(): void {
|
|
25
|
+
const banner = boxen(
|
|
26
|
+
chalk.bold.cyan('Family AI Agent') +
|
|
27
|
+
'\n\n' +
|
|
28
|
+
chalk.white('Your AI Family - Ready to Help') +
|
|
29
|
+
'\n' +
|
|
30
|
+
chalk.gray('Kakak (Chat) | Researcher | Coder | Automator'),
|
|
31
|
+
{
|
|
32
|
+
padding: 1,
|
|
33
|
+
margin: 1,
|
|
34
|
+
borderStyle: 'round',
|
|
35
|
+
borderColor: 'cyan',
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
console.log(banner);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Interactive chat mode
|
|
42
|
+
async function startInteractiveChat(): Promise<void> {
|
|
43
|
+
showBanner();
|
|
44
|
+
|
|
45
|
+
const threadId = `thread-${nanoid(8)}`;
|
|
46
|
+
const conversationMemory = getConversationMemory();
|
|
47
|
+
const vectorStore = getVectorStore();
|
|
48
|
+
|
|
49
|
+
console.log(chalk.gray(`\nSession: ${threadId}`));
|
|
50
|
+
console.log(chalk.gray('Type "exit" or "quit" to end the session'));
|
|
51
|
+
console.log(chalk.gray('Type "clear" to start a new conversation'));
|
|
52
|
+
console.log(chalk.gray('Type "memory" to view recent memories'));
|
|
53
|
+
console.log(chalk.gray('Type "help" for more commands\n'));
|
|
54
|
+
|
|
55
|
+
let conversationId: string | null = null;
|
|
56
|
+
|
|
57
|
+
while (true) {
|
|
58
|
+
try {
|
|
59
|
+
const { input } = await inquirer.prompt<{ input: string }>([
|
|
60
|
+
{
|
|
61
|
+
type: 'input',
|
|
62
|
+
name: 'input',
|
|
63
|
+
message: chalk.green('You:'),
|
|
64
|
+
prefix: '',
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
const trimmedInput = input.trim();
|
|
69
|
+
|
|
70
|
+
// Handle special commands
|
|
71
|
+
if (['exit', 'quit', 'bye'].includes(trimmedInput.toLowerCase())) {
|
|
72
|
+
console.log(chalk.cyan('\nGoodbye! See you next time. 👋\n'));
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (trimmedInput.toLowerCase() === 'clear') {
|
|
77
|
+
conversationId = null;
|
|
78
|
+
console.log(chalk.yellow('\nConversation cleared. Starting fresh!\n'));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (trimmedInput.toLowerCase() === 'help') {
|
|
83
|
+
showHelp();
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (trimmedInput.toLowerCase() === 'memory') {
|
|
88
|
+
await showMemories();
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!trimmedInput) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Validate input
|
|
97
|
+
const inputValidation = await validateInput(trimmedInput);
|
|
98
|
+
if (!inputValidation.valid) {
|
|
99
|
+
console.log(chalk.red(`\n❌ ${inputValidation.blockedReason}\n`));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (inputValidation.warnings.length > 0) {
|
|
104
|
+
for (const warning of inputValidation.warnings) {
|
|
105
|
+
console.log(chalk.yellow(`⚠️ ${warning}`));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Create conversation if needed
|
|
110
|
+
if (!conversationId) {
|
|
111
|
+
conversationId = await conversationMemory.createConversation(threadId);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Save user message
|
|
115
|
+
await conversationMemory.addMessage(conversationId, 'user', trimmedInput);
|
|
116
|
+
|
|
117
|
+
// Retrieve relevant memories
|
|
118
|
+
const memories = await vectorStore.search(trimmedInput, { limit: 3 });
|
|
119
|
+
|
|
120
|
+
// Show thinking indicator
|
|
121
|
+
const spinner = ora({
|
|
122
|
+
text: chalk.gray('Thinking...'),
|
|
123
|
+
spinner: 'dots',
|
|
124
|
+
}).start();
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
// Run the orchestrator
|
|
128
|
+
const result = await runOrchestrator(
|
|
129
|
+
inputValidation.sanitizedInput,
|
|
130
|
+
{ threadId, conversationId },
|
|
131
|
+
memories
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
spinner.stop();
|
|
135
|
+
|
|
136
|
+
// Validate and display output
|
|
137
|
+
const outputValidation = validateOutput(result.response);
|
|
138
|
+
const response = outputValidation.sanitizedOutput;
|
|
139
|
+
|
|
140
|
+
if (outputValidation.warnings.length > 0) {
|
|
141
|
+
for (const warning of outputValidation.warnings) {
|
|
142
|
+
console.log(chalk.yellow(`⚠️ ${warning}`));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Display response with agent info
|
|
147
|
+
const agentNames = result.activeAgents?.map((a) => a.name).join(', ') || 'AI';
|
|
148
|
+
console.log(chalk.cyan(`\n${agentNames}:`));
|
|
149
|
+
console.log(response);
|
|
150
|
+
console.log();
|
|
151
|
+
|
|
152
|
+
// Save assistant message
|
|
153
|
+
await conversationMemory.addMessage(conversationId, 'assistant', response);
|
|
154
|
+
|
|
155
|
+
// Store important information in long-term memory (simple heuristic)
|
|
156
|
+
if (response.length > 200) {
|
|
157
|
+
await vectorStore.store(
|
|
158
|
+
`Q: ${trimmedInput}\nA: ${response.slice(0, 500)}`,
|
|
159
|
+
'episodic',
|
|
160
|
+
{ importance: 0.6, metadata: { threadId } }
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
spinner.stop();
|
|
165
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
166
|
+
console.log(chalk.red(`\n❌ Error: ${errorMessage}\n`));
|
|
167
|
+
logger.error('Chat error', { error: errorMessage });
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
// Handle Ctrl+C gracefully
|
|
171
|
+
if ((error as { name?: string }).name === 'ExitPromptError') {
|
|
172
|
+
console.log(chalk.cyan('\n\nGoodbye! 👋\n'));
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Show help
|
|
181
|
+
function showHelp(): void {
|
|
182
|
+
console.log(chalk.bold('\n📚 Available Commands:\n'));
|
|
183
|
+
console.log(chalk.white(' exit, quit, bye') + chalk.gray(' - End the session'));
|
|
184
|
+
console.log(chalk.white(' clear') + chalk.gray(' - Start a new conversation'));
|
|
185
|
+
console.log(chalk.white(' memory') + chalk.gray(' - View recent memories'));
|
|
186
|
+
console.log(chalk.white(' help') + chalk.gray(' - Show this help message'));
|
|
187
|
+
console.log();
|
|
188
|
+
console.log(chalk.bold('💡 Tips:\n'));
|
|
189
|
+
console.log(chalk.gray(' • Ask about coding, research, or automation'));
|
|
190
|
+
console.log(chalk.gray(' • The system will route to the best agent'));
|
|
191
|
+
console.log(chalk.gray(' • Memories are saved for future context'));
|
|
192
|
+
console.log();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Show recent memories
|
|
196
|
+
async function showMemories(): Promise<void> {
|
|
197
|
+
console.log(chalk.bold('\n🧠 Recent Memories:\n'));
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const vectorStore = getVectorStore();
|
|
201
|
+
const memories = await vectorStore.search('recent conversation', {
|
|
202
|
+
limit: 5,
|
|
203
|
+
minSimilarity: 0.3,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (memories.length === 0) {
|
|
207
|
+
console.log(chalk.gray(' No memories stored yet.'));
|
|
208
|
+
} else {
|
|
209
|
+
for (const memory of memories) {
|
|
210
|
+
console.log(chalk.white(` [${memory.type}] `) + chalk.gray(memory.content.slice(0, 100) + '...'));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} catch {
|
|
214
|
+
console.log(chalk.gray(' Unable to retrieve memories.'));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Single query mode
|
|
221
|
+
async function runQuery(query: string): Promise<void> {
|
|
222
|
+
const spinner = ora('Processing...').start();
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const inputValidation = await validateInput(query);
|
|
226
|
+
if (!inputValidation.valid) {
|
|
227
|
+
spinner.fail(inputValidation.blockedReason);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const vectorStore = getVectorStore();
|
|
232
|
+
const memories = await vectorStore.search(query, { limit: 3 });
|
|
233
|
+
|
|
234
|
+
const result = await runOrchestrator(
|
|
235
|
+
inputValidation.sanitizedInput,
|
|
236
|
+
{},
|
|
237
|
+
memories
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
spinner.stop();
|
|
241
|
+
|
|
242
|
+
const outputValidation = validateOutput(result.response);
|
|
243
|
+
console.log(outputValidation.sanitizedOutput);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
spinner.fail('Query failed');
|
|
246
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Main program setup
|
|
252
|
+
program
|
|
253
|
+
.name('family-ai')
|
|
254
|
+
.description('Family AI Agent - Your AI Family for All Tasks')
|
|
255
|
+
.version('1.0.0');
|
|
256
|
+
|
|
257
|
+
program
|
|
258
|
+
.command('chat')
|
|
259
|
+
.description('Start interactive chat mode')
|
|
260
|
+
.action(async () => {
|
|
261
|
+
try {
|
|
262
|
+
await initDatabase();
|
|
263
|
+
await startInteractiveChat();
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.error(chalk.red('Failed to start chat:'), error);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
} finally {
|
|
268
|
+
await closePool();
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
program
|
|
273
|
+
.command('ask <query>')
|
|
274
|
+
.description('Ask a single question')
|
|
275
|
+
.action(async (query: string) => {
|
|
276
|
+
try {
|
|
277
|
+
await initDatabase();
|
|
278
|
+
await runQuery(query);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error(chalk.red('Failed:'), error);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
} finally {
|
|
283
|
+
await closePool();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
program
|
|
288
|
+
.command('upload <filepath>')
|
|
289
|
+
.description('Upload a document to the knowledge base')
|
|
290
|
+
.action(async (filepath: string) => {
|
|
291
|
+
const spinner = ora('Uploading document...').start();
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
await initDatabase();
|
|
295
|
+
|
|
296
|
+
const fs = await import('fs/promises');
|
|
297
|
+
const path = await import('path');
|
|
298
|
+
|
|
299
|
+
const content = await fs.readFile(filepath, 'utf-8');
|
|
300
|
+
const filename = path.basename(filepath);
|
|
301
|
+
const stats = await fs.stat(filepath);
|
|
302
|
+
|
|
303
|
+
const kb = getKnowledgeBase();
|
|
304
|
+
const docId = await kb.addDocument(content, filename, {
|
|
305
|
+
fileSize: stats.size,
|
|
306
|
+
fileType: path.extname(filepath),
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
spinner.succeed(`Document uploaded: ${docId}`);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
spinner.fail('Upload failed');
|
|
312
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
313
|
+
process.exit(1);
|
|
314
|
+
} finally {
|
|
315
|
+
await closePool();
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
program
|
|
320
|
+
.command('search <query>')
|
|
321
|
+
.description('Search the knowledge base')
|
|
322
|
+
.option('-l, --limit <number>', 'Maximum results', '5')
|
|
323
|
+
.action(async (query: string, options: { limit: string }) => {
|
|
324
|
+
const spinner = ora('Searching...').start();
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
await initDatabase();
|
|
328
|
+
|
|
329
|
+
const kb = getKnowledgeBase();
|
|
330
|
+
const results = await kb.search(query, {
|
|
331
|
+
limit: parseInt(options.limit, 10),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
spinner.stop();
|
|
335
|
+
|
|
336
|
+
if (results.length === 0) {
|
|
337
|
+
console.log(chalk.yellow('No results found.'));
|
|
338
|
+
} else {
|
|
339
|
+
console.log(chalk.bold(`\nFound ${results.length} results:\n`));
|
|
340
|
+
for (const result of results) {
|
|
341
|
+
console.log(chalk.white(`📄 ${result.filename}`));
|
|
342
|
+
console.log(chalk.gray(` Similarity: ${(result.similarity * 100).toFixed(1)}%`));
|
|
343
|
+
console.log(chalk.gray(` ${result.content.slice(0, 150)}...`));
|
|
344
|
+
console.log();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
spinner.fail('Search failed');
|
|
349
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
350
|
+
process.exit(1);
|
|
351
|
+
} finally {
|
|
352
|
+
await closePool();
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
program
|
|
357
|
+
.command('status')
|
|
358
|
+
.description('Show system status')
|
|
359
|
+
.action(async () => {
|
|
360
|
+
try {
|
|
361
|
+
await initDatabase();
|
|
362
|
+
|
|
363
|
+
console.log(chalk.bold('\n🤖 Family AI Agent Status\n'));
|
|
364
|
+
console.log(chalk.white('Database:'), chalk.green('Connected'));
|
|
365
|
+
console.log(chalk.white('Environment:'), chalk.cyan(config.NODE_ENV));
|
|
366
|
+
console.log(chalk.white('Default Model:'), chalk.cyan(config.DEFAULT_MODEL));
|
|
367
|
+
console.log(chalk.white('Safety Filters:'), config.ENABLE_CONTENT_FILTER ? chalk.green('Enabled') : chalk.yellow('Disabled'));
|
|
368
|
+
console.log(chalk.white('Audit Logging:'), config.ENABLE_AUDIT_LOGGING ? chalk.green('Enabled') : chalk.yellow('Disabled'));
|
|
369
|
+
console.log();
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.log(chalk.white('Database:'), chalk.red('Disconnected'));
|
|
372
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
373
|
+
} finally {
|
|
374
|
+
await closePool();
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Handle unhandled rejections
|
|
379
|
+
process.on('unhandledRejection', (error) => {
|
|
380
|
+
console.error(chalk.red('Unhandled error:'), error);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Parse command line arguments
|
|
385
|
+
program.parse();
|
|
386
|
+
|
|
387
|
+
// If no command specified, start interactive chat
|
|
388
|
+
if (!process.argv.slice(2).length) {
|
|
389
|
+
initDatabase()
|
|
390
|
+
.then(() => startInteractiveChat())
|
|
391
|
+
.finally(() => closePool());
|
|
392
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { config as dotenvConfig } from 'dotenv';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
dotenvConfig();
|
|
5
|
+
|
|
6
|
+
const envSchema = z.object({
|
|
7
|
+
// OpenRouter
|
|
8
|
+
OPENROUTER_API_KEY: z.string().min(1, 'OpenRouter API key is required'),
|
|
9
|
+
OPENROUTER_BASE_URL: z.string().url().default('https://openrouter.ai/api/v1'),
|
|
10
|
+
|
|
11
|
+
// Models
|
|
12
|
+
DEFAULT_MODEL: z.string().default('anthropic/claude-3.5-sonnet'),
|
|
13
|
+
FAST_MODEL: z.string().default('anthropic/claude-3-haiku'),
|
|
14
|
+
EMBEDDING_MODEL: z.string().default('openai/text-embedding-3-small'),
|
|
15
|
+
|
|
16
|
+
// Database
|
|
17
|
+
DB_HOST: z.string().default('localhost'),
|
|
18
|
+
DB_PORT: z.coerce.number().default(5432),
|
|
19
|
+
DB_USER: z.string().default('familyai'),
|
|
20
|
+
DB_PASSWORD: z.string().default('familyai123'),
|
|
21
|
+
DB_NAME: z.string().default('familyai'),
|
|
22
|
+
DATABASE_URL: z.string().optional(),
|
|
23
|
+
|
|
24
|
+
// Redis
|
|
25
|
+
REDIS_HOST: z.string().default('localhost'),
|
|
26
|
+
REDIS_PORT: z.coerce.number().default(6379),
|
|
27
|
+
REDIS_URL: z.string().optional(),
|
|
28
|
+
|
|
29
|
+
// API
|
|
30
|
+
API_PORT: z.coerce.number().default(3000),
|
|
31
|
+
API_HOST: z.string().default('0.0.0.0'),
|
|
32
|
+
|
|
33
|
+
// Safety
|
|
34
|
+
ENABLE_CONTENT_FILTER: z.coerce.boolean().default(true),
|
|
35
|
+
ENABLE_PII_DETECTION: z.coerce.boolean().default(true),
|
|
36
|
+
ENABLE_AUDIT_LOGGING: z.coerce.boolean().default(true),
|
|
37
|
+
MAX_TOKENS_PER_REQUEST: z.coerce.number().default(4096),
|
|
38
|
+
RATE_LIMIT_MAX: z.coerce.number().default(100),
|
|
39
|
+
RATE_LIMIT_WINDOW_MS: z.coerce.number().default(60000),
|
|
40
|
+
|
|
41
|
+
// Sandbox
|
|
42
|
+
SANDBOX_ENABLED: z.coerce.boolean().default(true),
|
|
43
|
+
SANDBOX_TIMEOUT_MS: z.coerce.number().default(30000),
|
|
44
|
+
SANDBOX_MEMORY_LIMIT_MB: z.coerce.number().default(256),
|
|
45
|
+
|
|
46
|
+
// Logging
|
|
47
|
+
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
48
|
+
LOG_FORMAT: z.enum(['json', 'pretty']).default('json'),
|
|
49
|
+
|
|
50
|
+
// Web Search
|
|
51
|
+
TAVILY_API_KEY: z.string().optional(),
|
|
52
|
+
SERP_API_KEY: z.string().optional(),
|
|
53
|
+
|
|
54
|
+
// Environment
|
|
55
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
type EnvConfig = z.infer<typeof envSchema>;
|
|
59
|
+
|
|
60
|
+
function loadConfig(): EnvConfig {
|
|
61
|
+
const parsed = envSchema.safeParse(process.env);
|
|
62
|
+
|
|
63
|
+
if (!parsed.success) {
|
|
64
|
+
console.error('Configuration validation failed:');
|
|
65
|
+
console.error(parsed.error.format());
|
|
66
|
+
throw new Error('Invalid configuration. Check your .env file.');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return parsed.data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const config = loadConfig();
|
|
73
|
+
|
|
74
|
+
export const getDatabaseUrl = (): string => {
|
|
75
|
+
if (config.DATABASE_URL) return config.DATABASE_URL;
|
|
76
|
+
return `postgresql://${config.DB_USER}:${config.DB_PASSWORD}@${config.DB_HOST}:${config.DB_PORT}/${config.DB_NAME}`;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const getRedisUrl = (): string => {
|
|
80
|
+
if (config.REDIS_URL) return config.REDIS_URL;
|
|
81
|
+
return `redis://${config.REDIS_HOST}:${config.REDIS_PORT}`;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const isProduction = (): boolean => config.NODE_ENV === 'production';
|
|
85
|
+
export const isDevelopment = (): boolean => config.NODE_ENV === 'development';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// OpenRouter Model Configurations
|
|
2
|
+
export interface ModelConfig {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
contextWindow: number;
|
|
6
|
+
maxOutput: number;
|
|
7
|
+
costPer1kInput: number;
|
|
8
|
+
costPer1kOutput: number;
|
|
9
|
+
capabilities: ModelCapability[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ModelCapability =
|
|
13
|
+
| 'chat'
|
|
14
|
+
| 'code'
|
|
15
|
+
| 'reasoning'
|
|
16
|
+
| 'vision'
|
|
17
|
+
| 'function_calling'
|
|
18
|
+
| 'json_mode';
|
|
19
|
+
|
|
20
|
+
export const AVAILABLE_MODELS: Record<string, ModelConfig> = {
|
|
21
|
+
// Anthropic Models
|
|
22
|
+
'anthropic/claude-3.5-sonnet': {
|
|
23
|
+
id: 'anthropic/claude-3.5-sonnet',
|
|
24
|
+
name: 'Claude 3.5 Sonnet',
|
|
25
|
+
contextWindow: 200000,
|
|
26
|
+
maxOutput: 8192,
|
|
27
|
+
costPer1kInput: 0.003,
|
|
28
|
+
costPer1kOutput: 0.015,
|
|
29
|
+
capabilities: ['chat', 'code', 'reasoning', 'vision', 'function_calling', 'json_mode'],
|
|
30
|
+
},
|
|
31
|
+
'anthropic/claude-3-haiku': {
|
|
32
|
+
id: 'anthropic/claude-3-haiku',
|
|
33
|
+
name: 'Claude 3 Haiku',
|
|
34
|
+
contextWindow: 200000,
|
|
35
|
+
maxOutput: 4096,
|
|
36
|
+
costPer1kInput: 0.00025,
|
|
37
|
+
costPer1kOutput: 0.00125,
|
|
38
|
+
capabilities: ['chat', 'code', 'vision', 'function_calling'],
|
|
39
|
+
},
|
|
40
|
+
'anthropic/claude-3-opus': {
|
|
41
|
+
id: 'anthropic/claude-3-opus',
|
|
42
|
+
name: 'Claude 3 Opus',
|
|
43
|
+
contextWindow: 200000,
|
|
44
|
+
maxOutput: 4096,
|
|
45
|
+
costPer1kInput: 0.015,
|
|
46
|
+
costPer1kOutput: 0.075,
|
|
47
|
+
capabilities: ['chat', 'code', 'reasoning', 'vision', 'function_calling', 'json_mode'],
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// OpenAI Models
|
|
51
|
+
'openai/gpt-4-turbo': {
|
|
52
|
+
id: 'openai/gpt-4-turbo',
|
|
53
|
+
name: 'GPT-4 Turbo',
|
|
54
|
+
contextWindow: 128000,
|
|
55
|
+
maxOutput: 4096,
|
|
56
|
+
costPer1kInput: 0.01,
|
|
57
|
+
costPer1kOutput: 0.03,
|
|
58
|
+
capabilities: ['chat', 'code', 'reasoning', 'vision', 'function_calling', 'json_mode'],
|
|
59
|
+
},
|
|
60
|
+
'openai/gpt-4o': {
|
|
61
|
+
id: 'openai/gpt-4o',
|
|
62
|
+
name: 'GPT-4o',
|
|
63
|
+
contextWindow: 128000,
|
|
64
|
+
maxOutput: 16384,
|
|
65
|
+
costPer1kInput: 0.005,
|
|
66
|
+
costPer1kOutput: 0.015,
|
|
67
|
+
capabilities: ['chat', 'code', 'reasoning', 'vision', 'function_calling', 'json_mode'],
|
|
68
|
+
},
|
|
69
|
+
'openai/gpt-4o-mini': {
|
|
70
|
+
id: 'openai/gpt-4o-mini',
|
|
71
|
+
name: 'GPT-4o Mini',
|
|
72
|
+
contextWindow: 128000,
|
|
73
|
+
maxOutput: 16384,
|
|
74
|
+
costPer1kInput: 0.00015,
|
|
75
|
+
costPer1kOutput: 0.0006,
|
|
76
|
+
capabilities: ['chat', 'code', 'vision', 'function_calling', 'json_mode'],
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// Google Models
|
|
80
|
+
'google/gemini-pro-1.5': {
|
|
81
|
+
id: 'google/gemini-pro-1.5',
|
|
82
|
+
name: 'Gemini Pro 1.5',
|
|
83
|
+
contextWindow: 1000000,
|
|
84
|
+
maxOutput: 8192,
|
|
85
|
+
costPer1kInput: 0.00125,
|
|
86
|
+
costPer1kOutput: 0.005,
|
|
87
|
+
capabilities: ['chat', 'code', 'reasoning', 'vision', 'function_calling'],
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Meta Models
|
|
91
|
+
'meta-llama/llama-3.1-70b-instruct': {
|
|
92
|
+
id: 'meta-llama/llama-3.1-70b-instruct',
|
|
93
|
+
name: 'Llama 3.1 70B',
|
|
94
|
+
contextWindow: 131072,
|
|
95
|
+
maxOutput: 4096,
|
|
96
|
+
costPer1kInput: 0.00059,
|
|
97
|
+
costPer1kOutput: 0.00079,
|
|
98
|
+
capabilities: ['chat', 'code', 'function_calling'],
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
// Embedding Models
|
|
102
|
+
'openai/text-embedding-3-small': {
|
|
103
|
+
id: 'openai/text-embedding-3-small',
|
|
104
|
+
name: 'Text Embedding 3 Small',
|
|
105
|
+
contextWindow: 8191,
|
|
106
|
+
maxOutput: 1536,
|
|
107
|
+
costPer1kInput: 0.00002,
|
|
108
|
+
costPer1kOutput: 0,
|
|
109
|
+
capabilities: [],
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export type AgentRole = 'supervisor' | 'research' | 'coding' | 'automation' | 'chat';
|
|
114
|
+
|
|
115
|
+
// Recommended models for each agent role
|
|
116
|
+
export const AGENT_MODEL_RECOMMENDATIONS: Record<AgentRole, string[]> = {
|
|
117
|
+
supervisor: [
|
|
118
|
+
'anthropic/claude-3.5-sonnet',
|
|
119
|
+
'openai/gpt-4o',
|
|
120
|
+
'anthropic/claude-3-opus',
|
|
121
|
+
],
|
|
122
|
+
research: [
|
|
123
|
+
'anthropic/claude-3.5-sonnet',
|
|
124
|
+
'google/gemini-pro-1.5',
|
|
125
|
+
'openai/gpt-4o',
|
|
126
|
+
],
|
|
127
|
+
coding: [
|
|
128
|
+
'anthropic/claude-3.5-sonnet',
|
|
129
|
+
'openai/gpt-4o',
|
|
130
|
+
'meta-llama/llama-3.1-70b-instruct',
|
|
131
|
+
],
|
|
132
|
+
automation: [
|
|
133
|
+
'anthropic/claude-3-haiku',
|
|
134
|
+
'openai/gpt-4o-mini',
|
|
135
|
+
'meta-llama/llama-3.1-70b-instruct',
|
|
136
|
+
],
|
|
137
|
+
chat: [
|
|
138
|
+
'anthropic/claude-3.5-sonnet',
|
|
139
|
+
'openai/gpt-4o',
|
|
140
|
+
'anthropic/claude-3-haiku',
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export function getModelConfig(modelId: string): ModelConfig | undefined {
|
|
145
|
+
return AVAILABLE_MODELS[modelId];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function getRecommendedModel(role: AgentRole): string {
|
|
149
|
+
const recommendations = AGENT_MODEL_RECOMMENDATIONS[role];
|
|
150
|
+
return recommendations[0] ?? 'anthropic/claude-3.5-sonnet';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function hasCapability(modelId: string, capability: ModelCapability): boolean {
|
|
154
|
+
const model = AVAILABLE_MODELS[modelId];
|
|
155
|
+
return model?.capabilities.includes(capability) ?? false;
|
|
156
|
+
}
|