realtimex-deeptutor 0.5.0.post1__py3-none-any.whl → 0.5.0.post3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/METADATA +24 -17
  2. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/RECORD +143 -123
  3. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/WHEEL +1 -1
  4. realtimex_deeptutor-0.5.0.post3.dist-info/entry_points.txt +4 -0
  5. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/top_level.txt +1 -0
  6. scripts/__init__.py +1 -0
  7. scripts/audit_prompts.py +179 -0
  8. scripts/check_install.py +460 -0
  9. scripts/generate_roster.py +327 -0
  10. scripts/install_all.py +653 -0
  11. scripts/migrate_kb.py +655 -0
  12. scripts/start.py +807 -0
  13. scripts/start_web.py +632 -0
  14. scripts/sync_prompts_from_en.py +147 -0
  15. src/__init__.py +2 -2
  16. src/agents/ideagen/material_organizer_agent.py +2 -0
  17. src/agents/solve/__init__.py +6 -0
  18. src/agents/solve/main_solver.py +9 -0
  19. src/agents/solve/prompts/zh/analysis_loop/investigate_agent.yaml +9 -7
  20. src/agents/solve/session_manager.py +345 -0
  21. src/api/main.py +14 -0
  22. src/api/routers/chat.py +3 -3
  23. src/api/routers/co_writer.py +12 -7
  24. src/api/routers/config.py +1 -0
  25. src/api/routers/guide.py +3 -1
  26. src/api/routers/ideagen.py +7 -0
  27. src/api/routers/knowledge.py +64 -12
  28. src/api/routers/question.py +2 -0
  29. src/api/routers/realtimex.py +137 -0
  30. src/api/routers/research.py +9 -0
  31. src/api/routers/solve.py +120 -2
  32. src/cli/__init__.py +13 -0
  33. src/cli/start.py +209 -0
  34. src/config/constants.py +11 -9
  35. src/knowledge/add_documents.py +453 -213
  36. src/knowledge/extract_numbered_items.py +9 -10
  37. src/knowledge/initializer.py +102 -101
  38. src/knowledge/manager.py +251 -74
  39. src/knowledge/progress_tracker.py +43 -2
  40. src/knowledge/start_kb.py +11 -2
  41. src/logging/__init__.py +5 -0
  42. src/logging/adapters/__init__.py +1 -0
  43. src/logging/adapters/lightrag.py +25 -18
  44. src/logging/adapters/llamaindex.py +1 -0
  45. src/logging/config.py +30 -27
  46. src/logging/handlers/__init__.py +1 -0
  47. src/logging/handlers/console.py +7 -50
  48. src/logging/handlers/file.py +5 -20
  49. src/logging/handlers/websocket.py +23 -19
  50. src/logging/logger.py +161 -126
  51. src/logging/stats/__init__.py +1 -0
  52. src/logging/stats/llm_stats.py +37 -17
  53. src/services/__init__.py +17 -1
  54. src/services/config/__init__.py +1 -0
  55. src/services/config/knowledge_base_config.py +1 -0
  56. src/services/config/loader.py +1 -1
  57. src/services/config/unified_config.py +211 -4
  58. src/services/embedding/__init__.py +1 -0
  59. src/services/embedding/adapters/__init__.py +3 -0
  60. src/services/embedding/adapters/base.py +1 -0
  61. src/services/embedding/adapters/cohere.py +1 -0
  62. src/services/embedding/adapters/jina.py +1 -0
  63. src/services/embedding/adapters/ollama.py +1 -0
  64. src/services/embedding/adapters/openai_compatible.py +1 -0
  65. src/services/embedding/adapters/realtimex.py +125 -0
  66. src/services/embedding/client.py +27 -0
  67. src/services/embedding/config.py +3 -0
  68. src/services/embedding/provider.py +1 -0
  69. src/services/llm/__init__.py +17 -3
  70. src/services/llm/capabilities.py +47 -0
  71. src/services/llm/client.py +32 -0
  72. src/services/llm/cloud_provider.py +21 -4
  73. src/services/llm/config.py +36 -2
  74. src/services/llm/error_mapping.py +1 -0
  75. src/services/llm/exceptions.py +30 -0
  76. src/services/llm/factory.py +55 -16
  77. src/services/llm/local_provider.py +1 -0
  78. src/services/llm/providers/anthropic.py +1 -0
  79. src/services/llm/providers/base_provider.py +1 -0
  80. src/services/llm/providers/open_ai.py +1 -0
  81. src/services/llm/realtimex_provider.py +240 -0
  82. src/services/llm/registry.py +1 -0
  83. src/services/llm/telemetry.py +1 -0
  84. src/services/llm/types.py +1 -0
  85. src/services/llm/utils.py +1 -0
  86. src/services/prompt/__init__.py +1 -0
  87. src/services/prompt/manager.py +3 -2
  88. src/services/rag/__init__.py +27 -5
  89. src/services/rag/components/__init__.py +1 -0
  90. src/services/rag/components/base.py +1 -0
  91. src/services/rag/components/chunkers/__init__.py +1 -0
  92. src/services/rag/components/chunkers/base.py +1 -0
  93. src/services/rag/components/chunkers/fixed.py +1 -0
  94. src/services/rag/components/chunkers/numbered_item.py +1 -0
  95. src/services/rag/components/chunkers/semantic.py +1 -0
  96. src/services/rag/components/embedders/__init__.py +1 -0
  97. src/services/rag/components/embedders/base.py +1 -0
  98. src/services/rag/components/embedders/openai.py +1 -0
  99. src/services/rag/components/indexers/__init__.py +1 -0
  100. src/services/rag/components/indexers/base.py +1 -0
  101. src/services/rag/components/indexers/graph.py +5 -44
  102. src/services/rag/components/indexers/lightrag.py +5 -44
  103. src/services/rag/components/indexers/vector.py +1 -0
  104. src/services/rag/components/parsers/__init__.py +1 -0
  105. src/services/rag/components/parsers/base.py +1 -0
  106. src/services/rag/components/parsers/markdown.py +1 -0
  107. src/services/rag/components/parsers/pdf.py +1 -0
  108. src/services/rag/components/parsers/text.py +1 -0
  109. src/services/rag/components/retrievers/__init__.py +1 -0
  110. src/services/rag/components/retrievers/base.py +1 -0
  111. src/services/rag/components/retrievers/dense.py +1 -0
  112. src/services/rag/components/retrievers/hybrid.py +5 -44
  113. src/services/rag/components/retrievers/lightrag.py +5 -44
  114. src/services/rag/components/routing.py +48 -0
  115. src/services/rag/factory.py +112 -46
  116. src/services/rag/pipeline.py +1 -0
  117. src/services/rag/pipelines/__init__.py +27 -18
  118. src/services/rag/pipelines/lightrag.py +1 -0
  119. src/services/rag/pipelines/llamaindex.py +99 -0
  120. src/services/rag/pipelines/raganything.py +67 -100
  121. src/services/rag/pipelines/raganything_docling.py +368 -0
  122. src/services/rag/service.py +5 -12
  123. src/services/rag/types.py +1 -0
  124. src/services/rag/utils/__init__.py +17 -0
  125. src/services/rag/utils/image_migration.py +279 -0
  126. src/services/search/__init__.py +1 -0
  127. src/services/search/base.py +1 -0
  128. src/services/search/consolidation.py +1 -0
  129. src/services/search/providers/__init__.py +1 -0
  130. src/services/search/providers/baidu.py +1 -0
  131. src/services/search/providers/exa.py +1 -0
  132. src/services/search/providers/jina.py +1 -0
  133. src/services/search/providers/perplexity.py +1 -0
  134. src/services/search/providers/serper.py +1 -0
  135. src/services/search/providers/tavily.py +1 -0
  136. src/services/search/types.py +1 -0
  137. src/services/settings/__init__.py +1 -0
  138. src/services/settings/interface_settings.py +78 -0
  139. src/services/setup/__init__.py +1 -0
  140. src/services/tts/__init__.py +1 -0
  141. src/services/tts/config.py +1 -0
  142. src/utils/realtimex.py +284 -0
  143. realtimex_deeptutor-0.5.0.post1.dist-info/entry_points.txt +0 -2
  144. src/services/rag/pipelines/academic.py +0 -44
  145. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Generate Stargazers and Forkers roster SVG images for GitHub README.
