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,313 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Config Validator - Configuration validator
5
+ Validates the completeness and correctness of config.yaml
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ import yaml
12
+
13
+
14
+ class ConfigValidator:
15
+ """Configuration validator"""
16
+
17
+ # Required top-level configuration items
18
+ # Note: llm configuration has been moved to environment variables (env_config.py), no longer required in config.yaml
19
+ REQUIRED_SECTIONS = ["system", "agents"]
20
+
21
+ # Required system configuration
22
+ REQUIRED_SYSTEM_CONFIGS = [
23
+ "output_base_dir",
24
+ "save_intermediate_results",
25
+ # Note: language is now unified in config/main.yaml, not required in sub-configs
26
+ ]
27
+
28
+ # Supported languages (supports multiple formats)
29
+ SUPPORTED_LANGUAGES = ["zh", "en", "English", "Chinese"]
30
+
31
+ # Supported log levels
32
+ SUPPORTED_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
33
+
34
+ # Agent list - dual-loop architecture
35
+ STANDARD_AGENTS = [
36
+ # Analysis Loop
37
+ "investigate_agent",
38
+ "note_agent",
39
+ # Solve Loop
40
+ "manager_agent",
41
+ "solve_agent",
42
+ "tool_agent",
43
+ "response_agent",
44
+ "precision_answer_agent",
45
+ ]
46
+
47
+ def __init__(self):
48
+ self.errors: list[str] = []
49
+ self.warnings: list[str] = []
50
+
51
+ def validate(self, config: dict[str, Any]) -> tuple[bool, list[str], list[str]]:
52
+ """
53
+ Validate configuration
54
+
55
+ Args:
56
+ config: Configuration dictionary
57
+
58
+ Returns:
59
+ (is_valid, errors, warnings)
60
+ """
61
+ self.errors = []
62
+ self.warnings = []
63
+
64
+ # 1. Validate top-level structure
65
+ self._validate_structure(config)
66
+
67
+ # 2. Validate system configuration
68
+ if "system" in config:
69
+ self._validate_system(config["system"])
70
+
71
+ # 3. Validate agents configuration
72
+ if "agents" in config:
73
+ self._validate_agents(config["agents"])
74
+
75
+ # 4. Validate llm configuration (optional, LLM config now mainly comes from environment variables)
76
+ if "llm" in config:
77
+ self._validate_llm(config["llm"])
78
+
79
+ # 5. Validate logging configuration (optional)
80
+ if "logging" in config:
81
+ self._validate_logging(config["logging"])
82
+
83
+ # 6. Validate monitoring configuration (optional)
84
+ if "monitoring" in config:
85
+ self._validate_monitoring(config["monitoring"])
86
+
87
+ is_valid = len(self.errors) == 0
88
+ return is_valid, self.errors, self.warnings
89
+
90
+ def _validate_structure(self, config: dict[str, Any]):
91
+ """Validate top-level structure"""
92
+ for section in self.REQUIRED_SECTIONS:
93
+ if section not in config:
94
+ self.errors.append(f"Missing required configuration section: {section}")
95
+
96
+ def _validate_system(self, system_config: dict[str, Any]):
97
+ """Validate system configuration"""
98
+ if system_config is None:
99
+ self.errors.append("system configuration cannot be None")
100
+ return
101
+
102
+ # Check required fields
103
+ for field in self.REQUIRED_SYSTEM_CONFIGS:
104
+ if field not in system_config:
105
+ self.errors.append(f"system config missing required field: {field}")
106
+
107
+ # Check language configuration (unified in config/main.yaml, optional in sub-configs)
108
+ if "language" in system_config:
109
+ lang = system_config["language"]
110
+ if lang not in self.SUPPORTED_LANGUAGES:
111
+ self.warnings.append(
112
+ f"language '{lang}' not in supported list: {self.SUPPORTED_LANGUAGES}. Suggest unified configuration in config/main.yaml."
113
+ )
114
+ # Backward compatibility: if output_language still exists, give warning
115
+ if "output_language" in system_config:
116
+ self.warnings.append(
117
+ "output_language is deprecated, please use language field. Language configuration has been unified to system.language in config/main.yaml"
118
+ )
119
+
120
+ # Check auto_solve configuration
121
+ if "auto_solve" in system_config:
122
+ if not isinstance(system_config["auto_solve"], bool):
123
+ self.errors.append("auto_solve must be a boolean value")
124
+
125
+ def _validate_agents(self, agents_config: dict[str, Any]):
126
+ """Validate agents configuration"""
127
+ if agents_config is None:
128
+ self.errors.append("agents config cannot be None")
129
+ return
130
+
131
+ # Check if standard agents are configured
132
+ for agent_name in self.STANDARD_AGENTS:
133
+ if agent_name not in agents_config:
134
+ self.warnings.append(f"Agent not configured: {agent_name}")
135
+ else:
136
+ self._validate_agent_config(agent_name, agents_config[agent_name])
137
+
138
+ def _validate_agent_config(self, agent_name: str, agent_config: dict[str, Any]):
139
+ """Validate single Agent configuration"""
140
+ if agent_config is None:
141
+ self.errors.append(f"{agent_name} config cannot be None")
142
+ return
143
+
144
+ # Check enabled field
145
+ if "enabled" in agent_config:
146
+ if not isinstance(agent_config["enabled"], bool):
147
+ self.errors.append(f"{agent_name}.enabled must be a boolean value")
148
+
149
+ # Check model field
150
+ if "model" in agent_config:
151
+ if not isinstance(agent_config["model"], str):
152
+ self.errors.append(f"{agent_name}.model must be a string")
153
+
154
+ # Check temperature field
155
+ if "temperature" in agent_config:
156
+ temp = agent_config["temperature"]
157
+ if not isinstance(temp, (int, float)):
158
+ self.errors.append(f"{agent_name}.temperature must be a number")
159
+ elif not 0 <= temp <= 2:
160
+ self.warnings.append(
161
+ f"{agent_name}.temperature={temp} exceeds recommended range [0, 2]"
162
+ )
163
+
164
+ # Check max_retries field
165
+ if "max_retries" in agent_config:
166
+ if not isinstance(agent_config["max_retries"], int):
167
+ self.errors.append(f"{agent_name}.max_retries must be an integer")
168
+ elif agent_config["max_retries"] < 0:
169
+ self.errors.append(f"{agent_name}.max_retries cannot be negative")
170
+
171
+ def _validate_llm(self, llm_config: dict[str, Any]):
172
+ """Validate llm configuration"""
173
+ if llm_config is None:
174
+ self.errors.append("llm config cannot be None")
175
+ return
176
+
177
+ # Check default_model
178
+ if "default_model" not in llm_config:
179
+ self.warnings.append("llm config missing default_model field")
180
+
181
+ # Check max_retries
182
+ if "max_retries" in llm_config:
183
+ if not isinstance(llm_config["max_retries"], int):
184
+ self.errors.append("llm.max_retries must be an integer")
185
+ elif llm_config["max_retries"] < 0:
186
+ self.errors.append("llm.max_retries cannot be negative")
187
+
188
+ # Check timeout
189
+ if "timeout" in llm_config:
190
+ if not isinstance(llm_config["timeout"], (int, float)):
191
+ self.errors.append("llm.timeout must be a number")
192
+ elif llm_config["timeout"] <= 0:
193
+ self.warnings.append("llm.timeout should not be negative or zero")
194
+
195
+ def _validate_logging(self, logging_config: dict[str, Any]):
196
+ """Validate logging configuration"""
197
+ if logging_config is None:
198
+ # logging is optional, if None but not empty key, may be considered error or ignored
199
+ self.errors.append("logging config cannot be None")
200
+ return
201
+
202
+ # Check level
203
+ if "level" in logging_config:
204
+ level = logging_config["level"]
205
+ if level not in self.SUPPORTED_LOG_LEVELS:
206
+ self.errors.append(
207
+ f"logging.level '{level}' not in supported list: {self.SUPPORTED_LOG_LEVELS}"
208
+ )
209
+
210
+ # Check save_to_file
211
+ if "save_to_file" in logging_config:
212
+ if not isinstance(logging_config["save_to_file"], bool):
213
+ self.errors.append("logging.save_to_file must be a boolean value")
214
+
215
+ # Check verbose
216
+ if "verbose" in logging_config:
217
+ if not isinstance(logging_config["verbose"], bool):
218
+ self.errors.append("logging.verbose must be a boolean value")
219
+
220
+ def _validate_monitoring(self, monitoring_config: dict[str, Any]):
221
+ """Validate monitoring configuration"""
222
+ if monitoring_config is None:
223
+ self.errors.append("monitoring config cannot be None")
224
+ return
225
+
226
+ # Check enabled
227
+ if "enabled" in monitoring_config:
228
+ if not isinstance(monitoring_config["enabled"], bool):
229
+ self.errors.append("monitoring.enabled must be a boolean value")
230
+
231
+ # Check track_token_usage
232
+ if "track_token_usage" in monitoring_config:
233
+ if not isinstance(monitoring_config["track_token_usage"], bool):
234
+ self.errors.append("monitoring.track_token_usage must be a boolean value")
235
+
236
+ # Check track_time
237
+ if "track_time" in monitoring_config:
238
+ if not isinstance(monitoring_config["track_time"], bool):
239
+ self.errors.append("monitoring.track_time must be a boolean value")
240
+
241
+
242
+ def validate_config_file(config_path: str) -> tuple[bool, list[str], list[str]]:
243
+ """
244
+ Validate configuration file
245
+
246
+ Args:
247
+ config_path: Configuration file path
248
+
249
+ Returns:
250
+ (is_valid, errors, warnings)
251
+ """
252
+ try:
253
+ with open(config_path, encoding="utf-8") as f:
254
+ config = yaml.safe_load(f)
255
+ except FileNotFoundError:
256
+ return False, [f"Configuration file does not exist: {config_path}"], []
257
+ except yaml.YAMLError as e:
258
+ return False, [f"YAML parsing error: {e!s}"], []
259
+ except Exception as e:
260
+ return False, [f"Failed to load configuration file: {e!s}"], []
261
+
262
+ validator = ConfigValidator()
263
+ return validator.validate(config)
264
+
265
+
266
+ def print_validation_result(is_valid: bool, errors: list[str], warnings: list[str]):
267
+ """
268
+ Print validation result
269
+
270
+ Args:
271
+ is_valid: Whether configuration is valid
272
+ errors: List of errors
273
+ warnings: List of warnings
274
+ """
275
+ print("=" * 60)
276
+ print("Configuration Validation Result")
277
+ print("=" * 60)
278
+
279
+ if is_valid:
280
+ print("✓ Configuration validation passed")
281
+ else:
282
+ print("✗ Configuration validation failed")
283
+
284
+ print()
285
+
286
+ if errors:
287
+ print(f"Errors ({len(errors)}):")
288
+ for i, error in enumerate(errors, 1):
289
+ print(f" {i}. {error}")
290
+ print()
291
+
292
+ if warnings:
293
+ print(f"Warnings ({len(warnings)}):")
294
+ for i, warning in enumerate(warnings, 1):
295
+ print(f" {i}. {warning}")
296
+ print()
297
+
298
+ print("=" * 60)
299
+
300
+
301
+ if __name__ == "__main__":
302
+ # Test configuration validation
303
+ print("Configuration Validation Test")
304
+ print("=" * 60)
305
+
306
+ # Validate config.yaml in current directory
307
+ config_path = Path(__file__).parent.parent.parent.parent / "config.yaml"
308
+
309
+ if config_path.exists():
310
+ is_valid, errors, warnings = validate_config_file(str(config_path))
311
+ print_validation_result(is_valid, errors, warnings)
312
+ else:
313
+ print(f"Configuration file not found: {config_path}")
@@ -0,0 +1,223 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Display Manager - Terminal display manager
4
+ Uses rich library to implement beautiful terminal interface, including fixed header (status/statistics) and scrolling log area
5
+ """
6
+
7
+ from datetime import datetime
8
+ import sys
9
+ from typing import Any
10
+
11
+ try:
12
+ from rich.console import Console
13
+ from rich.layout import Layout
14
+ from rich.live import Live
15
+ from rich.panel import Panel
16
+ from rich.table import Table
17
+ from rich.text import Text
18
+
19
+ RICH_AVAILABLE = True
20
+ except ImportError:
21
+ RICH_AVAILABLE = False
22
+
23
+
24
+ class DisplayManager:
25
+ """
26
+ Terminal display manager
27
+
28
+ If rich is available, use TUI interface:
29
+ - Top: Agent status checklist + Token/Cost statistics
30
+ - Bottom: Scrolling logs
31
+
32
+ If rich is not available, fall back to standard stdout output
33
+ """
34
+
35
+ def __init__(self):
36
+ self.rich_available = RICH_AVAILABLE
37
+
38
+ self.agents_status = {
39
+ "InvestigateAgent": "pending",
40
+ "NoteAgent": "pending",
41
+ "ManagerAgent": "pending",
42
+ "SolveAgent": "pending",
43
+ "ToolAgent": "pending",
44
+ "ResponseAgent": "pending",
45
+ "PrecisionAnswerAgent": "pending",
46
+ }
47
+
48
+ self.stats = {
49
+ "model": "Unknown",
50
+ "calls": 0,
51
+ "tokens": 0,
52
+ "input_tokens": 0,
53
+ "output_tokens": 0,
54
+ "cost": 0.0,
55
+ }
56
+
57
+ # Log buffer
58
+ self.log_buffer = []
59
+ self.max_log_lines = 20
60
+
61
+ # Early return if rich not available (but after initializing core attributes)
62
+ if not self.rich_available:
63
+ self.console = None
64
+ self.layout = None
65
+ self.live = None
66
+ return
67
+
68
+ # Explicitly use raw stdout, bypass logger redirection
69
+ self.console = Console(file=sys.__stdout__)
70
+
71
+ # Layout
72
+ self.layout = self._make_layout()
73
+
74
+ # Live update context
75
+ self.live = None
76
+
77
+ def start(self):
78
+ """Start Live Display"""
79
+ if self.rich_available and self.live is None:
80
+ self.live = Live(
81
+ self.layout,
82
+ refresh_per_second=4,
83
+ console=self.console,
84
+ transient=True, # Remove interface on exit to avoid blocking subsequent input
85
+ )
86
+ self.live.start()
87
+
88
+ def stop(self):
89
+ """Stop Live Display"""
90
+ if self.rich_available and self.live:
91
+ self.live.stop()
92
+ self.live = None
93
+
94
+ def _make_layout(self) -> "Layout":
95
+ """Create layout"""
96
+ layout = Layout()
97
+
98
+ # Split into upper part (fixed info) and lower part (logs)
99
+ layout.split_column(Layout(name="header", size=10), Layout(name="body"))
100
+
101
+ # Split upper part into left (Agents) and right (Stats)
102
+ layout["header"].split_row(Layout(name="agents", ratio=1), Layout(name="stats", ratio=2))
103
+
104
+ return layout
105
+
106
+ def update(self):
107
+ """Update interface content"""
108
+ if not self.rich_available:
109
+ return
110
+
111
+ # Update Agents status panel
112
+ agents_table = Table(show_header=False, box=None, padding=(0, 1), expand=True)
113
+ agents_table.add_column("Status", width=2)
114
+ agents_table.add_column("Name")
115
+
116
+ for name, status in self.agents_status.items():
117
+ if status == "done":
118
+ icon = "✓"
119
+ style = "green"
120
+ elif status == "running":
121
+ icon = "●"
122
+ style = "yellow"
123
+ elif status == "error":
124
+ icon = "✗"
125
+ style = "red"
126
+ else:
127
+ icon = " "
128
+ style = "dim"
129
+
130
+ agents_table.add_row(Text(icon, style=style), Text(name, style=style))
131
+
132
+ self.layout["agents"].update(
133
+ Panel(
134
+ agents_table,
135
+ title="📦 Agents Status",
136
+ border_style="blue",
137
+ padding=(0, 1), # Increase panel padding
138
+ )
139
+ )
140
+
141
+ # Update statistics panel
142
+ stats_content = (
143
+ f"[bold]{self.stats['model']}[/bold]\n"
144
+ f"Calls: {self.stats['calls']}\n"
145
+ f"Tokens: {self.stats['tokens']:,} "
146
+ f"(Input: {self.stats['input_tokens']:,}, Output: {self.stats['output_tokens']:,})\n"
147
+ f"[bold yellow]Cost: ${self.stats['cost']:.6f} USD[/bold yellow]"
148
+ )
149
+
150
+ self.layout["stats"].update(
151
+ Panel(
152
+ stats_content,
153
+ title="📊 Performance & Cost",
154
+ border_style="green",
155
+ padding=(0, 1), # Increase panel padding
156
+ )
157
+ )
158
+
159
+ # Update log panel
160
+ log_text = "\n".join(self.log_buffer[-self.max_log_lines :])
161
+ self.layout["body"].update(
162
+ Panel(
163
+ log_text,
164
+ title="📝 Activity Log",
165
+ border_style="white",
166
+ padding=(0, 1), # Increase panel padding
167
+ )
168
+ )
169
+
170
+ def set_agent_status(self, agent_name: str, status: str):
171
+ """Set Agent status (pending, running, done, error)"""
172
+ self.agents_status[agent_name] = status
173
+ self.update()
174
+
175
+ def update_token_stats(self, summary: dict[str, Any]):
176
+ """Update Token statistics"""
177
+ self.stats["calls"] = summary.get("total_calls", 0)
178
+ self.stats["tokens"] = summary.get("total_tokens", 0)
179
+ self.stats["input_tokens"] = summary.get("total_prompt_tokens", 0)
180
+ self.stats["output_tokens"] = summary.get("total_completion_tokens", 0)
181
+ self.stats["cost"] = summary.get("total_cost_usd", 0.0)
182
+
183
+ # Try to get the main model used
184
+ by_model = summary.get("by_model", {})
185
+ if by_model:
186
+ # Find the most called model
187
+ top_model = max(by_model.items(), key=lambda x: x[1]["calls"])[0]
188
+ self.stats["model"] = top_model
189
+
190
+ self.update()
191
+
192
+ def log(self, message: str):
193
+ """Add log"""
194
+ if not self.rich_available:
195
+ print(message)
196
+ return
197
+
198
+ # Simple cleaning
199
+ clean_msg = message.rstrip()
200
+ if not clean_msg:
201
+ return
202
+
203
+ # Add timestamp
204
+ timestamp = datetime.now().strftime("%H:%M:%S")
205
+ formatted_msg = f"[{timestamp}] {clean_msg}"
206
+
207
+ self.log_buffer.append(formatted_msg)
208
+ # Maintain buffer size
209
+ if len(self.log_buffer) > 100:
210
+ self.log_buffer = self.log_buffer[-50:]
211
+
212
+ self.update()
213
+
214
+
215
+ # Global instance
216
+ _display_manager = None
217
+
218
+
219
+ def get_display_manager() -> DisplayManager:
220
+ global _display_manager
221
+ if _display_manager is None:
222
+ _display_manager = DisplayManager()
223
+ return _display_manager