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,310 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Query Numbered Item Tool - Query definitions, theorems, formulas, figures, etc.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ import sys
10
+
11
+ # Add parent directory to path (insert at front to prioritize project modules)
12
+ project_root = Path(__file__).parent.parent.parent
13
+ sys.path.insert(0, str(project_root))
14
+
15
+
16
+ def query_numbered_item(
17
+ identifier: str,
18
+ kb_name: str | None = None,
19
+ kb_base_dir: str | None = None,
20
+ max_results: int | None = None,
21
+ ) -> dict:
22
+ """
23
+ Query numbered item - Supports returning multiple matching results
24
+
25
+ Args:
26
+ identifier: Identifier of the numbered item
27
+ - Definition/Theorem: e.g., "Definition 1.1", "Theorem 2.3"
28
+ - Formula: e.g., "(1.2.1)", "(2.3.5)"
29
+ - Figure: e.g., "Figure 1.1", "Figure 2.5"
30
+ - Example: e.g., "Example 1.1", "Remark 2.1"
31
+ kb_name: Knowledge base name (optional, defaults to default knowledge base)
32
+ kb_base_dir: Knowledge base base directory (optional, defaults to knowledge_bases under project root)
33
+ max_results: Maximum number of items to return (optional, defaults to config value or 5)
34
+
35
+ Returns:
36
+ dict: Dictionary containing query results
37
+ {
38
+ "identifier": str, # Original query identifier
39
+ "type": str, # formula/definition/theorem/lemma/figure/example/remark
40
+ "status": str, # success/failed
41
+ "count": int, # Number of matched items
42
+ "items": [ # List of all matched items (sorted by priority)
43
+ {
44
+ "identifier": str, # Actual matched identifier
45
+ "type": str,
46
+ "content": str
47
+ },
48
+ ...
49
+ ],
50
+ "content": str, # Backward compatible: single item content or merged content for multiple items
51
+ "error": str (only when failed)
52
+ }
53
+ """
54
+ # Load configuration for max_results if not specified
55
+ if max_results is None:
56
+ try:
57
+ from src.services.config import load_config_with_main
58
+
59
+ project_root = Path(__file__).parent.parent.parent
60
+ config = load_config_with_main("main.yaml", project_root)
61
+ max_results = config.get("tools", {}).get("query_item", {}).get("max_results", 5)
62
+ except Exception:
63
+ max_results = 5 # Default value
64
+
65
+ # If path not specified, use absolute path relative to this file
66
+ if kb_base_dir is None:
67
+ # __file__ = DeepTutor/tools/query_item_tool.py
68
+ # .parent = DeepTutor/tools
69
+ # .parent = DeepTutor
70
+ kb_base_dir = Path(__file__).parent.parent.parent / "data/knowledge_bases"
71
+ else:
72
+ kb_base_dir = Path(kb_base_dir)
73
+
74
+ base_dir = kb_base_dir
75
+
76
+ # Get knowledge base
77
+ if not kb_name:
78
+ config_file = base_dir / "kb_config.json"
79
+ if config_file.exists():
80
+ try:
81
+ with open(config_file, encoding="utf-8") as f:
82
+ config = json.load(f)
83
+ kb_name = config.get("default")
84
+ except Exception:
85
+ pass
86
+
87
+ if not kb_name:
88
+ return {
89
+ "identifier": identifier,
90
+ "type": "unknown",
91
+ "content": "",
92
+ "status": "failed",
93
+ "error": "Error: Knowledge base not specified and no default knowledge base",
94
+ }
95
+
96
+ # Load items
97
+ kb_dir = base_dir / kb_name
98
+ if not kb_dir.exists():
99
+ return {
100
+ "identifier": identifier,
101
+ "type": "unknown",
102
+ "content": "",
103
+ "status": "failed",
104
+ "error": f"Error: Knowledge base '{kb_name}' does not exist",
105
+ }
106
+
107
+ items_file = kb_dir / "numbered_items.json"
108
+ if not items_file.exists():
109
+ return {
110
+ "identifier": identifier,
111
+ "type": "unknown",
112
+ "content": "",
113
+ "status": "failed",
114
+ "error": f"Error: numbered_items.json not found in knowledge base '{kb_name}'",
115
+ }
116
+
117
+ try:
118
+ with open(items_file, encoding="utf-8") as f:
119
+ raw_items = json.load(f)
120
+ except Exception as e:
121
+ return {
122
+ "identifier": identifier,
123
+ "type": "unknown",
124
+ "content": "",
125
+ "status": "failed",
126
+ "error": f"Error: Unable to read file - {e}",
127
+ }
128
+
129
+ # Extract text content
130
+ items = {}
131
+ for key, value in raw_items.items():
132
+ if isinstance(value, dict):
133
+ items[key] = value.get("text", str(value))
134
+ else:
135
+ items[key] = value
136
+
137
+ # Validate identifier parameter
138
+ if not identifier:
139
+ return {
140
+ "identifier": identifier or "",
141
+ "type": "unknown",
142
+ "content": "",
143
+ "status": "failed",
144
+ "error": "Error: identifier parameter is empty or None",
145
+ }
146
+
147
+ # Ensure identifier is a string
148
+ if not isinstance(identifier, str):
149
+ identifier = str(identifier)
150
+
151
+ # Determine type
152
+ item_type = "unknown"
153
+ identifier_lower = identifier.lower()
154
+ if "figure" in identifier_lower:
155
+ item_type = "figure"
156
+ elif "definition" in identifier_lower:
157
+ item_type = "definition"
158
+ elif "theorem" in identifier_lower:
159
+ item_type = "theorem"
160
+ elif "lemma" in identifier_lower:
161
+ item_type = "lemma"
162
+ elif "example" in identifier_lower:
163
+ item_type = "example"
164
+ elif "remark" in identifier_lower:
165
+ item_type = "remark"
166
+ elif identifier.strip().startswith("(") and identifier.strip().endswith(")"):
167
+ item_type = "formula"
168
+
169
+ # 1. Exact match (highest priority)
170
+ if identifier in items:
171
+ matched_item = {"identifier": identifier, "type": item_type, "content": items[identifier]}
172
+ return {
173
+ "identifier": identifier,
174
+ "type": item_type,
175
+ "status": "success",
176
+ "count": 1,
177
+ "items": [matched_item],
178
+ "content": items[identifier], # Backward compatible
179
+ }
180
+
181
+ # 2. Case-insensitive exact match
182
+ exact_matches = []
183
+ for key, value in items.items():
184
+ if key.lower() == identifier_lower:
185
+ exact_matches.append({"identifier": key, "type": item_type, "content": value})
186
+
187
+ if exact_matches:
188
+ # Limit results
189
+ if max_results and len(exact_matches) > max_results:
190
+ exact_matches = exact_matches[:max_results]
191
+
192
+ # Build content (backward compatible)
193
+ content = (
194
+ exact_matches[0]["content"]
195
+ if len(exact_matches) == 1
196
+ else "\n\n".join(
197
+ [f"[{item['identifier']}]\n{item['content']}" for item in exact_matches]
198
+ )
199
+ )
200
+
201
+ return {
202
+ "identifier": identifier,
203
+ "type": item_type,
204
+ "status": "success",
205
+ "count": len(exact_matches),
206
+ "items": exact_matches,
207
+ "content": content,
208
+ }
209
+
210
+ # 3. Prefix match (e.g., "2.1" matches "(2.1.1)", "(2.1.2)", etc.)
211
+ prefix_matches = []
212
+ identifier_clean = identifier.strip().strip("()") # Remove parentheses, extract pure numbers
213
+
214
+ for key, value in items.items():
215
+ key_clean = key.strip().strip("()")
216
+ # Prefix match: key starts with identifier (after removing parentheses)
217
+ if (
218
+ key_clean.startswith(identifier_clean + ".")
219
+ or key_clean == identifier_clean
220
+ or key.lower().startswith(identifier_lower)
221
+ ):
222
+ prefix_matches.append({"identifier": key, "type": item_type, "content": value})
223
+
224
+ # Limit results
225
+ if max_results and len(prefix_matches) > max_results:
226
+ prefix_matches = prefix_matches[:max_results]
227
+
228
+ if prefix_matches:
229
+ # Build content
230
+ content = (
231
+ prefix_matches[0]["content"]
232
+ if len(prefix_matches) == 1
233
+ else "\n\n".join(
234
+ [f"[{item['identifier']}]\n{item['content']}" for item in prefix_matches]
235
+ )
236
+ )
237
+
238
+ return {
239
+ "identifier": identifier,
240
+ "type": item_type,
241
+ "status": "success",
242
+ "count": len(prefix_matches),
243
+ "items": prefix_matches,
244
+ "content": content,
245
+ }
246
+
247
+ # 4. Partial match (contains query string)
248
+ partial_matches = []
249
+ for key, value in items.items():
250
+ if identifier_lower in key.lower():
251
+ partial_matches.append({"identifier": key, "type": item_type, "content": value})
252
+
253
+ # Limit results
254
+ if max_results and len(partial_matches) > max_results:
255
+ partial_matches = partial_matches[:max_results]
256
+
257
+ if partial_matches:
258
+ content = (
259
+ partial_matches[0]["content"]
260
+ if len(partial_matches) == 1
261
+ else "\n\n".join(
262
+ [f"[{item['identifier']}]\n{item['content']}" for item in partial_matches]
263
+ )
264
+ )
265
+
266
+ return {
267
+ "identifier": identifier,
268
+ "type": item_type,
269
+ "status": "success",
270
+ "count": len(partial_matches),
271
+ "items": partial_matches,
272
+ "content": content,
273
+ }
274
+
275
+ # 5. Not found - provide suggestions
276
+ suggestions = [k for k in items if identifier_lower in k.lower()][:5]
277
+ error_msg = f"Numbered item '{identifier}' not found"
278
+ if suggestions:
279
+ error_msg += "\n\nSimilar items:\n" + "\n".join(f" • {s}" for s in suggestions)
280
+
281
+ return {
282
+ "identifier": identifier,
283
+ "type": item_type,
284
+ "status": "failed",
285
+ "count": 0,
286
+ "items": [],
287
+ "content": "",
288
+ "error": error_msg,
289
+ }
290
+
291
+
292
+ if __name__ == "__main__":
293
+ import sys
294
+
295
+ if sys.platform == "win32":
296
+ import io
297
+
298
+ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
299
+
300
+ # Test
301
+ print("Testing query_numbered_item\n" + "=" * 60)
302
+
303
+ # Test 1
304
+ print("\n[Test 1] Query formula (1.2.1):")
305
+ result = query_numbered_item("(1.2.1)", kb_name="ai_textbook")
306
+ print(f"Status: {result['status']}")
307
+ print(f"Type: {result['type']}")
308
+ print(f"Content: {result.get('content', result.get('error'))[:200]}...")
309
+
310
+ print("\n" + "=" * 60)
@@ -0,0 +1,15 @@
1
+ """
2
+ Question Tools - Question generation system toolset
3
+
4
+ Tools for PDF parsing, question extraction, and exam mimicking.
5
+ """
6
+
7
+ from .exam_mimic import mimic_exam_questions
8
+ from .pdf_parser import parse_pdf_with_mineru
9
+ from .question_extractor import extract_questions_from_paper
10
+
11
+ __all__ = [
12
+ "parse_pdf_with_mineru",
13
+ "extract_questions_from_paper",
14
+ "mimic_exam_questions",
15
+ ]