alive-ai 0.1.0 → 0.1.2
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/README.md +204 -56
- package/brain/memory/manager.py +16 -0
- package/brain/memory/openmind.py +128 -0
- package/cli/index.js +122 -14
- package/config/settings.example.json +7 -0
- package/core/initialization.py +13 -3
- package/core/self.py +2 -2
- package/docs/assets/logo.svg +7 -14
- package/docs/dashboard.html +2150 -0
- package/docs/index.html +208 -242
- package/input/manifest.md +7 -1
- package/input/terminal/__init__.py +2 -0
- package/input/terminal/listener.py +307 -0
- package/main.py +15 -1
- package/manifest.md +9 -7
- package/package.json +6 -2
- package/pyproject.toml +1 -1
package/cli/index.js
CHANGED
|
@@ -45,12 +45,15 @@ Usage:
|
|
|
45
45
|
alive-ai setup [--yes] Create local config from templates
|
|
46
46
|
alive-ai demo [--port 8080] Run the animated dashboard demo
|
|
47
47
|
alive-ai start [--skip-install] Install Python deps if needed and start runtime
|
|
48
|
+
alive-ai chat [--skip-install] Start runtime with terminal chat input
|
|
48
49
|
alive-ai doctor Check local prerequisites
|
|
49
50
|
|
|
50
51
|
Quick start:
|
|
51
|
-
npx
|
|
52
|
+
npx alive-ai@latest init my-ai
|
|
52
53
|
cd my-ai
|
|
53
54
|
npx . setup
|
|
55
|
+
npx . doctor
|
|
56
|
+
npx . chat
|
|
54
57
|
npx . demo
|
|
55
58
|
npx . start`);
|
|
56
59
|
}
|
|
@@ -114,6 +117,8 @@ function initProject(args) {
|
|
|
114
117
|
console.log("Next:");
|
|
115
118
|
console.log(` cd ${target}`);
|
|
116
119
|
console.log(" npx . setup");
|
|
120
|
+
console.log(" npx . doctor");
|
|
121
|
+
console.log(" npx . chat");
|
|
117
122
|
console.log(" npx . demo");
|
|
118
123
|
}
|
|
119
124
|
|
|
@@ -138,6 +143,28 @@ function ask(question, fallback, assumeYes) {
|
|
|
138
143
|
});
|
|
139
144
|
}
|
|
140
145
|
|
|
146
|
+
function normalizeChoice(value, fallback = "") {
|
|
147
|
+
return String(value || fallback).trim().toLowerCase();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function isSkipped(value) {
|
|
151
|
+
return normalizeChoice(value) === "skip";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function emptyIfSkipped(value) {
|
|
155
|
+
return isSkipped(value) ? "" : value;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function readProjectSettings() {
|
|
159
|
+
const settingsPath = path.join(process.cwd(), "config", "settings.json");
|
|
160
|
+
if (!fs.existsSync(settingsPath)) return {};
|
|
161
|
+
try {
|
|
162
|
+
return readJson(settingsPath);
|
|
163
|
+
} catch {
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
141
168
|
async function setupProject(args) {
|
|
142
169
|
const dryRun = hasFlag(args, "--dry-run");
|
|
143
170
|
const assumeYes = hasFlag(args, "--yes") || hasFlag(args, "-y") || dryRun;
|
|
@@ -165,25 +192,68 @@ async function setupProject(args) {
|
|
|
165
192
|
return;
|
|
166
193
|
}
|
|
167
194
|
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
const
|
|
195
|
+
const displayNameAnswer = await ask("Agent display name", "Nova", assumeYes);
|
|
196
|
+
const displayName = emptyIfSkipped(displayNameAnswer) || "Nova";
|
|
197
|
+
const ownerId = emptyIfSkipped(await ask("Telegram owner ID (optional, use skip to leave blank)", "", assumeYes));
|
|
198
|
+
const telegramToken = emptyIfSkipped(await ask("Telegram bot token (optional, use skip to leave blank)", "", assumeYes));
|
|
199
|
+
const providerChoice = normalizeChoice(
|
|
200
|
+
await ask("LLM provider: local, openrouter, zai, or skip", "local", assumeYes),
|
|
201
|
+
"local"
|
|
202
|
+
);
|
|
203
|
+
const provider = providerChoice === "local" ? "ollama" : providerChoice;
|
|
172
204
|
const openRouterKey = provider === "openrouter"
|
|
173
|
-
? await ask("OpenRouter API key", "", assumeYes)
|
|
205
|
+
? emptyIfSkipped(await ask("OpenRouter API key (or skip)", "", assumeYes))
|
|
174
206
|
: "";
|
|
175
207
|
const zaiKey = provider === "zai"
|
|
176
|
-
? await ask("ZAI API key", "", assumeYes)
|
|
208
|
+
? emptyIfSkipped(await ask("ZAI API key (or skip)", "", assumeYes))
|
|
209
|
+
: "";
|
|
210
|
+
const ttsChoice = normalizeChoice(
|
|
211
|
+
await ask("Voice provider: gtts, google, vibe, or skip", "gtts", assumeYes),
|
|
212
|
+
"gtts"
|
|
213
|
+
);
|
|
214
|
+
const googleTtsKey = ttsChoice === "google"
|
|
215
|
+
? emptyIfSkipped(await ask("Google TTS API key (optional, use skip for ADC)", "", assumeYes))
|
|
216
|
+
: "";
|
|
217
|
+
const vibeTtsUrl = ttsChoice === "vibe"
|
|
218
|
+
? emptyIfSkipped(await ask("VibeVoice URL (or skip)", "http://127.0.0.1:8088", assumeYes))
|
|
219
|
+
: "";
|
|
220
|
+
const falKey = emptyIfSkipped(await ask("Fal.ai image API key (optional, use skip to disable)", "", assumeYes));
|
|
221
|
+
const memoryChoice = normalizeChoice(
|
|
222
|
+
await ask("Memory mode: built-in, openmind-cloud, or openmind-local", "built-in", assumeYes),
|
|
223
|
+
"built-in"
|
|
224
|
+
);
|
|
225
|
+
const openmindEnabled = memoryChoice.startsWith("openmind");
|
|
226
|
+
const openmindBaseUrl = openmindEnabled
|
|
227
|
+
? memoryChoice === "openmind-local"
|
|
228
|
+
? emptyIfSkipped(await ask("OpenMind local URL", "http://127.0.0.1:3333", assumeYes))
|
|
229
|
+
: emptyIfSkipped(await ask("OpenMind cloud URL", "https://theopenmind.pro", assumeYes))
|
|
230
|
+
: "";
|
|
231
|
+
const openmindKey = openmindEnabled
|
|
232
|
+
? emptyIfSkipped(await ask("OpenMind API key (om_..., optional for unauthenticated local dev)", "", assumeYes))
|
|
177
233
|
: "";
|
|
178
234
|
|
|
179
235
|
const settings = readJson(settingsExample);
|
|
180
236
|
settings.AGENT_NAME = displayName;
|
|
237
|
+
settings.INPUT_CHANNEL = "telegram";
|
|
181
238
|
settings.telegram_token = telegramToken;
|
|
182
239
|
settings.TELEGRAM_OWNER_ID = ownerId;
|
|
183
|
-
settings.LLM_PROVIDER = provider;
|
|
240
|
+
settings.LLM_PROVIDER = provider === "skip" ? "ollama" : provider;
|
|
184
241
|
settings.OPENROUTER_API_KEY = openRouterKey;
|
|
185
242
|
settings.ZAI_API_KEY = zaiKey;
|
|
186
|
-
settings.LLM_FALLBACK.
|
|
243
|
+
settings.LLM_FALLBACK.ENABLED = provider !== "skip";
|
|
244
|
+
settings.LLM_FALLBACK.ORDER = provider === "skip"
|
|
245
|
+
? ["ollama"]
|
|
246
|
+
: provider === "ollama"
|
|
247
|
+
? ["ollama"]
|
|
248
|
+
: [provider, "ollama"];
|
|
249
|
+
settings.TTS_PROVIDER = ttsChoice === "skip" ? "none" : ttsChoice;
|
|
250
|
+
settings.GOOGLE_TTS_API_KEY = googleTtsKey;
|
|
251
|
+
settings.vibe_tts_url = vibeTtsUrl;
|
|
252
|
+
settings.FAL_API_KEY = falKey;
|
|
253
|
+
settings.OPENMIND_ENABLED = openmindEnabled;
|
|
254
|
+
settings.OPENMIND_MODE = openmindEnabled ? "hybrid" : "built-in";
|
|
255
|
+
settings.OPENMIND_BASE_URL = openmindBaseUrl || "https://theopenmind.pro";
|
|
256
|
+
settings.OPENMIND_API_KEY = openmindKey;
|
|
187
257
|
|
|
188
258
|
const self = readJson(selfExample);
|
|
189
259
|
self.who_i_am.name = displayName;
|
|
@@ -201,7 +271,7 @@ async function setupProject(args) {
|
|
|
201
271
|
ensureDir(path.join(process.cwd(), "myvids"));
|
|
202
272
|
|
|
203
273
|
console.log("Alive-AI config created.");
|
|
204
|
-
console.log("Run `npx . demo`
|
|
274
|
+
console.log("Run `npx . chat` for terminal chat, `npx . demo` for the dashboard preview, or `npx . start` for Telegram/runtime mode.");
|
|
205
275
|
}
|
|
206
276
|
|
|
207
277
|
function findCommand(candidates) {
|
|
@@ -212,19 +282,47 @@ function findCommand(candidates) {
|
|
|
212
282
|
return null;
|
|
213
283
|
}
|
|
214
284
|
|
|
215
|
-
function doctor() {
|
|
285
|
+
async function doctor() {
|
|
216
286
|
const python = findCommand(["python3.11", "python3", "python"]);
|
|
217
287
|
const uv = findCommand(["uv"]);
|
|
218
288
|
const ffmpeg = findCommand(["ffmpeg"]);
|
|
219
289
|
const docker = findCommand(["docker"]);
|
|
220
290
|
const node = process.version;
|
|
291
|
+
const settings = readProjectSettings();
|
|
221
292
|
|
|
222
293
|
console.log("Alive-AI doctor");
|
|
294
|
+
console.log(` system: ${os.platform()} ${os.arch()}`);
|
|
223
295
|
console.log(` node: ${node}`);
|
|
224
296
|
console.log(` python: ${python || "missing"}`);
|
|
225
297
|
console.log(` uv: ${uv || "missing, will use venv + pip"}`);
|
|
226
298
|
console.log(` ffmpeg: ${ffmpeg || "missing, voice conversion may be limited"}`);
|
|
227
299
|
console.log(` docker: ${docker || "missing, Redis can still be external"}`);
|
|
300
|
+
console.log(` input: ${settings.INPUT_CHANNEL || "telegram"}`);
|
|
301
|
+
|
|
302
|
+
if (!python) {
|
|
303
|
+
console.log("");
|
|
304
|
+
console.log("Install Python 3.11+ first:");
|
|
305
|
+
if (process.platform === "darwin") console.log(" brew install python@3.11");
|
|
306
|
+
else if (process.platform === "win32") console.log(" winget install Python.Python.3.11");
|
|
307
|
+
else console.log(" sudo apt install python3.11 python3.11-venv");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (settings.OPENMIND_ENABLED) {
|
|
311
|
+
const baseUrl = String(settings.OPENMIND_BASE_URL || "https://theopenmind.pro").replace(/\/$/, "");
|
|
312
|
+
const headers = {};
|
|
313
|
+
if (settings.OPENMIND_API_KEY) headers.authorization = `Bearer ${settings.OPENMIND_API_KEY}`;
|
|
314
|
+
try {
|
|
315
|
+
const controller = new AbortController();
|
|
316
|
+
const timer = setTimeout(() => controller.abort(), 4000);
|
|
317
|
+
const response = await fetch(`${baseUrl}/health`, { headers, signal: controller.signal });
|
|
318
|
+
clearTimeout(timer);
|
|
319
|
+
console.log(` OpenMind: ${response.ok ? "reachable" : `HTTP ${response.status}`} (${baseUrl})`);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.log(` OpenMind: unreachable (${baseUrl})`);
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
console.log(" OpenMind: disabled");
|
|
325
|
+
}
|
|
228
326
|
|
|
229
327
|
if (!python) process.exitCode = 1;
|
|
230
328
|
}
|
|
@@ -267,14 +365,23 @@ function ensurePythonEnv(skipInstall) {
|
|
|
267
365
|
|
|
268
366
|
function startRuntime(args) {
|
|
269
367
|
if (!fs.existsSync(path.join(process.cwd(), "config", "settings.json"))) {
|
|
270
|
-
console.
|
|
271
|
-
process.
|
|
368
|
+
console.log("Missing config/settings.json. Starting onboarding first.");
|
|
369
|
+
const setupArgs = process.stdin.isTTY ? [] : ["--yes"];
|
|
370
|
+
setupProject(setupArgs).then(() => startRuntime(args));
|
|
371
|
+
return;
|
|
272
372
|
}
|
|
273
373
|
const pythonBin = ensurePythonEnv(hasFlag(args, "--skip-install"));
|
|
274
|
-
const
|
|
374
|
+
const extraArgs = [];
|
|
375
|
+
const inputChannel = argValue(args, "--input", null);
|
|
376
|
+
if (inputChannel) extraArgs.push("--input", inputChannel);
|
|
377
|
+
const child = spawn(pythonBin, ["main.py", ...extraArgs], { stdio: "inherit", cwd: process.cwd() });
|
|
275
378
|
child.on("exit", (code) => process.exit(code || 0));
|
|
276
379
|
}
|
|
277
380
|
|
|
381
|
+
function startTerminalChat(args) {
|
|
382
|
+
return startRuntime(["--input", "terminal", ...args]);
|
|
383
|
+
}
|
|
384
|
+
|
|
278
385
|
function demoHtml() {
|
|
279
386
|
return fs.readFileSync(path.join(PACKAGE_ROOT, "demo", "index.html"), "utf8");
|
|
280
387
|
}
|
|
@@ -359,6 +466,7 @@ async function main() {
|
|
|
359
466
|
if (command === "setup") return setupProject(args);
|
|
360
467
|
if (command === "demo") return startDemo(args);
|
|
361
468
|
if (command === "start") return startRuntime(args);
|
|
469
|
+
if (command === "chat") return startTerminalChat(args);
|
|
362
470
|
if (command === "doctor") return doctor();
|
|
363
471
|
console.error(`Unknown command: ${command}`);
|
|
364
472
|
usage();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Alive-AI settings. Copy to config/settings.json with `npx alive-ai setup`.",
|
|
3
3
|
"AGENT_NAME": "Nova",
|
|
4
|
+
"INPUT_CHANNEL": "telegram",
|
|
5
|
+
"ALIVE_AI_TERMINAL_USER_ID": "terminal_owner",
|
|
4
6
|
"telegram_token": "",
|
|
5
7
|
"TELEGRAM_OWNER_ID": "",
|
|
6
8
|
"WEBUI_ENABLED": true,
|
|
@@ -31,7 +33,12 @@
|
|
|
31
33
|
"TTS_PROVIDER": "gtts",
|
|
32
34
|
"vibe_tts_url": "",
|
|
33
35
|
"GOOGLE_TTS_API_KEY": "",
|
|
36
|
+
"FAL_API_KEY": "",
|
|
34
37
|
"HF_TOKEN": "",
|
|
38
|
+
"OPENMIND_ENABLED": false,
|
|
39
|
+
"OPENMIND_MODE": "built-in",
|
|
40
|
+
"OPENMIND_BASE_URL": "https://theopenmind.pro",
|
|
41
|
+
"OPENMIND_API_KEY": "",
|
|
35
42
|
"EMOTION_RATE_LOVE": 65,
|
|
36
43
|
"EMOTION_RATE_DESIRE": 45,
|
|
37
44
|
"EMOTION_RATE_AROUSAL": 40,
|
package/core/initialization.py
CHANGED
|
@@ -6,7 +6,7 @@ Module loading and startup logic for Self
|
|
|
6
6
|
import os
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
async def load_modules(self):
|
|
9
|
+
async def load_modules(self, input_channel: str = "telegram"):
|
|
10
10
|
"""Load all modules and initialize the AI system"""
|
|
11
11
|
name = self.config.identity.get("name", "AI")
|
|
12
12
|
print(f"[{name}] Waking up...")
|
|
@@ -21,7 +21,6 @@ async def load_modules(self):
|
|
|
21
21
|
from brain.stt import GoogleSTT
|
|
22
22
|
from brain.embeddings import get_embedding_service
|
|
23
23
|
from heart.core import Heart
|
|
24
|
-
from input.telegram.listener import TelegramListener
|
|
25
24
|
from output.text.sender import TextSender
|
|
26
25
|
from skills.photo_manager.scanner import PhotoScanner
|
|
27
26
|
from skills.video_manager.scanner import VideoScanner
|
|
@@ -41,7 +40,14 @@ async def load_modules(self):
|
|
|
41
40
|
_init_photos(self, name)
|
|
42
41
|
_init_videos(self, name)
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
input_channel = (input_channel or "telegram").lower()
|
|
44
|
+
if input_channel == "terminal":
|
|
45
|
+
from input.terminal.listener import TerminalListener
|
|
46
|
+
self._input = TerminalListener(self.nervous, self.config, stt=self._stt, heart=self._heart)
|
|
47
|
+
else:
|
|
48
|
+
from input.telegram.listener import TelegramListener
|
|
49
|
+
self._input = TelegramListener(self.nervous, self.config, stt=self._stt, heart=self._heart)
|
|
50
|
+
print(f"[{name}] Input channel: {input_channel}")
|
|
45
51
|
self._output = TextSender(self.nervous, self.config)
|
|
46
52
|
self.nervous.heart = self._heart
|
|
47
53
|
|
|
@@ -99,6 +105,10 @@ async def _init_voice(self, name: str):
|
|
|
99
105
|
|
|
100
106
|
# Get TTS provider from settings (default to vibe)
|
|
101
107
|
tts_provider = get("TTS_PROVIDER", "vibe").lower()
|
|
108
|
+
if tts_provider in ("none", "skip", "disabled", "off"):
|
|
109
|
+
print(f"[{name}] Voice disabled")
|
|
110
|
+
self._voice = None
|
|
111
|
+
return
|
|
102
112
|
|
|
103
113
|
# Provider-specific configuration
|
|
104
114
|
if tts_provider == "google":
|
package/core/self.py
CHANGED
|
@@ -52,7 +52,7 @@ class Self:
|
|
|
52
52
|
# Default Mode Network (background idle processing)
|
|
53
53
|
self._default_mode = None
|
|
54
54
|
|
|
55
|
-
async def start(self):
|
|
55
|
+
async def start(self, input_channel: str = "telegram"):
|
|
56
56
|
"""Start the AI system"""
|
|
57
57
|
# Set the active settings path for this async context
|
|
58
58
|
settings_path = self.base / "config" / "settings.json"
|
|
@@ -71,7 +71,7 @@ class Self:
|
|
|
71
71
|
name = self.config.identity.get("name", "AI")
|
|
72
72
|
|
|
73
73
|
# Load modules via initialization module
|
|
74
|
-
await load_modules(self)
|
|
74
|
+
await load_modules(self, input_channel=input_channel)
|
|
75
75
|
|
|
76
76
|
# Init Subconscious
|
|
77
77
|
from brain.subconscious import SubconsciousLoop
|
package/docs/assets/logo.svg
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
2
|
-
<title id="title">Alive-AI
|
|
3
|
-
<desc id="desc">A
|
|
4
|
-
<rect width="
|
|
5
|
-
<
|
|
6
|
-
<path d="
|
|
7
|
-
<
|
|
8
|
-
<circle cx="256" cy="256" r="57" fill="#101820" stroke="#f5f7fb" stroke-width="10"/>
|
|
9
|
-
<circle cx="256" cy="256" r="15" fill="#41f0a1"/>
|
|
10
|
-
<circle cx="216" cy="224" r="10" fill="#ffcf5a"/>
|
|
11
|
-
<circle cx="301" cy="223" r="10" fill="#ff5c8a"/>
|
|
12
|
-
<circle cx="221" cy="295" r="10" fill="#f5f7fb"/>
|
|
13
|
-
<circle cx="300" cy="294" r="10" fill="#41f0a1"/>
|
|
14
|
-
<path d="M226 229l30 27 45-31M256 256l-35 39M256 256l44 38" fill="none" stroke="#f5f7fb" stroke-width="7" stroke-linecap="round"/>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Alive-AI</title>
|
|
3
|
+
<desc id="desc">A heart with a live pulse line.</desc>
|
|
4
|
+
<rect width="96" height="96" rx="22" fill="#070a0f"/>
|
|
5
|
+
<path d="M48 76S19 59 19 35c0-10 8-18 18-18 6 0 11 3 14 8 3-5 8-8 14-8 10 0 18 8 18 18 0 24-35 41-35 41Z" fill="#ff5c8a"/>
|
|
6
|
+
<path d="M20 50h15l7-17 11 30 8-23h15" fill="none" stroke="#41f0a1" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
|
|
7
|
+
<circle cx="48" cy="48" r="34" fill="none" stroke="#253445" stroke-width="4"/>
|
|
15
8
|
</svg>
|