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,16 @@
1
+ """
2
+ Statistics Tracking
3
+ ===================
4
+
5
+ Utilities for tracking LLM usage, costs, and performance metrics.
6
+ """
7
+
8
+ from .llm_stats import MODEL_PRICING, LLMCall, LLMStats, estimate_tokens, get_pricing
9
+
10
+ __all__ = [
11
+ "LLMStats",
12
+ "LLMCall",
13
+ "get_pricing",
14
+ "estimate_tokens",
15
+ "MODEL_PRICING",
16
+ ]
@@ -0,0 +1,179 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ LLM Stats Tracker
4
+ =================
5
+
6
+ Simple utility for tracking LLM token usage and costs across all modules.
7
+ Outputs summary to terminal at the end of processing.
8
+
9
+ Usage:
10
+ from src.logging import LLMStats
11
+
12
+ stats = LLMStats()
13
+
14
+ # After each LLM call:
15
+ stats.add_call(
16
+ model="gpt-4o-mini",
17
+ prompt_tokens=100,
18
+ completion_tokens=50
19
+ )
20
+
21
+ # At the end:
22
+ stats.print_summary()
23
+ """
24
+
25
+ from dataclasses import dataclass, field
26
+ from datetime import datetime
27
+ from typing import Any, Optional
28
+
29
+ # Model pricing per 1K tokens (USD)
30
+ MODEL_PRICING = {
31
+ "gpt-4o": {"input": 0.0025, "output": 0.010},
32
+ "gpt-4o-mini": {"input": 0.00015, "output": 0.0006},
33
+ "gpt-4-turbo": {"input": 0.01, "output": 0.03},
34
+ "gpt-4": {"input": 0.03, "output": 0.06},
35
+ "gpt-3.5-turbo": {"input": 0.0005, "output": 0.0015},
36
+ "deepseek-chat": {"input": 0.00014, "output": 0.00028},
37
+ "claude-3-5-sonnet": {"input": 0.003, "output": 0.015},
38
+ "claude-3-opus": {"input": 0.015, "output": 0.075},
39
+ "claude-3-haiku": {"input": 0.00025, "output": 0.00125},
40
+ }
41
+
42
+
43
+ def get_pricing(model: str) -> dict[str, float]:
44
+ """Get pricing for a model (fuzzy match)."""
45
+ model_lower = model.lower()
46
+ for key, pricing in MODEL_PRICING.items():
47
+ if key in model_lower or model_lower in key:
48
+ return pricing
49
+ return MODEL_PRICING.get("gpt-4o-mini", {"input": 0.00015, "output": 0.0006})
50
+
51
+
52
+ def estimate_tokens(text: str) -> int:
53
+ """Rough estimate of tokens (1.3 tokens per word)."""
54
+ return int(len(text.split()) * 1.3)
55
+
56
+
57
+ @dataclass
58
+ class LLMCall:
59
+ """Single LLM call record."""
60
+
61
+ model: str
62
+ prompt_tokens: int
63
+ completion_tokens: int
64
+ cost: float
65
+ timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
66
+
67
+
68
+ class LLMStats:
69
+ """
70
+ LLM usage statistics tracker.
71
+ Tracks token usage and costs, outputs summary to terminal.
72
+ """
73
+
74
+ def __init__(self, module_name: str = "Module"):
75
+ """
76
+ Initialize stats tracker.
77
+
78
+ Args:
79
+ module_name: Name of the module (for display)
80
+ """
81
+ self.module_name = module_name
82
+ self.calls: list[LLMCall] = []
83
+ self.total_prompt_tokens = 0
84
+ self.total_completion_tokens = 0
85
+ self.total_cost = 0.0
86
+ self.model_used: Optional[str] = None
87
+
88
+ def add_call(
89
+ self,
90
+ model: str,
91
+ prompt_tokens: Optional[int] = None,
92
+ completion_tokens: Optional[int] = None,
93
+ # Alternative: estimate from text
94
+ system_prompt: Optional[str] = None,
95
+ user_prompt: Optional[str] = None,
96
+ response: Optional[str] = None,
97
+ ):
98
+ """
99
+ Add an LLM call to the stats.
100
+
101
+ Args:
102
+ model: Model name
103
+ prompt_tokens: Number of prompt tokens (if known)
104
+ completion_tokens: Number of completion tokens (if known)
105
+ system_prompt: System prompt text (for estimation)
106
+ user_prompt: User prompt text (for estimation)
107
+ response: Response text (for estimation)
108
+ """
109
+ # Estimate tokens if not provided
110
+ if prompt_tokens is None and (system_prompt or user_prompt):
111
+ prompt_text = (system_prompt or "") + "\n" + (user_prompt or "")
112
+ prompt_tokens = estimate_tokens(prompt_text)
113
+
114
+ if completion_tokens is None and response:
115
+ completion_tokens = estimate_tokens(response)
116
+
117
+ prompt_tokens = prompt_tokens or 0
118
+ completion_tokens = completion_tokens or 0
119
+
120
+ # Calculate cost
121
+ pricing = get_pricing(model)
122
+ cost = (prompt_tokens / 1000.0) * pricing["input"] + (completion_tokens / 1000.0) * pricing[
123
+ "output"
124
+ ]
125
+
126
+ # Record call
127
+ call = LLMCall(
128
+ model=model, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, cost=cost
129
+ )
130
+ self.calls.append(call)
131
+
132
+ # Update totals
133
+ self.total_prompt_tokens += prompt_tokens
134
+ self.total_completion_tokens += completion_tokens
135
+ self.total_cost += cost
136
+
137
+ # Track primary model
138
+ if self.model_used is None:
139
+ self.model_used = model
140
+
141
+ def get_summary(self) -> dict[str, Any]:
142
+ """Get summary as dictionary."""
143
+ return {
144
+ "module": self.module_name,
145
+ "model": self.model_used or "Unknown",
146
+ "calls": len(self.calls),
147
+ "prompt_tokens": self.total_prompt_tokens,
148
+ "completion_tokens": self.total_completion_tokens,
149
+ "total_tokens": self.total_prompt_tokens + self.total_completion_tokens,
150
+ "cost_usd": self.total_cost,
151
+ }
152
+
153
+ def print_summary(self):
154
+ """Print summary to terminal."""
155
+ if len(self.calls) == 0:
156
+ return
157
+
158
+ total_tokens = self.total_prompt_tokens + self.total_completion_tokens
159
+
160
+ print()
161
+ print("=" * 60)
162
+ print(f"📊 [{self.module_name}] LLM Usage Summary")
163
+ print("=" * 60)
164
+ print(f" Model : {self.model_used or 'Unknown'}")
165
+ print(f" API Calls : {len(self.calls)}")
166
+ print(
167
+ f" Tokens : {total_tokens:,} (Input: {self.total_prompt_tokens:,}, Output: {self.total_completion_tokens:,})"
168
+ )
169
+ print(f" Cost : ${self.total_cost:.6f} USD")
170
+ print("=" * 60)
171
+ print()
172
+
173
+ def reset(self):
174
+ """Reset all statistics."""
175
+ self.calls.clear()
176
+ self.total_prompt_tokens = 0
177
+ self.total_completion_tokens = 0
178
+ self.total_cost = 0.0
179
+ self.model_used = None
@@ -0,0 +1,56 @@
1
+ """
2
+ Services Layer
3
+ ==============
4
+
5
+ Unified service layer for DeepTutor providing:
6
+ - LLM client and configuration
7
+ - Embedding client and configuration
8
+ - RAG pipelines and components
9
+ - Prompt management
10
+ - TTS configuration
11
+ - Web Search providers
12
+ - System setup utilities
13
+ - Configuration loading
14
+
15
+ Usage:
16
+ from src.services.llm import get_llm_client
17
+ from src.services.embedding import get_embedding_client
18
+ from src.services.rag import get_pipeline
19
+ from src.services.prompt import get_prompt_manager
20
+ from src.services.tts import get_tts_config
21
+ from src.services.search import web_search
22
+ from src.services.setup import init_user_directories
23
+ from src.services.config import load_config_with_main
24
+
25
+ # LLM
26
+ llm = get_llm_client()
27
+ response = await llm.complete("Hello, world!")
28
+
29
+ # Embedding
30
+ embed = get_embedding_client()
31
+ vectors = await embed.embed(["text1", "text2"])
32
+
33
+ # RAG
34
+ pipeline = get_pipeline("raganything")
35
+ result = await pipeline.search("query", "kb_name")
36
+
37
+ # Prompt
38
+ pm = get_prompt_manager()
39
+ prompts = pm.load_prompts("guide", "tutor_agent")
40
+
41
+ # Search
42
+ result = web_search("What is AI?")
43
+ """
44
+
45
+ from . import config, embedding, llm, prompt, rag, search, setup, tts
46
+
47
+ __all__ = [
48
+ "llm",
49
+ "embedding",
50
+ "rag",
51
+ "prompt",
52
+ "tts",
53
+ "search",
54
+ "setup",
55
+ "config",
56
+ ]
@@ -0,0 +1,61 @@
1
+ """
2
+ Configuration Service
3
+ =====================
4
+
5
+ Provides three types of configuration:
6
+
7
+ 1. **YAML Configuration (loader.py)** - For application settings from config/*.yaml
8
+ - PROJECT_ROOT, load_config_with_main, get_path_from_config, parse_language, get_agent_params
9
+
10
+ 2. **Unified Config Service (unified_config.py)** - For service configurations (LLM, Embedding, TTS, Search)
11
+ - ConfigType, UnifiedConfigManager, get_config_manager
12
+ - get_active_llm_config, get_active_embedding_config, get_active_tts_config, get_active_search_config
13
+
14
+ 3. **Knowledge Base Config Service (knowledge_base_config.py)** - For KB-specific settings
15
+ - KnowledgeBaseConfigService, get_kb_config_service
16
+ """
17
+
18
+ # Re-export everything from loader.py (existing functionality)
19
+ # Export knowledge base config service
20
+ from .knowledge_base_config import (
21
+ KnowledgeBaseConfigService,
22
+ get_kb_config_service,
23
+ )
24
+ from .loader import (
25
+ PROJECT_ROOT,
26
+ get_agent_params,
27
+ get_path_from_config,
28
+ load_config_with_main,
29
+ parse_language,
30
+ )
31
+
32
+ # Export new unified config service
33
+ from .unified_config import (
34
+ ConfigType,
35
+ UnifiedConfigManager,
36
+ get_active_embedding_config,
37
+ get_active_llm_config,
38
+ get_active_search_config,
39
+ get_active_tts_config,
40
+ get_config_manager,
41
+ )
42
+
43
+ __all__ = [
44
+ # From loader.py
45
+ "PROJECT_ROOT",
46
+ "load_config_with_main",
47
+ "get_path_from_config",
48
+ "parse_language",
49
+ "get_agent_params",
50
+ # From unified_config.py
51
+ "ConfigType",
52
+ "UnifiedConfigManager",
53
+ "get_config_manager",
54
+ "get_active_llm_config",
55
+ "get_active_embedding_config",
56
+ "get_active_tts_config",
57
+ "get_active_search_config",
58
+ # From knowledge_base_config.py
59
+ "KnowledgeBaseConfigService",
60
+ "get_kb_config_service",
61
+ ]
@@ -0,0 +1,210 @@
1
+ """
2
+ Knowledge Base Configuration Service
3
+ =====================================
4
+
5
+ Centralized configuration management for knowledge bases.
6
+ Stores KB-specific settings like RAG provider, search mode, etc.
7
+ """
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any, Dict, Optional
12
+
13
+ from src.logging import get_logger
14
+
15
+ logger = get_logger("KBConfigService")
16
+
17
+ # Default config file path
18
+ DEFAULT_CONFIG_PATH = (
19
+ Path(__file__).parent.parent.parent.parent
20
+ / "data"
21
+ / "user"
22
+ / "settings"
23
+ / "knowledge_base_configs.json"
24
+ )
25
+
26
+
27
+ class KnowledgeBaseConfigService:
28
+ """
29
+ Service for managing knowledge base configurations.
30
+
31
+ Provides a centralized way to store and retrieve KB-specific settings,
32
+ separate from the per-KB metadata.json files.
33
+ """
34
+
35
+ _instance: Optional["KnowledgeBaseConfigService"] = None
36
+
37
+ def __init__(self, config_path: Optional[Path] = None):
38
+ self.config_path = config_path or DEFAULT_CONFIG_PATH
39
+ self._config: Dict[str, Any] = self._load_config()
40
+
41
+ @classmethod
42
+ def get_instance(cls, config_path: Optional[Path] = None) -> "KnowledgeBaseConfigService":
43
+ """Get singleton instance."""
44
+ if cls._instance is None:
45
+ cls._instance = cls(config_path)
46
+ return cls._instance
47
+
48
+ def _load_config(self) -> Dict[str, Any]:
49
+ """Load configuration from file."""
50
+ if self.config_path.exists():
51
+ try:
52
+ with open(self.config_path, "r", encoding="utf-8") as f:
53
+ return json.load(f)
54
+ except Exception as e:
55
+ logger.warning(f"Failed to load KB config: {e}")
56
+
57
+ # Return default config
58
+ return {
59
+ "configs": {},
60
+ "default_kb": None,
61
+ "global_defaults": {"rag_provider": "llamaindex", "search_mode": "hybrid"},
62
+ }
63
+
64
+ def _save_config(self):
65
+ """Save configuration to file."""
66
+ try:
67
+ self.config_path.parent.mkdir(parents=True, exist_ok=True)
68
+ with open(self.config_path, "w", encoding="utf-8") as f:
69
+ json.dump(self._config, f, indent=2, ensure_ascii=False)
70
+ except Exception as e:
71
+ logger.error(f"Failed to save KB config: {e}")
72
+
73
+ def get_kb_config(self, kb_name: str) -> Dict[str, Any]:
74
+ """
75
+ Get configuration for a specific knowledge base.
76
+
77
+ Args:
78
+ kb_name: Knowledge base name
79
+
80
+ Returns:
81
+ KB configuration dict with defaults applied
82
+ """
83
+ kb_config = self._config.get("configs", {}).get(kb_name, {})
84
+ defaults = self._config.get("global_defaults", {})
85
+
86
+ # Merge with defaults
87
+ return {
88
+ "rag_provider": kb_config.get("rag_provider")
89
+ or defaults.get("rag_provider", "llamaindex"),
90
+ "search_mode": kb_config.get("search_mode") or defaults.get("search_mode", "hybrid"),
91
+ **kb_config,
92
+ }
93
+
94
+ def set_kb_config(self, kb_name: str, config: Dict[str, Any]):
95
+ """
96
+ Set configuration for a specific knowledge base.
97
+
98
+ Args:
99
+ kb_name: Knowledge base name
100
+ config: Configuration dict
101
+ """
102
+ if "configs" not in self._config:
103
+ self._config["configs"] = {}
104
+
105
+ # Merge with existing config
106
+ existing = self._config["configs"].get(kb_name, {})
107
+ existing.update(config)
108
+ self._config["configs"][kb_name] = existing
109
+
110
+ self._save_config()
111
+ logger.info(f"Updated config for KB '{kb_name}': {config}")
112
+
113
+ def get_rag_provider(self, kb_name: str) -> str:
114
+ """Get RAG provider for a knowledge base."""
115
+ return self.get_kb_config(kb_name).get("rag_provider", "llamaindex")
116
+
117
+ def set_rag_provider(self, kb_name: str, provider: str):
118
+ """Set RAG provider for a knowledge base."""
119
+ self.set_kb_config(kb_name, {"rag_provider": provider})
120
+
121
+ def get_search_mode(self, kb_name: str) -> str:
122
+ """Get search mode for a knowledge base."""
123
+ return self.get_kb_config(kb_name).get("search_mode", "hybrid")
124
+
125
+ def set_search_mode(self, kb_name: str, mode: str):
126
+ """Set search mode for a knowledge base."""
127
+ self.set_kb_config(kb_name, {"search_mode": mode})
128
+
129
+ def delete_kb_config(self, kb_name: str):
130
+ """Delete configuration for a knowledge base."""
131
+ if "configs" in self._config and kb_name in self._config["configs"]:
132
+ del self._config["configs"][kb_name]
133
+ self._save_config()
134
+ logger.info(f"Deleted config for KB '{kb_name}'")
135
+
136
+ def get_all_configs(self) -> Dict[str, Any]:
137
+ """Get all knowledge base configurations."""
138
+ return self._config
139
+
140
+ def set_global_defaults(self, defaults: Dict[str, Any]):
141
+ """Set global default values."""
142
+ if "global_defaults" not in self._config:
143
+ self._config["global_defaults"] = {}
144
+
145
+ self._config["global_defaults"].update(defaults)
146
+ self._save_config()
147
+ logger.info(f"Updated global defaults: {defaults}")
148
+
149
+ def set_default_kb(self, kb_name: Optional[str]):
150
+ """Set the default knowledge base."""
151
+ self._config["default_kb"] = kb_name
152
+ self._save_config()
153
+ logger.info(f"Set default KB: {kb_name}")
154
+
155
+ def get_default_kb(self) -> Optional[str]:
156
+ """Get the default knowledge base name."""
157
+ return self._config.get("default_kb")
158
+
159
+ def sync_from_metadata(self, kb_name: str, kb_base_dir: Path):
160
+ """
161
+ Sync configuration from a KB's metadata.json file.
162
+
163
+ Useful for migrating existing KBs to the centralized config.
164
+
165
+ Args:
166
+ kb_name: Knowledge base name
167
+ kb_base_dir: Base directory for knowledge bases
168
+ """
169
+ metadata_file = kb_base_dir / kb_name / "metadata.json"
170
+
171
+ if not metadata_file.exists():
172
+ return
173
+
174
+ try:
175
+ with open(metadata_file, "r", encoding="utf-8") as f:
176
+ metadata = json.load(f)
177
+
178
+ # Extract relevant config from metadata
179
+ config = {}
180
+ if "rag_provider" in metadata and metadata["rag_provider"]:
181
+ config["rag_provider"] = metadata["rag_provider"]
182
+
183
+ if config:
184
+ self.set_kb_config(kb_name, config)
185
+ logger.info(f"Synced config for KB '{kb_name}' from metadata.json")
186
+
187
+ except Exception as e:
188
+ logger.warning(f"Failed to sync config from metadata for '{kb_name}': {e}")
189
+
190
+ def sync_all_from_metadata(self, kb_base_dir: Path):
191
+ """
192
+ Sync configurations from all KBs' metadata.json files.
193
+
194
+ Args:
195
+ kb_base_dir: Base directory for knowledge bases
196
+ """
197
+ if not kb_base_dir.exists():
198
+ return
199
+
200
+ for kb_dir in kb_base_dir.iterdir():
201
+ if kb_dir.is_dir() and kb_dir.name != "__pycache__":
202
+ metadata_file = kb_dir / "metadata.json"
203
+ if metadata_file.exists():
204
+ self.sync_from_metadata(kb_dir.name, kb_base_dir)
205
+
206
+
207
+ # Convenience function
208
+ def get_kb_config_service() -> KnowledgeBaseConfigService:
209
+ """Get the knowledge base config service instance."""
210
+ return KnowledgeBaseConfigService.get_instance()