realtimex-deeptutor 0.5.0.post1__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.
Files changed (276) hide show
  1. realtimex_deeptutor/__init__.py +67 -0
  2. realtimex_deeptutor-0.5.0.post1.dist-info/METADATA +1612 -0
  3. realtimex_deeptutor-0.5.0.post1.dist-info/RECORD +276 -0
  4. realtimex_deeptutor-0.5.0.post1.dist-info/WHEEL +5 -0
  5. realtimex_deeptutor-0.5.0.post1.dist-info/entry_points.txt +2 -0
  6. realtimex_deeptutor-0.5.0.post1.dist-info/licenses/LICENSE +661 -0
  7. realtimex_deeptutor-0.5.0.post1.dist-info/top_level.txt +2 -0
  8. src/__init__.py +40 -0
  9. src/agents/__init__.py +24 -0
  10. src/agents/base_agent.py +657 -0
  11. src/agents/chat/__init__.py +24 -0
  12. src/agents/chat/chat_agent.py +435 -0
  13. src/agents/chat/prompts/en/chat_agent.yaml +35 -0
  14. src/agents/chat/prompts/zh/chat_agent.yaml +35 -0
  15. src/agents/chat/session_manager.py +311 -0
  16. src/agents/co_writer/__init__.py +0 -0
  17. src/agents/co_writer/edit_agent.py +260 -0
  18. src/agents/co_writer/narrator_agent.py +423 -0
  19. src/agents/co_writer/prompts/en/edit_agent.yaml +113 -0
  20. src/agents/co_writer/prompts/en/narrator_agent.yaml +88 -0
  21. src/agents/co_writer/prompts/zh/edit_agent.yaml +113 -0
  22. src/agents/co_writer/prompts/zh/narrator_agent.yaml +88 -0
  23. src/agents/guide/__init__.py +16 -0
  24. src/agents/guide/agents/__init__.py +11 -0
  25. src/agents/guide/agents/chat_agent.py +104 -0
  26. src/agents/guide/agents/interactive_agent.py +223 -0
  27. src/agents/guide/agents/locate_agent.py +149 -0
  28. src/agents/guide/agents/summary_agent.py +150 -0
  29. src/agents/guide/guide_manager.py +500 -0
  30. src/agents/guide/prompts/en/chat_agent.yaml +41 -0
  31. src/agents/guide/prompts/en/interactive_agent.yaml +202 -0
  32. src/agents/guide/prompts/en/locate_agent.yaml +68 -0
  33. src/agents/guide/prompts/en/summary_agent.yaml +157 -0
  34. src/agents/guide/prompts/zh/chat_agent.yaml +41 -0
  35. src/agents/guide/prompts/zh/interactive_agent.yaml +626 -0
  36. src/agents/guide/prompts/zh/locate_agent.yaml +68 -0
  37. src/agents/guide/prompts/zh/summary_agent.yaml +157 -0
  38. src/agents/ideagen/__init__.py +12 -0
  39. src/agents/ideagen/idea_generation_workflow.py +426 -0
  40. src/agents/ideagen/material_organizer_agent.py +173 -0
  41. src/agents/ideagen/prompts/en/idea_generation.yaml +187 -0
  42. src/agents/ideagen/prompts/en/material_organizer.yaml +69 -0
  43. src/agents/ideagen/prompts/zh/idea_generation.yaml +187 -0
  44. src/agents/ideagen/prompts/zh/material_organizer.yaml +69 -0
  45. src/agents/question/__init__.py +24 -0
  46. src/agents/question/agents/__init__.py +18 -0
  47. src/agents/question/agents/generate_agent.py +381 -0
  48. src/agents/question/agents/relevance_analyzer.py +207 -0
  49. src/agents/question/agents/retrieve_agent.py +239 -0
  50. src/agents/question/coordinator.py +718 -0
  51. src/agents/question/example.py +109 -0
  52. src/agents/question/prompts/en/coordinator.yaml +75 -0
  53. src/agents/question/prompts/en/generate_agent.yaml +77 -0
  54. src/agents/question/prompts/en/relevance_analyzer.yaml +41 -0
  55. src/agents/question/prompts/en/retrieve_agent.yaml +32 -0
  56. src/agents/question/prompts/zh/coordinator.yaml +75 -0
  57. src/agents/question/prompts/zh/generate_agent.yaml +77 -0
  58. src/agents/question/prompts/zh/relevance_analyzer.yaml +39 -0
  59. src/agents/question/prompts/zh/retrieve_agent.yaml +30 -0
  60. src/agents/research/agents/__init__.py +23 -0
  61. src/agents/research/agents/decompose_agent.py +507 -0
  62. src/agents/research/agents/manager_agent.py +228 -0
  63. src/agents/research/agents/note_agent.py +180 -0
  64. src/agents/research/agents/rephrase_agent.py +263 -0
  65. src/agents/research/agents/reporting_agent.py +1333 -0
  66. src/agents/research/agents/research_agent.py +714 -0
  67. src/agents/research/data_structures.py +451 -0
  68. src/agents/research/main.py +188 -0
  69. src/agents/research/prompts/en/decompose_agent.yaml +89 -0
  70. src/agents/research/prompts/en/manager_agent.yaml +24 -0
  71. src/agents/research/prompts/en/note_agent.yaml +121 -0
  72. src/agents/research/prompts/en/rephrase_agent.yaml +58 -0
  73. src/agents/research/prompts/en/reporting_agent.yaml +380 -0
  74. src/agents/research/prompts/en/research_agent.yaml +173 -0
  75. src/agents/research/prompts/zh/decompose_agent.yaml +89 -0
  76. src/agents/research/prompts/zh/manager_agent.yaml +24 -0
  77. src/agents/research/prompts/zh/note_agent.yaml +121 -0
  78. src/agents/research/prompts/zh/rephrase_agent.yaml +58 -0
  79. src/agents/research/prompts/zh/reporting_agent.yaml +380 -0
  80. src/agents/research/prompts/zh/research_agent.yaml +173 -0
  81. src/agents/research/research_pipeline.py +1309 -0
  82. src/agents/research/utils/__init__.py +60 -0
  83. src/agents/research/utils/citation_manager.py +799 -0
  84. src/agents/research/utils/json_utils.py +98 -0
  85. src/agents/research/utils/token_tracker.py +297 -0
  86. src/agents/solve/__init__.py +80 -0
  87. src/agents/solve/analysis_loop/__init__.py +14 -0
  88. src/agents/solve/analysis_loop/investigate_agent.py +414 -0
  89. src/agents/solve/analysis_loop/note_agent.py +190 -0
  90. src/agents/solve/main_solver.py +862 -0
  91. src/agents/solve/memory/__init__.py +34 -0
  92. src/agents/solve/memory/citation_memory.py +353 -0
  93. src/agents/solve/memory/investigate_memory.py +226 -0
  94. src/agents/solve/memory/solve_memory.py +340 -0
  95. src/agents/solve/prompts/en/analysis_loop/investigate_agent.yaml +55 -0
  96. src/agents/solve/prompts/en/analysis_loop/note_agent.yaml +54 -0
  97. src/agents/solve/prompts/en/solve_loop/manager_agent.yaml +67 -0
  98. src/agents/solve/prompts/en/solve_loop/precision_answer_agent.yaml +62 -0
  99. src/agents/solve/prompts/en/solve_loop/response_agent.yaml +90 -0
  100. src/agents/solve/prompts/en/solve_loop/solve_agent.yaml +75 -0
  101. src/agents/solve/prompts/en/solve_loop/tool_agent.yaml +38 -0
  102. src/agents/solve/prompts/zh/analysis_loop/investigate_agent.yaml +53 -0
  103. src/agents/solve/prompts/zh/analysis_loop/note_agent.yaml +54 -0
  104. src/agents/solve/prompts/zh/solve_loop/manager_agent.yaml +66 -0
  105. src/agents/solve/prompts/zh/solve_loop/precision_answer_agent.yaml +62 -0
  106. src/agents/solve/prompts/zh/solve_loop/response_agent.yaml +90 -0
  107. src/agents/solve/prompts/zh/solve_loop/solve_agent.yaml +76 -0
  108. src/agents/solve/prompts/zh/solve_loop/tool_agent.yaml +41 -0
  109. src/agents/solve/solve_loop/__init__.py +22 -0
  110. src/agents/solve/solve_loop/citation_manager.py +74 -0
  111. src/agents/solve/solve_loop/manager_agent.py +274 -0
  112. src/agents/solve/solve_loop/precision_answer_agent.py +96 -0
  113. src/agents/solve/solve_loop/response_agent.py +301 -0
  114. src/agents/solve/solve_loop/solve_agent.py +325 -0
  115. src/agents/solve/solve_loop/tool_agent.py +470 -0
  116. src/agents/solve/utils/__init__.py +64 -0
  117. src/agents/solve/utils/config_validator.py +313 -0
  118. src/agents/solve/utils/display_manager.py +223 -0
  119. src/agents/solve/utils/error_handler.py +363 -0
  120. src/agents/solve/utils/json_utils.py +98 -0
  121. src/agents/solve/utils/performance_monitor.py +407 -0
  122. src/agents/solve/utils/token_tracker.py +541 -0
  123. src/api/__init__.py +0 -0
  124. src/api/main.py +240 -0
  125. src/api/routers/__init__.py +1 -0
  126. src/api/routers/agent_config.py +69 -0
  127. src/api/routers/chat.py +296 -0
  128. src/api/routers/co_writer.py +337 -0
  129. src/api/routers/config.py +627 -0
  130. src/api/routers/dashboard.py +18 -0
  131. src/api/routers/guide.py +337 -0
  132. src/api/routers/ideagen.py +436 -0
  133. src/api/routers/knowledge.py +821 -0
  134. src/api/routers/notebook.py +247 -0
  135. src/api/routers/question.py +537 -0
  136. src/api/routers/research.py +394 -0
  137. src/api/routers/settings.py +164 -0
  138. src/api/routers/solve.py +305 -0
  139. src/api/routers/system.py +252 -0
  140. src/api/run_server.py +61 -0
  141. src/api/utils/history.py +172 -0
  142. src/api/utils/log_interceptor.py +21 -0
  143. src/api/utils/notebook_manager.py +415 -0
  144. src/api/utils/progress_broadcaster.py +72 -0
  145. src/api/utils/task_id_manager.py +100 -0
  146. src/config/__init__.py +0 -0
  147. src/config/accessors.py +18 -0
  148. src/config/constants.py +34 -0
  149. src/config/defaults.py +18 -0
  150. src/config/schema.py +38 -0
  151. src/config/settings.py +50 -0
  152. src/core/errors.py +62 -0
  153. src/knowledge/__init__.py +23 -0
  154. src/knowledge/add_documents.py +606 -0
  155. src/knowledge/config.py +65 -0
  156. src/knowledge/example_add_documents.py +236 -0
  157. src/knowledge/extract_numbered_items.py +1039 -0
  158. src/knowledge/initializer.py +621 -0
  159. src/knowledge/kb.py +22 -0
  160. src/knowledge/manager.py +782 -0
  161. src/knowledge/progress_tracker.py +182 -0
  162. src/knowledge/start_kb.py +535 -0
  163. src/logging/__init__.py +103 -0
  164. src/logging/adapters/__init__.py +17 -0
  165. src/logging/adapters/lightrag.py +184 -0
  166. src/logging/adapters/llamaindex.py +141 -0
  167. src/logging/config.py +80 -0
  168. src/logging/handlers/__init__.py +20 -0
  169. src/logging/handlers/console.py +75 -0
  170. src/logging/handlers/file.py +201 -0
  171. src/logging/handlers/websocket.py +127 -0
  172. src/logging/logger.py +709 -0
  173. src/logging/stats/__init__.py +16 -0
  174. src/logging/stats/llm_stats.py +179 -0
  175. src/services/__init__.py +56 -0
  176. src/services/config/__init__.py +61 -0
  177. src/services/config/knowledge_base_config.py +210 -0
  178. src/services/config/loader.py +260 -0
  179. src/services/config/unified_config.py +603 -0
  180. src/services/embedding/__init__.py +45 -0
  181. src/services/embedding/adapters/__init__.py +22 -0
  182. src/services/embedding/adapters/base.py +106 -0
  183. src/services/embedding/adapters/cohere.py +127 -0
  184. src/services/embedding/adapters/jina.py +99 -0
  185. src/services/embedding/adapters/ollama.py +116 -0
  186. src/services/embedding/adapters/openai_compatible.py +96 -0
  187. src/services/embedding/client.py +159 -0
  188. src/services/embedding/config.py +156 -0
  189. src/services/embedding/provider.py +119 -0
  190. src/services/llm/__init__.py +152 -0
  191. src/services/llm/capabilities.py +313 -0
  192. src/services/llm/client.py +302 -0
  193. src/services/llm/cloud_provider.py +530 -0
  194. src/services/llm/config.py +200 -0
  195. src/services/llm/error_mapping.py +103 -0
  196. src/services/llm/exceptions.py +152 -0
  197. src/services/llm/factory.py +450 -0
  198. src/services/llm/local_provider.py +347 -0
  199. src/services/llm/providers/anthropic.py +95 -0
  200. src/services/llm/providers/base_provider.py +93 -0
  201. src/services/llm/providers/open_ai.py +83 -0
  202. src/services/llm/registry.py +71 -0
  203. src/services/llm/telemetry.py +40 -0
  204. src/services/llm/types.py +27 -0
  205. src/services/llm/utils.py +333 -0
  206. src/services/prompt/__init__.py +25 -0
  207. src/services/prompt/manager.py +206 -0
  208. src/services/rag/__init__.py +64 -0
  209. src/services/rag/components/__init__.py +29 -0
  210. src/services/rag/components/base.py +59 -0
  211. src/services/rag/components/chunkers/__init__.py +18 -0
  212. src/services/rag/components/chunkers/base.py +34 -0
  213. src/services/rag/components/chunkers/fixed.py +71 -0
  214. src/services/rag/components/chunkers/numbered_item.py +94 -0
  215. src/services/rag/components/chunkers/semantic.py +97 -0
  216. src/services/rag/components/embedders/__init__.py +14 -0
  217. src/services/rag/components/embedders/base.py +32 -0
  218. src/services/rag/components/embedders/openai.py +63 -0
  219. src/services/rag/components/indexers/__init__.py +18 -0
  220. src/services/rag/components/indexers/base.py +35 -0
  221. src/services/rag/components/indexers/graph.py +172 -0
  222. src/services/rag/components/indexers/lightrag.py +156 -0
  223. src/services/rag/components/indexers/vector.py +146 -0
  224. src/services/rag/components/parsers/__init__.py +18 -0
  225. src/services/rag/components/parsers/base.py +35 -0
  226. src/services/rag/components/parsers/markdown.py +52 -0
  227. src/services/rag/components/parsers/pdf.py +115 -0
  228. src/services/rag/components/parsers/text.py +86 -0
  229. src/services/rag/components/retrievers/__init__.py +18 -0
  230. src/services/rag/components/retrievers/base.py +34 -0
  231. src/services/rag/components/retrievers/dense.py +200 -0
  232. src/services/rag/components/retrievers/hybrid.py +164 -0
  233. src/services/rag/components/retrievers/lightrag.py +169 -0
  234. src/services/rag/components/routing.py +286 -0
  235. src/services/rag/factory.py +234 -0
  236. src/services/rag/pipeline.py +215 -0
  237. src/services/rag/pipelines/__init__.py +32 -0
  238. src/services/rag/pipelines/academic.py +44 -0
  239. src/services/rag/pipelines/lightrag.py +43 -0
  240. src/services/rag/pipelines/llamaindex.py +313 -0
  241. src/services/rag/pipelines/raganything.py +384 -0
  242. src/services/rag/service.py +244 -0
  243. src/services/rag/types.py +73 -0
  244. src/services/search/__init__.py +284 -0
  245. src/services/search/base.py +87 -0
  246. src/services/search/consolidation.py +398 -0
  247. src/services/search/providers/__init__.py +128 -0
  248. src/services/search/providers/baidu.py +188 -0
  249. src/services/search/providers/exa.py +194 -0
  250. src/services/search/providers/jina.py +161 -0
  251. src/services/search/providers/perplexity.py +153 -0
  252. src/services/search/providers/serper.py +209 -0
  253. src/services/search/providers/tavily.py +161 -0
  254. src/services/search/types.py +114 -0
  255. src/services/setup/__init__.py +34 -0
  256. src/services/setup/init.py +285 -0
  257. src/services/tts/__init__.py +16 -0
  258. src/services/tts/config.py +99 -0
  259. src/tools/__init__.py +91 -0
  260. src/tools/code_executor.py +536 -0
  261. src/tools/paper_search_tool.py +171 -0
  262. src/tools/query_item_tool.py +310 -0
  263. src/tools/question/__init__.py +15 -0
  264. src/tools/question/exam_mimic.py +616 -0
  265. src/tools/question/pdf_parser.py +211 -0
  266. src/tools/question/question_extractor.py +397 -0
  267. src/tools/rag_tool.py +173 -0
  268. src/tools/tex_chunker.py +339 -0
  269. src/tools/tex_downloader.py +253 -0
  270. src/tools/web_search.py +71 -0
  271. src/utils/config_manager.py +206 -0
  272. src/utils/document_validator.py +168 -0
  273. src/utils/error_rate_tracker.py +111 -0
  274. src/utils/error_utils.py +82 -0
  275. src/utils/json_parser.py +110 -0
  276. src/utils/network/circuit_breaker.py +79 -0