4
+ Modern, minimal style - shows latest users with "and X others" text.
5
+
6
+ Usage:
7
+ python generate_roster.py --repo HKUDS/DeepTutor --output assets/roster
8
+ """
9
+
10
+ import argparse
11
+ import base64
12
+ from concurrent.futures import ThreadPoolExecutor, as_completed
13
+ import json
14
+ import os
15
+ import ssl
16
+ import urllib.request
17
+
18
+ # Create SSL context that doesn't verify certificates (for macOS compatibility)
19
+ ssl_context = ssl.create_default_context()
20
+ ssl_context.check_hostname = False
21
+ ssl_context.verify_mode = ssl.CERT_NONE
22
+
23
+
24
+ def fetch_repo_stats(owner: str, repo: str, token: str = None) -> dict:
25
+ """Fetch repository statistics (accurate counts) from GitHub API."""
26
+ headers = {"Accept": "application/vnd.github.v3+json", "User-Agent": "Repo-Roster-Generator"}
27
+ if token:
28
+ headers["Authorization"] = f"token {token}"
29
+
30
+ url = f"https://api.github.com/repos/{owner}/{repo}"
31
+ req = urllib.request.Request(url, headers=headers)
32
+
33
+ try:
34
+ with urllib.request.urlopen(req, timeout=30, context=ssl_context) as response:
35
+ data = json.loads(response.read().decode())
36
+ return {
37
+ "stargazers_count": data.get("stargazers_count", 0),
38
+ "forks_count": data.get("forks_count", 0),
39
+ }
40
+ except Exception as e:
41
+ print(f"Error fetching repo stats: {e}")
42
+ return {"stargazers_count": 0, "forks_count": 0}
43
+
44
+
45
+ def fetch_github_api(url: str, token: str = None, count_only: bool = False) -> tuple:
46
+ """Fetch data from GitHub API with pagination. Returns (users, total_count)."""
47
+ headers = {"Accept": "application/vnd.github.v3+json", "User-Agent": "Repo-Roster-Generator"}
48
+ if token:
49
+ headers["Authorization"] = f"token {token}"
50
+
51
+ all_data = []
52
+ page = 1
53
+ per_page = 100
54
+ total_count = 0
55
+
56
+ while True:
57
+ paginated_url = f"{url}?per_page={per_page}&page={page}"
58
+ req = urllib.request.Request(paginated_url, headers=headers)
59
+
60
+ try:
61
+ with urllib.request.urlopen(req, timeout=30, context=ssl_context) as response:
62
+ data = json.loads(response.read().decode())
63
+ if not data:
64
+ break
65
+ all_data.extend(data)
66
+ total_count += len(data)
67
+ if len(data) < per_page:
68
+ break
69
+ page += 1
70
+ # Keep counting but limit data collection
71
+ if len(all_data) >= 500:
72
+ # Continue counting total
73
+ while True:
74
+ page += 1
75
+ paginated_url = f"{url}?per_page={per_page}&page={page}"
76
+ req = urllib.request.Request(paginated_url, headers=headers)
77
+ try:
78
+ with urllib.request.urlopen(
79
+ req, timeout=30, context=ssl_context
80
+ ) as resp:
81
+ more_data = json.loads(resp.read().decode())
82
+ if not more_data:
83
+ break
84
+ total_count += len(more_data)
85
+ if len(more_data) < per_page:
86
+ break
87
+ except:
88
+ break
89
+ break
90
+ except Exception as e:
91
+ print(f"Error fetching {paginated_url}: {e}")
92
+ break
93
+
94
+ return all_data, total_count
95
+
96
+
97
+ def fetch_avatar_as_base64(avatar_url: str, size: int = 48) -> str:
98
+ """Fetch avatar image and convert to base64 data URI."""
99
+ try:
100
+ if "?" in avatar_url:
101
+ avatar_url += f"&s={size}"
102
+ else:
103
+ avatar_url += f"?s={size}"
104
+
105
+ req = urllib.request.Request(avatar_url, headers={"User-Agent": "Repo-Roster-Generator"})
106
+ with urllib.request.urlopen(req, timeout=10, context=ssl_context) as response:
107
+ data = response.read()
108
+ content_type = response.headers.get("Content-Type", "image/png")
109
+ base64_data = base64.b64encode(data).decode("utf-8")
110
+ return f"data:{content_type};base64,{base64_data}"
111
+ except Exception as e:
112
+ print(f"Error fetching avatar {avatar_url}: {e}")
113
+ return None
114
+
115
+
116
+ def generate_modern_roster_svg(
117
+ users: list,
118
+ total_count: int,
119
+ title: str,
120
+ theme: str = "dark",
121
+ display_count: int = 6,
122
+ avatar_size: int = 36,
123
+ ) -> str:
124
+ """Generate modern minimal SVG with overlapping avatars."""
125
+
126
+ # Get latest users (reverse to show newest first)
127
+ display_users = (
128
+ list(reversed(users[-display_count:]))
129
+ if len(users) >= display_count
130
+ else list(reversed(users))
131
+ )
132
+ others_count = total_count - len(display_users)
133
+
134
+ if not display_users:
135
+ return generate_empty_svg(title, theme)
136
+
137
+ # Theme colors
138
+ if theme == "dark":
139
+ bg_color = "#0d1117"
140
+ text_color = "#e6edf3"
141
+ muted_color = "#8b949e"
142
+ border_color = "#30363d"
143
+ accent_color = "#58a6ff"
144
+ else:
145
+ bg_color = "#ffffff"
146
+ text_color = "#1f2328"
147
+ muted_color = "#656d76"
148
+ border_color = "#d0d7de"
149
+ accent_color = "#0969da"
150
+
151
+ # Calculate dimensions
152
+ overlap = 12 # How much avatars overlap
153
+ avatar_spacing = avatar_size - overlap
154
+ avatars_width = avatar_size + (len(display_users) - 1) * avatar_spacing
155
+
156
+ padding_x = 24
157
+ padding_y = 16
158
+
159
+ # Text measurements (approximate)
160
+ title_width = len(title) * 9
161
+ others_text = f"and {others_count:,} others" if others_count > 0 else ""
162
+ others_width = len(others_text) * 7 if others_text else 0
163
+
164
+ total_content_width = avatars_width + 16 + others_width
165
+ width = max(total_content_width + padding_x * 2, 280)
166
+ height = avatar_size + padding_y * 2 + 28 # Extra space for title
167
+
168
+ # Fetch avatars
169
+ print(f"Fetching {len(display_users)} avatars for {title}...")
170
+ avatar_cache = {}
171
+
172
+ with ThreadPoolExecutor(max_workers=10) as executor:
173
+ future_to_user = {
174
+ executor.submit(fetch_avatar_as_base64, user["avatar_url"], avatar_size * 2): user[
175
+ "login"
176
+ ]
177
+ for user in display_users
178
+ }
179
+ for future in as_completed(future_to_user):
180
+ login = future_to_user[future]
181
+ try:
182
+ avatar_cache[login] = future.result()
183
+ except Exception as e:
184
+ print(f"Error processing {login}: {e}")
185
+
186
+ # Build SVG
187
+ svg_parts = [
188
+ f'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}" viewBox="0 0 {width} {height}">',
189
+ "<defs>",
190
+ "<style>",
191
+ '@import url("https://fonts.googleapis.com/css2?family=Inter:wght@500;600&amp;display=swap");',
192
+ '.title { font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-weight: 600; }',
193
+ '.others { font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-weight: 500; }',
194
+ "</style>",
195
+ "</defs>",
196
+ f'<rect width="100%" height="100%" fill="{bg_color}" rx="12"/>',
197
+ ]
198
+
199
+ # Title
200
+ svg_parts.append(
201
+ f'<text x="{padding_x}" y="{padding_y + 14}" class="title" fill="{text_color}" font-size="14">{title}</text>'
202
+ )
203
+
204
+ # Avatars row
205
+ avatar_y = padding_y + 28
206
+
207
+ for i, user in enumerate(display_users):
208
+ x = padding_x + i * avatar_spacing
209
+ avatar_data = avatar_cache.get(user["login"])
210
+
211
+ clip_id = f"clip-{i}"
212
+ svg_parts.append(
213
+ f'<clipPath id="{clip_id}"><circle cx="{x + avatar_size / 2}" cy="{avatar_y + avatar_size / 2}" r="{avatar_size / 2}"/></clipPath>'
214
+ )
215
+
216
+ # White/dark border ring for depth
217
+ svg_parts.append(
218
+ f'<circle cx="{x + avatar_size / 2}" cy="{avatar_y + avatar_size / 2}" r="{avatar_size / 2 + 2}" fill="{bg_color}"/>'
219
+ )
220
+
221
+ if avatar_data:
222
+ svg_parts.append(
223
+ f'<image x="{x}" y="{avatar_y}" width="{avatar_size}" height="{avatar_size}" '
224
+ f'clip-path="url(#{clip_id})" xlink:href="{avatar_data}" preserveAspectRatio="xMidYMid slice"/>'
225
+ )
226
+ else:
227
+ svg_parts.append(
228
+ f'<circle cx="{x + avatar_size / 2}" cy="{avatar_y + avatar_size / 2}" r="{avatar_size / 2}" fill="{border_color}"/>'
229
+ )
230
+ svg_parts.append(
231
+ f'<text x="{x + avatar_size / 2}" y="{avatar_y + avatar_size / 2 + 5}" text-anchor="middle" '
232
+ f'fill="{text_color}" font-size="14" font-weight="600">{user["login"][0].upper()}</text>'
233
+ )
234
+
235
+ # "and X others" text
236
+ if others_count > 0:
237
+ text_x = padding_x + avatars_width + 12
238
+ text_y = avatar_y + avatar_size / 2 + 5
239
+ svg_parts.append(
240
+ f'<text x="{text_x}" y="{text_y}" class="others" fill="{muted_color}" font-size="13">and {others_count:,} others</text>'
241
+ )
242
+
243
+ svg_parts.append("</svg>")
244
+
245
+ return "\n".join(svg_parts)
246
+
247
+
248
+ def generate_empty_svg(title: str, theme: str) -> str:
249
+ """Generate empty state SVG."""
250
+ if theme == "dark":
251
+ bg_color = "#0d1117"
252
+ text_color = "#8b949e"
253
+ else:
254
+ bg_color = "#ffffff"
255
+ text_color = "#656d76"
256
+
257
+ return f'''<svg xmlns="http://www.w3.org/2000/svg" width="200" height="60">
258
+ <rect width="100%" height="100%" fill="{bg_color}" rx="12"/>
259
+ <text x="100" y="35" text-anchor="middle" fill="{text_color}"
260
+ font-family="Inter, -apple-system, sans-serif" font-size="13" font-weight="500">
261
+ No {title.lower()} yet
262
+ </text>
263
+ </svg>'''
264
+
265
+
266
+ def main():
267
+ parser = argparse.ArgumentParser(description="Generate GitHub roster SVG images")
268
+ parser.add_argument("--repo", required=True, help="GitHub repo in format owner/repo")
269
+ parser.add_argument("--output", default="assets/roster", help="Output directory")
270
+ parser.add_argument("--theme", default="dark", choices=["dark", "light"], help="Color theme")
271
+ parser.add_argument("--display", type=int, default=6, help="Number of avatars to display")
272
+ parser.add_argument("--token", default=os.environ.get("GITHUB_TOKEN"), help="GitHub token")
273
+
274
+ args = parser.parse_args()
275
+
276
+ owner, repo = args.repo.split("/")
277
+
278
+ # Create output directory
279
+ os.makedirs(args.output, exist_ok=True)
280
+
281
+ # Fetch accurate counts from repo API
282
+ print(f"Fetching repo stats for {args.repo}...")
283
+ repo_stats = fetch_repo_stats(owner, repo, args.token)
284
+ stargazers_total = repo_stats["stargazers_count"]
285
+ forks_total = repo_stats["forks_count"]
286
+ print(f"Repo stats: {stargazers_total:,} stars, {forks_total:,} forks")
287
+
288
+ # Fetch stargazers (only need latest ones for avatars)
289
+ print(f"Fetching stargazers for {args.repo}...")
290
+ stargazers_url = f"https://api.github.com/repos/{owner}/{repo}/stargazers"
291
+ stargazers, _ = fetch_github_api(stargazers_url, args.token)
292
+ print(f"Fetched {len(stargazers)} stargazer records for avatars")
293
+
294
+ # Fetch forkers (only need latest ones for avatars)
295
+ print(f"Fetching forkers for {args.repo}...")
296
+ forks_url = f"https://api.github.com/repos/{owner}/{repo}/forks"
297
+ forks, _ = fetch_github_api(forks_url, args.token)
298
+ forkers = [
299
+ {"login": f["owner"]["login"], "avatar_url": f["owner"]["avatar_url"]} for f in forks
300
+ ]
301
+ print(f"Fetched {len(forkers)} forker records for avatars")
302
+
303
+ # Generate stargazers SVG
304
+ print("Generating stargazers SVG...")
305
+ stargazers_svg = generate_modern_roster_svg(
306
+ stargazers, stargazers_total, "Stargazers", theme=args.theme, display_count=args.display
307
+ )
308
+ stargazers_path = os.path.join(args.output, "stargazers.svg")
309
+ with open(stargazers_path, "w", encoding="utf-8") as f:
310
+ f.write(stargazers_svg)
311
+ print(f"Saved: {stargazers_path}")
312
+
313
+ # Generate forkers SVG
314
+ print("Generating forkers SVG...")
315
+ forkers_svg = generate_modern_roster_svg(
316
+ forkers, forks_total, "Forkers", theme=args.theme, display_count=args.display
317
+ )
318
+ forkers_path = os.path.join(args.output, "forkers.svg")
319
+ with open(forkers_path, "w", encoding="utf-8") as f:
320
+ f.write(forkers_svg)
321
+ print(f"Saved: {forkers_path}")
322
+
323
+ print("Done!")
324
+
325
+
326
+ if __name__ == "__main__":
327
+ main()