realtimex-deeptutor 0.5.0.post2__py3-none-any.whl → 0.5.0.post3__py3-none-any.whl
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.
- {realtimex_deeptutor-0.5.0.post2.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/METADATA +1 -1
- {realtimex_deeptutor-0.5.0.post2.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/RECORD +17 -8
- {realtimex_deeptutor-0.5.0.post2.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/top_level.txt +1 -0
- scripts/__init__.py +1 -0
- scripts/audit_prompts.py +179 -0
- scripts/check_install.py +460 -0
- scripts/generate_roster.py +327 -0
- scripts/install_all.py +653 -0
- scripts/migrate_kb.py +655 -0
- scripts/start.py +807 -0
- scripts/start_web.py +632 -0
- scripts/sync_prompts_from_en.py +147 -0
- src/cli/start.py +58 -66
- src/services/config/unified_config.py +2 -2
- {realtimex_deeptutor-0.5.0.post2.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/WHEEL +0 -0
- {realtimex_deeptutor-0.5.0.post2.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/entry_points.txt +0 -0
- {realtimex_deeptutor-0.5.0.post2.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/licenses/LICENSE +0 -0
scripts/check_install.py
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
DeepTutor Installation Checker
|
|
4
|
+
|
|
5
|
+
Verifies that all required dependencies are correctly installed.
|
|
6
|
+
Run this script to diagnose installation issues.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import importlib.metadata
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
# Set Windows console UTF-8 encoding
|
|
17
|
+
if sys.platform == "win32":
|
|
18
|
+
import io
|
|
19
|
+
|
|
20
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
|
21
|
+
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Color codes for terminal output
|
|
25
|
+
class Colors:
|
|
26
|
+
RED = "\033[0;31m"
|
|
27
|
+
GREEN = "\033[0;32m"
|
|
28
|
+
YELLOW = "\033[1;33m"
|
|
29
|
+
BLUE = "\033[0;34m"
|
|
30
|
+
CYAN = "\033[0;36m"
|
|
31
|
+
NC = "\033[0m" # No Color
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def disable(cls):
|
|
35
|
+
"""Disable colors for non-TTY output"""
|
|
36
|
+
cls.RED = cls.GREEN = cls.YELLOW = cls.BLUE = cls.CYAN = cls.NC = ""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Disable colors if not a TTY
|
|
40
|
+
if not sys.stdout.isatty():
|
|
41
|
+
Colors.disable()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def print_header(message: str):
|
|
45
|
+
"""Print section header"""
|
|
46
|
+
print(f"\n{'=' * 60}")
|
|
47
|
+
print(f"📋 {message}")
|
|
48
|
+
print("=" * 60)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def print_success(message: str):
|
|
52
|
+
"""Print success message"""
|
|
53
|
+
print(f"{Colors.GREEN}✅ {message}{Colors.NC}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def print_error(message: str):
|
|
57
|
+
"""Print error message"""
|
|
58
|
+
print(f"{Colors.RED}❌ {message}{Colors.NC}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def print_warning(message: str):
|
|
62
|
+
"""Print warning message"""
|
|
63
|
+
print(f"{Colors.YELLOW}⚠️ {message}{Colors.NC}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def print_info(message: str):
|
|
67
|
+
"""Print info message"""
|
|
68
|
+
print(f"{Colors.CYAN}ℹ️ {message}{Colors.NC}")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_package_version(package_name: str) -> str | None:
|
|
72
|
+
"""Get installed package version"""
|
|
73
|
+
try:
|
|
74
|
+
return importlib.metadata.version(package_name)
|
|
75
|
+
except importlib.metadata.PackageNotFoundError:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def check_python_environment() -> bool:
|
|
80
|
+
"""Check Python environment"""
|
|
81
|
+
print_header("Python Environment")
|
|
82
|
+
|
|
83
|
+
all_ok = True
|
|
84
|
+
|
|
85
|
+
# Python version
|
|
86
|
+
py_version = sys.version_info
|
|
87
|
+
print_info(f"Python version: {py_version.major}.{py_version.minor}.{py_version.micro}")
|
|
88
|
+
|
|
89
|
+
if py_version >= (3, 10):
|
|
90
|
+
print_success(f"Python {py_version.major}.{py_version.minor} meets requirement (>=3.10)")
|
|
91
|
+
else:
|
|
92
|
+
print_error(f"Python {py_version.major}.{py_version.minor} is below requirement (>=3.10)")
|
|
93
|
+
all_ok = False
|
|
94
|
+
|
|
95
|
+
# Python executable
|
|
96
|
+
print_info(f"Python executable: {sys.executable}")
|
|
97
|
+
|
|
98
|
+
# Virtual environment
|
|
99
|
+
in_venv = hasattr(sys, "real_prefix") or (
|
|
100
|
+
hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
|
|
101
|
+
)
|
|
102
|
+
conda_env = os.environ.get("CONDA_DEFAULT_ENV")
|
|
103
|
+
|
|
104
|
+
if conda_env:
|
|
105
|
+
print_success(f"Conda environment: {conda_env}")
|
|
106
|
+
elif in_venv:
|
|
107
|
+
print_success(f"Virtual environment: {sys.prefix}")
|
|
108
|
+
else:
|
|
109
|
+
print_warning("No isolated environment detected (recommended: use conda or venv)")
|
|
110
|
+
|
|
111
|
+
return all_ok
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def check_backend_packages() -> tuple[bool, int, int]:
|
|
115
|
+
"""Check backend Python packages"""
|
|
116
|
+
print_header("Backend Dependencies")
|
|
117
|
+
|
|
118
|
+
# Required packages: (pip_name, import_name, min_version, is_optional)
|
|
119
|
+
packages = [
|
|
120
|
+
# Core
|
|
121
|
+
("python-dotenv", "dotenv", "1.0.0", False),
|
|
122
|
+
("PyYAML", "yaml", "6.0", False),
|
|
123
|
+
("tiktoken", "tiktoken", "0.5.0", False),
|
|
124
|
+
("jinja2", "jinja2", "3.1.0", False),
|
|
125
|
+
# HTTP & API
|
|
126
|
+
("requests", "requests", "2.32.0", False),
|
|
127
|
+
("openai", "openai", "1.30.0", False),
|
|
128
|
+
("anthropic", "anthropic", "0.30.0", False),
|
|
129
|
+
("aiohttp", "aiohttp", "3.9.0", False),
|
|
130
|
+
("httpx", "httpx", "0.27.0", False),
|
|
131
|
+
# Async
|
|
132
|
+
("nest-asyncio", "nest_asyncio", "1.5.8", False),
|
|
133
|
+
("tenacity", "tenacity", "8.0.0", False),
|
|
134
|
+
# Web framework
|
|
135
|
+
("fastapi", "fastapi", "0.100.0", False),
|
|
136
|
+
("uvicorn", "uvicorn", "0.24.0", False),
|
|
137
|
+
("websockets", "websockets", "12.0", False),
|
|
138
|
+
("pydantic", "pydantic", "2.0.0", False),
|
|
139
|
+
# RAG
|
|
140
|
+
("lightrag-hku", "lightrag", "1.0.0", False),
|
|
141
|
+
("raganything", "raganything", "0.1.0", False),
|
|
142
|
+
("llama-index", "llama_index", "0.14.0", False),
|
|
143
|
+
# Document parsing
|
|
144
|
+
("docling", "docling", "2.31.0", True), # Optional
|
|
145
|
+
("PyMuPDF", "fitz", "1.26.0", False),
|
|
146
|
+
# Academic
|
|
147
|
+
("arxiv", "arxiv", "2.0.0", False),
|
|
148
|
+
# Optional API clients
|
|
149
|
+
("perplexityai", "perplexity", "0.1.0", True),
|
|
150
|
+
("dashscope", "dashscope", "1.14.0", True),
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
installed = 0
|
|
154
|
+
missing = 0
|
|
155
|
+
all_ok = True
|
|
156
|
+
|
|
157
|
+
for pip_name, import_name, min_version, is_optional in packages:
|
|
158
|
+
try:
|
|
159
|
+
__import__(import_name)
|
|
160
|
+
version = get_package_version(pip_name)
|
|
161
|
+
version_str = f" (v{version})" if version else ""
|
|
162
|
+
print_success(f" ✓ {pip_name}{version_str}")
|
|
163
|
+
installed += 1
|
|
164
|
+
except ImportError:
|
|
165
|
+
if is_optional:
|
|
166
|
+
print_warning(f" ⚠ {pip_name} not installed (optional)")
|
|
167
|
+
else:
|
|
168
|
+
print_error(f" ✗ {pip_name} not installed")
|
|
169
|
+
all_ok = False
|
|
170
|
+
missing += 1
|
|
171
|
+
|
|
172
|
+
return all_ok, installed, missing
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def check_frontend_packages(project_root: Path) -> tuple[bool, int, int]:
|
|
176
|
+
"""Check frontend Node.js packages"""
|
|
177
|
+
print_header("Frontend Dependencies")
|
|
178
|
+
|
|
179
|
+
web_dir = project_root / "web"
|
|
180
|
+
node_modules = web_dir / "node_modules"
|
|
181
|
+
package_json = web_dir / "package.json"
|
|
182
|
+
|
|
183
|
+
installed = 0
|
|
184
|
+
missing = 0
|
|
185
|
+
all_ok = True
|
|
186
|
+
|
|
187
|
+
# Check npm
|
|
188
|
+
npm_path = shutil.which("npm")
|
|
189
|
+
if npm_path:
|
|
190
|
+
try:
|
|
191
|
+
result = subprocess.run(
|
|
192
|
+
["npm", "--version"],
|
|
193
|
+
capture_output=True,
|
|
194
|
+
text=True,
|
|
195
|
+
timeout=10,
|
|
196
|
+
)
|
|
197
|
+
npm_version = result.stdout.strip()
|
|
198
|
+
print_success(f"npm available (v{npm_version})")
|
|
199
|
+
except Exception:
|
|
200
|
+
print_warning("npm found but version check failed")
|
|
201
|
+
else:
|
|
202
|
+
print_error("npm not found - Node.js is required for frontend")
|
|
203
|
+
all_ok = False
|
|
204
|
+
|
|
205
|
+
# Check node
|
|
206
|
+
node_path = shutil.which("node")
|
|
207
|
+
if node_path:
|
|
208
|
+
try:
|
|
209
|
+
result = subprocess.run(
|
|
210
|
+
["node", "--version"],
|
|
211
|
+
capture_output=True,
|
|
212
|
+
text=True,
|
|
213
|
+
timeout=10,
|
|
214
|
+
)
|
|
215
|
+
node_version = result.stdout.strip()
|
|
216
|
+
print_success(f"Node.js available ({node_version})")
|
|
217
|
+
except Exception:
|
|
218
|
+
print_warning("Node.js found but version check failed")
|
|
219
|
+
else:
|
|
220
|
+
print_error("Node.js not found")
|
|
221
|
+
all_ok = False
|
|
222
|
+
|
|
223
|
+
# Check package.json exists
|
|
224
|
+
if not package_json.exists():
|
|
225
|
+
print_error(f"package.json not found: {package_json}")
|
|
226
|
+
return False, 0, 0
|
|
227
|
+
|
|
228
|
+
# Check node_modules
|
|
229
|
+
if not node_modules.exists():
|
|
230
|
+
print_error("node_modules directory does not exist")
|
|
231
|
+
print_info("Run 'npm install' in web/ directory to install dependencies")
|
|
232
|
+
return False, 0, 0
|
|
233
|
+
|
|
234
|
+
# Check key packages
|
|
235
|
+
key_packages = [
|
|
236
|
+
"next",
|
|
237
|
+
"react",
|
|
238
|
+
"react-dom",
|
|
239
|
+
"typescript",
|
|
240
|
+
"tailwindcss",
|
|
241
|
+
"@radix-ui/react-dialog",
|
|
242
|
+
"lucide-react",
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
print_info("Checking key frontend packages...")
|
|
246
|
+
|
|
247
|
+
for pkg in key_packages:
|
|
248
|
+
pkg_dir = node_modules / pkg
|
|
249
|
+
if pkg_dir.exists():
|
|
250
|
+
# Try to get version from package.json
|
|
251
|
+
pkg_json = pkg_dir / "package.json"
|
|
252
|
+
version = ""
|
|
253
|
+
if pkg_json.exists():
|
|
254
|
+
try:
|
|
255
|
+
import json
|
|
256
|
+
|
|
257
|
+
with open(pkg_json) as f:
|
|
258
|
+
data = json.load(f)
|
|
259
|
+
version = f" (v{data.get('version', '?')})"
|
|
260
|
+
except Exception:
|
|
261
|
+
pass
|
|
262
|
+
print_success(f" ✓ {pkg}{version}")
|
|
263
|
+
installed += 1
|
|
264
|
+
else:
|
|
265
|
+
print_error(f" ✗ {pkg} not installed")
|
|
266
|
+
missing += 1
|
|
267
|
+
all_ok = False
|
|
268
|
+
|
|
269
|
+
return all_ok, installed, missing
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def check_system_tools() -> bool:
|
|
273
|
+
"""Check system tools and utilities"""
|
|
274
|
+
print_header("System Tools")
|
|
275
|
+
|
|
276
|
+
all_ok = True
|
|
277
|
+
|
|
278
|
+
# Git
|
|
279
|
+
git_path = shutil.which("git")
|
|
280
|
+
if git_path:
|
|
281
|
+
try:
|
|
282
|
+
result = subprocess.run(
|
|
283
|
+
["git", "--version"],
|
|
284
|
+
capture_output=True,
|
|
285
|
+
text=True,
|
|
286
|
+
timeout=10,
|
|
287
|
+
)
|
|
288
|
+
git_version = result.stdout.strip()
|
|
289
|
+
print_success(f"{git_version}")
|
|
290
|
+
except Exception:
|
|
291
|
+
print_warning("git found but version check failed")
|
|
292
|
+
else:
|
|
293
|
+
print_warning("git not found (optional but recommended)")
|
|
294
|
+
|
|
295
|
+
# uv (fast Python package manager)
|
|
296
|
+
uv_path = shutil.which("uv")
|
|
297
|
+
if uv_path:
|
|
298
|
+
try:
|
|
299
|
+
result = subprocess.run(
|
|
300
|
+
["uv", "--version"],
|
|
301
|
+
capture_output=True,
|
|
302
|
+
text=True,
|
|
303
|
+
timeout=10,
|
|
304
|
+
)
|
|
305
|
+
uv_version = result.stdout.strip()
|
|
306
|
+
print_success(f"uv available ({uv_version})")
|
|
307
|
+
except Exception:
|
|
308
|
+
print_warning("uv found but version check failed")
|
|
309
|
+
else:
|
|
310
|
+
print_info("uv not found (optional, for faster package installation)")
|
|
311
|
+
|
|
312
|
+
return all_ok
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def check_env_file(project_root: Path) -> bool:
|
|
316
|
+
"""Check .env file configuration"""
|
|
317
|
+
print_header("Environment Configuration")
|
|
318
|
+
|
|
319
|
+
env_file = project_root / ".env"
|
|
320
|
+
env_example = project_root / ".env.example"
|
|
321
|
+
|
|
322
|
+
if env_file.exists():
|
|
323
|
+
print_success(".env file exists")
|
|
324
|
+
|
|
325
|
+
# Check for required keys (without revealing values)
|
|
326
|
+
required_keys = [
|
|
327
|
+
"OPENAI_API_KEY",
|
|
328
|
+
]
|
|
329
|
+
optional_keys = [
|
|
330
|
+
"ANTHROPIC_API_KEY",
|
|
331
|
+
"PERPLEXITY_API_KEY",
|
|
332
|
+
"DASHSCOPE_API_KEY",
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
with open(env_file) as f:
|
|
337
|
+
content = f.read()
|
|
338
|
+
lines = content.split("\n")
|
|
339
|
+
env_vars = {}
|
|
340
|
+
for line in lines:
|
|
341
|
+
line = line.strip()
|
|
342
|
+
if line and not line.startswith("#") and "=" in line:
|
|
343
|
+
key = line.split("=")[0].strip()
|
|
344
|
+
value = line.split("=", 1)[1].strip() if "=" in line else ""
|
|
345
|
+
env_vars[key] = value
|
|
346
|
+
|
|
347
|
+
for key in required_keys:
|
|
348
|
+
if key in env_vars and env_vars[key]:
|
|
349
|
+
print_success(f" ✓ {key} is set")
|
|
350
|
+
else:
|
|
351
|
+
print_warning(f" ⚠ {key} is not set (required for most features)")
|
|
352
|
+
|
|
353
|
+
for key in optional_keys:
|
|
354
|
+
if key in env_vars and env_vars[key]:
|
|
355
|
+
print_success(f" ✓ {key} is set")
|
|
356
|
+
else:
|
|
357
|
+
print_info(f" ○ {key} is not set (optional)")
|
|
358
|
+
|
|
359
|
+
except Exception as e:
|
|
360
|
+
print_warning(f"Could not read .env file: {e}")
|
|
361
|
+
|
|
362
|
+
return True
|
|
363
|
+
else:
|
|
364
|
+
print_warning(".env file not found")
|
|
365
|
+
if env_example.exists():
|
|
366
|
+
print_info("Copy .env.example to .env and configure your API keys")
|
|
367
|
+
else:
|
|
368
|
+
print_info("Create a .env file with your API keys (e.g., OPENAI_API_KEY)")
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def main():
|
|
373
|
+
"""Main function"""
|
|
374
|
+
print("\n" + "=" * 60)
|
|
375
|
+
print("🔍 DeepTutor Installation Checker")
|
|
376
|
+
print("=" * 60)
|
|
377
|
+
print("Checking all dependencies and configurations...")
|
|
378
|
+
|
|
379
|
+
# Get project root
|
|
380
|
+
script_dir = Path(__file__).parent
|
|
381
|
+
project_root = script_dir.parent
|
|
382
|
+
|
|
383
|
+
print_info(f"Project root: {project_root}")
|
|
384
|
+
|
|
385
|
+
# Track overall status
|
|
386
|
+
all_checks_passed = True
|
|
387
|
+
summary = []
|
|
388
|
+
|
|
389
|
+
# 1. Python environment
|
|
390
|
+
if check_python_environment():
|
|
391
|
+
summary.append(("Python Environment", "✅ OK"))
|
|
392
|
+
else:
|
|
393
|
+
summary.append(("Python Environment", "❌ Issues found"))
|
|
394
|
+
all_checks_passed = False
|
|
395
|
+
|
|
396
|
+
# 2. Backend packages
|
|
397
|
+
backend_ok, backend_installed, backend_missing = check_backend_packages()
|
|
398
|
+
if backend_ok:
|
|
399
|
+
summary.append(("Backend Dependencies", f"✅ OK ({backend_installed} packages)"))
|
|
400
|
+
else:
|
|
401
|
+
summary.append(
|
|
402
|
+
("Backend Dependencies", f"❌ {backend_missing} missing, {backend_installed} installed")
|
|
403
|
+
)
|
|
404
|
+
all_checks_passed = False
|
|
405
|
+
|
|
406
|
+
# 3. Frontend packages
|
|
407
|
+
frontend_ok, frontend_installed, frontend_missing = check_frontend_packages(project_root)
|
|
408
|
+
if frontend_ok:
|
|
409
|
+
summary.append(("Frontend Dependencies", f"✅ OK ({frontend_installed} packages)"))
|
|
410
|
+
else:
|
|
411
|
+
summary.append(
|
|
412
|
+
(
|
|
413
|
+
"Frontend Dependencies",
|
|
414
|
+
f"❌ {frontend_missing} missing, {frontend_installed} installed",
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
all_checks_passed = False
|
|
418
|
+
|
|
419
|
+
# 4. System tools
|
|
420
|
+
check_system_tools()
|
|
421
|
+
summary.append(("System Tools", "✅ Checked"))
|
|
422
|
+
|
|
423
|
+
# 5. Environment configuration
|
|
424
|
+
if check_env_file(project_root):
|
|
425
|
+
summary.append(("Environment Config", "✅ .env exists"))
|
|
426
|
+
else:
|
|
427
|
+
summary.append(("Environment Config", "⚠️ .env missing"))
|
|
428
|
+
|
|
429
|
+
# Print summary
|
|
430
|
+
print_header("Summary")
|
|
431
|
+
|
|
432
|
+
for item, status in summary:
|
|
433
|
+
print(f" {item}: {status}")
|
|
434
|
+
|
|
435
|
+
print("")
|
|
436
|
+
if all_checks_passed:
|
|
437
|
+
print_success("All required dependencies are installed!")
|
|
438
|
+
print_info("You can start DeepTutor with: python scripts/start_web.py")
|
|
439
|
+
else:
|
|
440
|
+
print_error("Some dependencies are missing!")
|
|
441
|
+
print_info("Run: python scripts/install_all.py")
|
|
442
|
+
print_info("Or manually install missing packages")
|
|
443
|
+
|
|
444
|
+
print("=" * 60 + "\n")
|
|
445
|
+
|
|
446
|
+
return 0 if all_checks_passed else 1
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
if __name__ == "__main__":
|
|
450
|
+
try:
|
|
451
|
+
sys.exit(main())
|
|
452
|
+
except KeyboardInterrupt:
|
|
453
|
+
print("\n\n⚠️ Check interrupted by user")
|
|
454
|
+
sys.exit(1)
|
|
455
|
+
except Exception as e:
|
|
456
|
+
print_error(f"\n❌ Unexpected error: {e}")
|
|
457
|
+
import traceback
|
|
458
|
+
|
|
459
|
+
traceback.print_exc()
|
|
460
|
+
sys.exit(1)
|