@@ -0,0 +1,100 @@
1
+ """
2
+ Task ID Manager - Assigns unique IDs to each background task
3
+ """
4
+
5
+ from datetime import datetime, timedelta
6
+ import threading
7
+ from typing import Optional
8
+ import uuid
9
+
10
+
11
+ class TaskIDManager:
12
+ """Singleton class for managing task IDs"""
13
+
14
+ _instance: Optional["TaskIDManager"] = None
15
+ _lock = threading.Lock()
16
+ _task_ids: dict[str, str] = {} # task_key -> task_id
17
+ _task_metadata: dict[str, dict] = {} # task_id -> metadata
18
+
19
+ @classmethod
20
+ def get_instance(cls) -> "TaskIDManager":
21
+ """Get singleton instance"""
22
+ if cls._instance is None:
23
+ with cls._lock:
24
+ if cls._instance is None:
25
+ cls._instance = cls()
26
+ return cls._instance
27
+
28
+ def generate_task_id(self, task_type: str, task_key: str) -> str:
29
+ """
30
+ Generate unique ID for task
31
+
32
+ Args:
33
+ task_type: Task type (e.g., 'kb_init', 'kb_upload', 'question_gen', 'solve', 'research')
34
+ task_key: Task unique identifier (e.g., knowledge base name, question ID, etc.)
35
+
36
+ Returns:
37
+ Task ID (format: {task_type}_{timestamp}_{uuid})
38
+ """
39
+ with self._lock:
40
+ # If task already exists, return existing ID
41
+ if task_key in self._task_ids:
42
+ return self._task_ids[task_key]
43
+
44
+ # Generate new ID
45
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
46
+ unique_id = str(uuid.uuid4())[:8]
47
+ task_id = f"{task_type}_{timestamp}_{unique_id}"
48
+
49
+ # Save mapping and metadata
50
+ self._task_ids[task_key] = task_id
51
+ self._task_metadata[task_id] = {
52
+ "task_type": task_type,
53
+ "task_key": task_key,
54
+ "created_at": datetime.now().isoformat(),
55
+ "status": "running",
56
+ }
57
+
58
+ return task_id
59
+
60
+ def get_task_id(self, task_key: str) -> str | None:
61
+ """Get task ID"""
62
+ with self._lock:
63
+ return self._task_ids.get(task_key)
64
+
65
+ def update_task_status(self, task_id: str, status: str, **kwargs):
66
+ """Update task status"""
67
+ with self._lock:
68
+ if task_id in self._task_metadata:
69
+ self._task_metadata[task_id]["status"] = status
70
+ self._task_metadata[task_id].update(kwargs)
71
+ if status in ["completed", "error", "cancelled"]:
72
+ self._task_metadata[task_id]["finished_at"] = datetime.now().isoformat()
73
+
74
+ def get_task_metadata(self, task_id: str) -> dict | None:
75
+ """Get task metadata"""
76
+ with self._lock:
77
+ return self._task_metadata.get(task_id, {}).copy()
78
+
79
+ def cleanup_old_tasks(self, max_age_hours: int = 24):
80
+ """Clean up old tasks (completed tasks older than specified hours)"""
81
+ with self._lock:
82
+ cutoff = datetime.now() - timedelta(hours=max_age_hours)
83
+
84
+ to_remove = []
85
+ for task_id, metadata in self._task_metadata.items():
86
+ if metadata.get("status") in ["completed", "error", "cancelled"]:
87
+ finished_at = metadata.get("finished_at")
88
+ if finished_at:
89
+ try:
90
+ finished_time = datetime.fromisoformat(finished_at)
91
+ if finished_time < cutoff:
92
+ to_remove.append(task_id)
93
+ except:
94
+ pass
95
+
96
+ for task_id in to_remove:
97
+ metadata = self._task_metadata.pop(task_id, {})
98
+ task_key = metadata.get("task_key")
99
+ if task_key:
100
+ self._task_ids.pop(task_key, None)
src/config/__init__.py ADDED
File without changes
@@ -0,0 +1,18 @@
1
+ from typing import Callable
2
+
3
+
4
+ class ConfigAccessor:
5
+ def __init__(self, loader: Callable[[], dict]):
6
+ self._loader = loader
7
+
8
+ def llm_model(self) -> str:
9
+ cfg = self._loader()
10
+ return str(cfg.get("llm", {}).get("model", "Pro/Flash"))
11
+
12
+ def llm_provider(self) -> str:
13
+ cfg = self._loader()
14
+ return str(cfg.get("llm", {}).get("provider", "openai"))
15
+
16
+ def user_data_dir(self) -> str:
17
+ cfg = self._loader()
18
+ return str(cfg.get("paths", {}).get("user_data_dir", "./data/user"))
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Constants for DeepTutor
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ # Project root directory - central location for all path calculations
10
+ PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
11
+
12
+ # Valid tools for investigate agent
13
+ VALID_INVESTIGATE_TOOLS = ["rag_naive", "rag_hybrid", "web_search", "query_item", "none"]
14
+
15
+ # Valid tools for solve agent
16
+ VALID_SOLVE_TOOLS = [
17
+ "web_search",
18
+ "code_execution",
19
+ "rag_naive",
20
+ "rag_hybrid",
21
+ "query_item",
22
+ "none",
23
+ "finish",
24
+ ]
25
+
26
+ # Logging symbols for different log levels
27
+ LOG_SYMBOLS = {
28
+ "DEBUG": "·",
29
+ "INFO": "●",
30
+ "SUCCESS": "✓",
31
+ "WARNING": "⚠",
32
+ "ERROR": "✗",
33
+ "CRITICAL": "✗",
34
+ }
src/config/defaults.py ADDED
@@ -0,0 +1,18 @@
1
+ """
2
+ Default configuration values for DeepTutor.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ # Get project root
8
+ _project_root = Path(__file__).parent.parent.parent
9
+
10
+ # Default configuration
11
+ DEFAULTS = {
12
+ "llm": {"model": "gpt-4o-mini", "provider": "openai"},
13
+ "paths": {
14
+ "user_data_dir": str(_project_root / "data" / "user"),
15
+ "knowledge_bases_dir": str(_project_root / "data" / "knowledge_bases"),
16
+ "user_log_dir": str(_project_root / "logs"),
17
+ },
18
+ }
src/config/schema.py ADDED
@@ -0,0 +1,38 @@
1
+ from typing import Any, Dict
2
+
3
+ from pydantic import BaseModel, field_validator
4
+
5
+
6
+ class LLMConfig(BaseModel):
7
+ model: str
8
+ provider: str = "openai"
9
+
10
+
11
+ class PathsConfig(BaseModel):
12
+ user_data_dir: str
13
+ knowledge_bases_dir: str
14
+ user_log_dir: str
15
+
16
+
17
+ class AppConfig(BaseModel):
18
+ llm: LLMConfig
19
+ paths: PathsConfig
20
+
21
+ @field_validator("llm", mode="before")
22
+ @classmethod
23
+ def ensure_llm(cls, v: Any) -> Dict[str, Any]:
24
+ if not isinstance(v, dict):
25
+ raise ValueError("llm section must be a mapping")
26
+ if "model" not in v:
27
+ raise ValueError("llm.model is required")
28
+ return v
29
+
30
+
31
+ CURRENT_SCHEMA_VERSION = 1
32
+
33
+
34
+ def migrate_config(cfg: Dict[str, Any]) -> Dict[str, Any]:
35
+ """
36
+ No-op migration for now; placeholder for future versioned changes.
37
+ """
38
+ return cfg
src/config/settings.py ADDED
@@ -0,0 +1,50 @@
1
+ """
2
+ Configuration Settings for DeepTutor
3
+
4
+ Environment Variables:
5
+ LLM_RETRY__MAX_RETRIES: Maximum retry attempts for LLM calls (default: 3)
6
+ LLM_RETRY__BASE_DELAY: Base delay between retries in seconds (default: 1.0)
7
+ LLM_RETRY__EXPONENTIAL_BACKOFF: Whether to use exponential backoff (default: True)
8
+
9
+ Examples:
10
+ export LLM_RETRY__MAX_RETRIES=5
11
+ export LLM_RETRY__BASE_DELAY=2.0
12
+ export LLM_RETRY__EXPONENTIAL_BACKOFF=false
13
+ """
14
+
15
+ from pydantic import BaseModel, Field
16
+ from pydantic_settings import BaseSettings, SettingsConfigDict
17
+
18
+
19
+ class LLMRetryConfig(BaseModel):
20
+ max_retries: int = Field(default=3, description="Maximum retry attempts for LLM calls")
21
+ base_delay: float = Field(default=1.0, description="Base delay between retries in seconds")
22
+ exponential_backoff: bool = Field(
23
+ default=True, description="Whether to use exponential backoff"
24
+ )
25
+
26
+
27
+ class Settings(BaseSettings):
28
+ # LLM retry configuration
29
+ retry: LLMRetryConfig = Field(default_factory=LLMRetryConfig)
30
+
31
+ # Deprecated: use retry instead
32
+ @property
33
+ def llm_retry(self):
34
+ import warnings
35
+
36
+ warnings.warn(
37
+ "settings.llm_retry is deprecated, use settings.retry instead",
38
+ DeprecationWarning,
39
+ stacklevel=2,
40
+ )
41
+ return self.retry
42
+
43
+ model_config = SettingsConfigDict(
44
+ env_prefix="LLM_",
45
+ env_nested_delimiter="__",
46
+ )
47
+
48
+
49
+ # Global settings instance
50
+ settings = Settings()
src/core/errors.py ADDED
@@ -0,0 +1,62 @@
1
+ """
2
+ Base exception classes for consistent error handling across the application.
3
+ Provides a standardized way to distinguish between bugs, recoverable errors,
4
+ and configuration issues.
5
+ """
6
+
7
+ from typing import Any, Dict, Optional
8
+
9
+
10
+ class DeepTutorError(Exception):
11
+ """Base class for all application errors in DeepTutor."""
12
+
13
+ def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
14
+ super().__init__(message)
15
+ self.message = message
16
+ self.details = details or {}
17
+
18
+ def __str__(self) -> str:
19
+ if self.details:
20
+ return f"{self.message} (details: {self.details})"
21
+ return self.message
22
+
23
+
24
+ class ConfigurationError(DeepTutorError):
25
+ """Raised when there's a configuration-related error."""
26
+
27
+ pass
28
+
29
+
30
+ class ValidationError(DeepTutorError):
31
+ """Raised when input validation fails."""
32
+
33
+ pass
34
+
35
+
36
+ class ServiceError(DeepTutorError):
37
+ """Base class for service layer errors."""
38
+
39
+ pass
40
+
41
+
42
+ class LLMServiceError(ServiceError):
43
+ """Base class for LLM service-related errors."""
44
+
45
+ pass
46
+
47
+
48
+ class LLMContextError(LLMServiceError):
49
+ """Raised when prompt exceeds model context window."""
50
+
51
+ pass
52
+
53
+
54
+ class EnvironmentConfigError(ConfigurationError):
55
+ """Raised when there's an environment-related configuration error."""
56
+
57
+ pass
58
+
59
+
60
+ # Aliases for backward compatibility
61
+ ConfigError = ConfigurationError
62
+ EnvError = EnvironmentConfigError
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Knowledge Base Initialization Module
4
+
5
+ Includes:
6
+ - init_knowledge_base: Knowledge base initializer
7
+ - add_documents: Incremental document addition (new feature)
8
+ - kb_manager: Knowledge base manager
9
+ - extract_numbered_items: Extract numbered items
10
+ - config: Path configuration
11
+ """
12
+
13
+ from . import config
14
+ from .add_documents import DocumentAdder
15
+ from .initializer import KnowledgeBaseInitializer
16
+ from .manager import KnowledgeBaseManager
17
+
18
+ __all__ = [
19
+ "DocumentAdder",
20
+ "KnowledgeBaseInitializer",
21
+ "KnowledgeBaseManager",
22
+ "config",
23
+ ]