deeptutor-knowledge 1.7.0__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 (168) hide show
  1. deeptutor/api/routers/knowledge.py +5225 -0
  2. deeptutor/api/routers/marginnote4.py +278 -0
  3. deeptutor/capabilities/ima/__init__.py +6 -0
  4. deeptutor/capabilities/ima/binding.py +118 -0
  5. deeptutor/capabilities/ima/capability.py +92 -0
  6. deeptutor/capabilities/ima/prompts/en/system.md +33 -0
  7. deeptutor/capabilities/ima/prompts/zh/system.md +24 -0
  8. deeptutor/capabilities/ima/tools.py +476 -0
  9. deeptutor/capabilities/marginnote4/__init__.py +13 -0
  10. deeptutor/capabilities/marginnote4/binding.py +66 -0
  11. deeptutor/capabilities/marginnote4/capability.py +80 -0
  12. deeptutor/capabilities/marginnote4/models.py +173 -0
  13. deeptutor/capabilities/marginnote4/prompts/en/system.md +54 -0
  14. deeptutor/capabilities/marginnote4/prompts/zh/system.md +44 -0
  15. deeptutor/capabilities/marginnote4/store.py +562 -0
  16. deeptutor/capabilities/marginnote4/tools.py +329 -0
  17. deeptutor/capabilities/obsidian/__init__.py +6 -0
  18. deeptutor/capabilities/obsidian/binding.py +75 -0
  19. deeptutor/capabilities/obsidian/capability.py +82 -0
  20. deeptutor/capabilities/obsidian/prompts/en/system.md +32 -0
  21. deeptutor/capabilities/obsidian/prompts/zh/system.md +28 -0
  22. deeptutor/capabilities/obsidian/tools.py +399 -0
  23. deeptutor/capabilities/obsidian/vault.py +341 -0
  24. deeptutor/knowledge/__init__.py +28 -0
  25. deeptutor/knowledge/add_documents.py +734 -0
  26. deeptutor/knowledge/indexing_run.py +432 -0
  27. deeptutor/knowledge/initializer.py +448 -0
  28. deeptutor/knowledge/manager.py +2570 -0
  29. deeptutor/knowledge/module_service.py +29 -0
  30. deeptutor/knowledge/naming.py +40 -0
  31. deeptutor/knowledge/progress_events.py +45 -0
  32. deeptutor/knowledge/progress_tracker.py +355 -0
  33. deeptutor/knowledge/staging.py +69 -0
  34. deeptutor/services/config/knowledge_base_config.py +239 -0
  35. deeptutor/services/embedding/__init__.py +46 -0
  36. deeptutor/services/embedding/adapters/__init__.py +40 -0
  37. deeptutor/services/embedding/adapters/base.py +196 -0
  38. deeptutor/services/embedding/adapters/cohere.py +176 -0
  39. deeptutor/services/embedding/adapters/dashscope_native.py +242 -0
  40. deeptutor/services/embedding/adapters/gemini.py +462 -0
  41. deeptutor/services/embedding/adapters/jina.py +161 -0
  42. deeptutor/services/embedding/adapters/ollama.py +145 -0
  43. deeptutor/services/embedding/adapters/openai_compatible.py +406 -0
  44. deeptutor/services/embedding/adapters/openai_sdk.py +150 -0
  45. deeptutor/services/embedding/client.py +323 -0
  46. deeptutor/services/embedding/config.py +114 -0
  47. deeptutor/services/embedding/request_options.py +46 -0
  48. deeptutor/services/embedding/validation.py +128 -0
  49. deeptutor/services/github_source/__init__.py +5 -0
  50. deeptutor/services/github_source/client.py +164 -0
  51. deeptutor/services/github_source/sync.py +384 -0
  52. deeptutor/services/github_source/sync_service.py +72 -0
  53. deeptutor/services/rag/__init__.py +21 -0
  54. deeptutor/services/rag/embedding_binding.py +331 -0
  55. deeptutor/services/rag/embedding_signature.py +63 -0
  56. deeptutor/services/rag/eval/__init__.py +92 -0
  57. deeptutor/services/rag/eval/dataset.py +244 -0
  58. deeptutor/services/rag/eval/diff.py +312 -0
  59. deeptutor/services/rag/eval/matching.py +218 -0
  60. deeptutor/services/rag/eval/metrics.py +213 -0
  61. deeptutor/services/rag/eval/report.py +111 -0
  62. deeptutor/services/rag/eval/runner.py +309 -0
  63. deeptutor/services/rag/factory.py +369 -0
  64. deeptutor/services/rag/file_routing.py +5 -0
  65. deeptutor/services/rag/index_probe.py +327 -0
  66. deeptutor/services/rag/index_versioning.py +387 -0
  67. deeptutor/services/rag/kb_paths.py +50 -0
  68. deeptutor/services/rag/linked_kb.py +237 -0
  69. deeptutor/services/rag/pipelines/__init__.py +7 -0
  70. deeptutor/services/rag/pipelines/base.py +41 -0
  71. deeptutor/services/rag/pipelines/graphrag/__init__.py +18 -0
  72. deeptutor/services/rag/pipelines/graphrag/compatibility.py +42 -0
  73. deeptutor/services/rag/pipelines/graphrag/completion_adapter.py +251 -0
  74. deeptutor/services/rag/pipelines/graphrag/config.py +357 -0
  75. deeptutor/services/rag/pipelines/graphrag/engine.py +479 -0
  76. deeptutor/services/rag/pipelines/graphrag/errors.py +328 -0
  77. deeptutor/services/rag/pipelines/graphrag/ingestion.py +135 -0
  78. deeptutor/services/rag/pipelines/graphrag/pandas_compat.py +68 -0
  79. deeptutor/services/rag/pipelines/graphrag/pipeline.py +284 -0
  80. deeptutor/services/rag/pipelines/graphrag/provider.py +134 -0
  81. deeptutor/services/rag/pipelines/graphrag/storage.py +127 -0
  82. deeptutor/services/rag/pipelines/ima/__init__.py +28 -0
  83. deeptutor/services/rag/pipelines/ima/client.py +364 -0
  84. deeptutor/services/rag/pipelines/ima/config.py +140 -0
  85. deeptutor/services/rag/pipelines/ima/envelope.py +109 -0
  86. deeptutor/services/rag/pipelines/ima/inventory.py +209 -0
  87. deeptutor/services/rag/pipelines/ima/media.py +190 -0
  88. deeptutor/services/rag/pipelines/ima/models.py +348 -0
  89. deeptutor/services/rag/pipelines/ima/notes.py +240 -0
  90. deeptutor/services/rag/pipelines/ima/pipeline.py +238 -0
  91. deeptutor/services/rag/pipelines/ima/probe.py +105 -0
  92. deeptutor/services/rag/pipelines/ima/sources.py +105 -0
  93. deeptutor/services/rag/pipelines/ima/transport.py +130 -0
  94. deeptutor/services/rag/pipelines/kiwix/__init__.py +1 -0
  95. deeptutor/services/rag/pipelines/kiwix/client.py +369 -0
  96. deeptutor/services/rag/pipelines/kiwix/pipeline.py +75 -0
  97. deeptutor/services/rag/pipelines/lightrag/README.md +78 -0
  98. deeptutor/services/rag/pipelines/lightrag/__init__.py +15 -0
  99. deeptutor/services/rag/pipelines/lightrag/block_policy.py +266 -0
  100. deeptutor/services/rag/pipelines/lightrag/cache_reuse.py +137 -0
  101. deeptutor/services/rag/pipelines/lightrag/config.py +453 -0
  102. deeptutor/services/rag/pipelines/lightrag/engine.py +536 -0
  103. deeptutor/services/rag/pipelines/lightrag/indexing_policy.py +696 -0
  104. deeptutor/services/rag/pipelines/lightrag/ingress.py +364 -0
  105. deeptutor/services/rag/pipelines/lightrag/parser.py +99 -0
  106. deeptutor/services/rag/pipelines/lightrag/pipeline.py +692 -0
  107. deeptutor/services/rag/pipelines/lightrag/roles.py +132 -0
  108. deeptutor/services/rag/pipelines/lightrag/sidecar.py +320 -0
  109. deeptutor/services/rag/pipelines/lightrag/storage.py +338 -0
  110. deeptutor/services/rag/pipelines/lightrag/worker.py +259 -0
  111. deeptutor/services/rag/pipelines/lightrag/write_lock.py +55 -0
  112. deeptutor/services/rag/pipelines/lightrag_server/__init__.py +15 -0
  113. deeptutor/services/rag/pipelines/lightrag_server/client.py +149 -0
  114. deeptutor/services/rag/pipelines/lightrag_server/config.py +68 -0
  115. deeptutor/services/rag/pipelines/lightrag_server/pipeline.py +120 -0
  116. deeptutor/services/rag/pipelines/lightrag_server/probe.py +110 -0
  117. deeptutor/services/rag/pipelines/llamaindex/__init__.py +3 -0
  118. deeptutor/services/rag/pipelines/llamaindex/config.py +199 -0
  119. deeptutor/services/rag/pipelines/llamaindex/document_loader.py +648 -0
  120. deeptutor/services/rag/pipelines/llamaindex/embedding_adapter.py +264 -0
  121. deeptutor/services/rag/pipelines/llamaindex/errors.py +76 -0
  122. deeptutor/services/rag/pipelines/llamaindex/exercise_lookup.py +373 -0
  123. deeptutor/services/rag/pipelines/llamaindex/ingestion.py +129 -0
  124. deeptutor/services/rag/pipelines/llamaindex/pipeline.py +556 -0
  125. deeptutor/services/rag/pipelines/llamaindex/rerank.py +130 -0
  126. deeptutor/services/rag/pipelines/llamaindex/retrievers.py +360 -0
  127. deeptutor/services/rag/pipelines/llamaindex/storage.py +655 -0
  128. deeptutor/services/rag/pipelines/llamaindex/vector_store.py +288 -0
  129. deeptutor/services/rag/pipelines/modes.py +48 -0
  130. deeptutor/services/rag/pipelines/pageindex/__init__.py +47 -0
  131. deeptutor/services/rag/pipelines/pageindex/client.py +146 -0
  132. deeptutor/services/rag/pipelines/pageindex/config.py +49 -0
  133. deeptutor/services/rag/pipelines/pageindex/pipeline.py +313 -0
  134. deeptutor/services/rag/pipelines/pageindex/reasoning.py +262 -0
  135. deeptutor/services/rag/pipelines/pageindex/storage.py +133 -0
  136. deeptutor/services/rag/pipelines/pageindex/tools.py +221 -0
  137. deeptutor/services/rag/pipelines/weknora/__init__.py +13 -0
  138. deeptutor/services/rag/pipelines/weknora/client.py +106 -0
  139. deeptutor/services/rag/pipelines/weknora/config.py +56 -0
  140. deeptutor/services/rag/pipelines/weknora/pipeline.py +74 -0
  141. deeptutor/services/rag/pipelines/weknora/probe.py +80 -0
  142. deeptutor/services/rag/preflight.py +274 -0
  143. deeptutor/services/rag/provider_binding.py +70 -0
  144. deeptutor/services/rag/query_rewrite.py +186 -0
  145. deeptutor/services/rag/service.py +379 -0
  146. deeptutor/services/rag/smart_retriever.py +81 -0
  147. deeptutor/services/rag/source_pages.py +292 -0
  148. deeptutor/services/rag/source_visuals.py +434 -0
  149. deeptutor/services/rag/visual_assets.py +430 -0
  150. deeptutor/services/rag/visual_coverage.py +165 -0
  151. deeptutor/services/web_source/__init__.py +1 -0
  152. deeptutor/services/web_source/bilingual.py +255 -0
  153. deeptutor/services/web_source/crawler.py +681 -0
  154. deeptutor/services/web_source/html_extractor.py +643 -0
  155. deeptutor/services/web_source/markdown.py +28 -0
  156. deeptutor/services/web_source/navigation.py +72 -0
  157. deeptutor/services/web_source/repository.py +640 -0
  158. deeptutor/services/web_source/robots.py +182 -0
  159. deeptutor/services/web_source/scheduler.py +336 -0
  160. deeptutor/services/web_source/snapshot_assets.py +171 -0
  161. deeptutor/services/web_source/sync.py +253 -0
  162. deeptutor/services/workspace/kb_move.py +446 -0
  163. deeptutor/tools/rag_tool.py +110 -0
  164. deeptutor_cli/kb.py +756 -0
  165. deeptutor_knowledge-1.7.0.dist-info/METADATA +15 -0
  166. deeptutor_knowledge-1.7.0.dist-info/RECORD +168 -0
  167. deeptutor_knowledge-1.7.0.dist-info/WHEEL +5 -0
  168. deeptutor_knowledge-1.7.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,255 @@
1
+ """Bilingual source pairing and language variant alignment for web sources.
2
+
3
+ Detects language variants (via hreflang links or URL language patterns),
4
+ associates variant pages with canonical source pages, and prepares pairing
5
+ records for storage and API exposure.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ from typing import Any
12
+ from urllib.parse import urlparse
13
+
14
+ # Canonical supported language codes and their common regional/script aliases.
15
+ _CANONICAL_LANGUAGES: dict[str, str] = {
16
+ "en": "en",
17
+ "en-us": "en",
18
+ "en-gb": "en",
19
+ "en-au": "en",
20
+ "en-ca": "en",
21
+ "zh": "zh",
22
+ "zh-cn": "zh",
23
+ "zh-hans": "zh",
24
+ "zh-sg": "zh",
25
+ "zh-tw": "zh",
26
+ "zh-hant": "zh",
27
+ "zh-hk": "zh",
28
+ "ja": "ja",
29
+ "ja-jp": "ja",
30
+ "ko": "ko",
31
+ "ko-kr": "ko",
32
+ "fr": "fr",
33
+ "fr-fr": "fr",
34
+ "fr-ca": "fr",
35
+ "de": "de",
36
+ "de-de": "de",
37
+ "de-at": "de",
38
+ "de-ch": "de",
39
+ "es": "es",
40
+ "es-es": "es",
41
+ "es-419": "es",
42
+ "es-mx": "es",
43
+ "ru": "ru",
44
+ "ru-ru": "ru",
45
+ "pt": "pt",
46
+ "pt-br": "pt",
47
+ "pt-pt": "pt",
48
+ "it": "it",
49
+ "it-it": "it",
50
+ }
51
+
52
+ _KNOWN_LANG_CODES = frozenset(_CANONICAL_LANGUAGES.keys())
53
+
54
+ SUPPORTED_LANGUAGE_PAIRS = frozenset(
55
+ {
56
+ frozenset({"en", "zh"}),
57
+ frozenset({"en", "ja"}),
58
+ frozenset({"en", "ko"}),
59
+ frozenset({"en", "fr"}),
60
+ frozenset({"en", "de"}),
61
+ frozenset({"en", "es"}),
62
+ frozenset({"en", "ru"}),
63
+ frozenset({"en", "pt"}),
64
+ frozenset({"en", "it"}),
65
+ }
66
+ )
67
+
68
+
69
+ def normalize_language(code: str | None) -> str:
70
+ """Normalize a language tag to its canonical primary language code.
71
+
72
+ Examples:
73
+ 'zh-CN' -> 'zh'
74
+ 'en-US' -> 'en'
75
+ 'zh-Hans' -> 'zh'
76
+ 'FR' -> 'fr'
77
+ 'x-default' -> ''
78
+ """
79
+ if not code:
80
+ return ""
81
+ cleaned = code.strip().lower().replace("_", "-")
82
+ if cleaned in ("x-default", "default"):
83
+ return ""
84
+ if cleaned in _CANONICAL_LANGUAGES:
85
+ return _CANONICAL_LANGUAGES[cleaned]
86
+ primary = cleaned.split("-", 1)[0]
87
+ return _CANONICAL_LANGUAGES.get(primary, primary)
88
+
89
+
90
+ def is_supported_pair(lang_a: str, lang_b: str) -> bool:
91
+ """Check if two languages form a supported bilingual pairing."""
92
+ if not lang_a or not lang_b or lang_a == lang_b:
93
+ return False
94
+ pair = frozenset({lang_a, lang_b})
95
+ if pair in SUPPORTED_LANGUAGE_PAIRS:
96
+ return True
97
+ return "en" in pair or "zh" in pair
98
+
99
+
100
+ def extract_url_language_and_stem(url: str) -> tuple[str, str]:
101
+ """Extract language code (if in URL path) and the language-agnostic stem path.
102
+
103
+ Handles leading language segments (/zh/docs/intro -> ("zh", "/docs/intro"))
104
+ and common nested prefixes (/docs/zh/intro -> ("zh", "/docs/intro")).
105
+ """
106
+ parsed = urlparse(url)
107
+ raw_path = parsed.path.rstrip("/")
108
+ if not raw_path:
109
+ return "", "/"
110
+
111
+ segments = [s for s in raw_path.split("/") if s]
112
+ if not segments:
113
+ return "", "/"
114
+
115
+ first_norm = normalize_language(segments[0])
116
+ if segments[0].lower() in _KNOWN_LANG_CODES or first_norm in SUPPORTED_LANGUAGE_PAIRS:
117
+ stem = "/" + "/".join(segments[1:])
118
+ return first_norm, stem or "/"
119
+
120
+ if len(segments) >= 2:
121
+ second_norm = normalize_language(segments[1])
122
+ if segments[1].lower() in _KNOWN_LANG_CODES or second_norm in SUPPORTED_LANGUAGE_PAIRS:
123
+ stem = f"/{segments[0]}" + ("/" + "/".join(segments[2:]) if len(segments) > 2 else "")
124
+ return second_norm, stem or "/"
125
+
126
+ return "", raw_path or "/"
127
+
128
+
129
+ def detect_bilingual_pairings(
130
+ pages: list[Any],
131
+ page_file_map: dict[str, str] | None = None,
132
+ ) -> list[dict[str, Any]]:
133
+ """Detect bilingual page pairings among crawled pages.
134
+
135
+ Uses both explicit hreflang alternate declarations and URL language prefix
136
+ patterns. Returns a deduplicated, deterministically sorted list of pairing
137
+ records.
138
+ """
139
+ if not pages:
140
+ return []
141
+
142
+ file_map = page_file_map or {}
143
+ pages_by_url = {page.url: page for page in pages}
144
+ pairings: dict[str, dict[str, Any]] = {}
145
+
146
+ # 1. Hreflang alternate links (highest confidence)
147
+ for page in pages:
148
+ p1_lang = (
149
+ normalize_language(getattr(page, "language", ""))
150
+ or extract_url_language_and_stem(page.url)[0]
151
+ or "en"
152
+ )
153
+ alternate_links: dict[str, str] = getattr(page, "alternate_links", {})
154
+ for hreflang, target_url in alternate_links.items():
155
+ if target_url not in pages_by_url or target_url == page.url:
156
+ continue
157
+ target_page = pages_by_url[target_url]
158
+ p2_lang = (
159
+ normalize_language(hreflang)
160
+ or normalize_language(getattr(target_page, "language", ""))
161
+ or extract_url_language_and_stem(target_url)[0]
162
+ or "zh"
163
+ )
164
+ if not is_supported_pair(p1_lang, p2_lang):
165
+ continue
166
+
167
+ if p1_lang == "en":
168
+ src, tgt = page, target_page
169
+ s_lang, t_lang = p1_lang, p2_lang
170
+ elif p2_lang == "en":
171
+ src, tgt = target_page, page
172
+ s_lang, t_lang = p2_lang, p1_lang
173
+ elif page.url <= target_page.url:
174
+ src, tgt = page, target_page
175
+ s_lang, t_lang = p1_lang, p2_lang
176
+ else:
177
+ src, tgt = target_page, page
178
+ s_lang, t_lang = p2_lang, p1_lang
179
+
180
+ pairing_id = hashlib.sha256(f"{src.url}::{tgt.url}".encode("utf-8")).hexdigest()[:16]
181
+ pairings[pairing_id] = {
182
+ "pairing_id": pairing_id,
183
+ "source_url": src.url,
184
+ "target_url": tgt.url,
185
+ "source_file": file_map.get(src.url, ""),
186
+ "target_file": file_map.get(tgt.url, ""),
187
+ "source_lang": s_lang,
188
+ "target_lang": t_lang,
189
+ "pairing_method": "hreflang",
190
+ }
191
+
192
+ # 2. URL language stem pattern matching
193
+ stem_groups: dict[str, list[tuple[Any, str]]] = {}
194
+ for page in pages:
195
+ url_lang, stem = extract_url_language_and_stem(page.url)
196
+ page_lang = normalize_language(getattr(page, "language", "")) or url_lang
197
+ stem_groups.setdefault(stem, []).append((page, page_lang))
198
+
199
+ for stem, group in stem_groups.items():
200
+ if len(group) < 2:
201
+ continue
202
+ for i in range(len(group)):
203
+ for j in range(i + 1, len(group)):
204
+ p1, l1 = group[i]
205
+ p2, l2 = group[j]
206
+
207
+ # If one page is unprefixed and the other has a language tag,
208
+ # infer default 'en' for the unprefixed base documentation page.
209
+ if not l1 and l2 and l2 != "en":
210
+ l1 = "en"
211
+ elif not l2 and l1 and l1 != "en":
212
+ l2 = "en"
213
+
214
+ if not is_supported_pair(l1, l2):
215
+ continue
216
+
217
+ if l1 == "en":
218
+ src, tgt = p1, p2
219
+ s_lang, t_lang = l1, l2
220
+ elif l2 == "en":
221
+ src, tgt = p2, p1
222
+ s_lang, t_lang = l2, l1
223
+ elif p1.url <= p2.url:
224
+ src, tgt = p1, p2
225
+ s_lang, t_lang = l1, l2
226
+ else:
227
+ src, tgt = p2, p1
228
+ s_lang, t_lang = l2, l1
229
+
230
+ pairing_id = hashlib.sha256(f"{src.url}::{tgt.url}".encode("utf-8")).hexdigest()[
231
+ :16
232
+ ]
233
+ if pairing_id not in pairings:
234
+ pairings[pairing_id] = {
235
+ "pairing_id": pairing_id,
236
+ "source_url": src.url,
237
+ "target_url": tgt.url,
238
+ "source_file": file_map.get(src.url, ""),
239
+ "target_file": file_map.get(tgt.url, ""),
240
+ "source_lang": s_lang,
241
+ "target_lang": t_lang,
242
+ "pairing_method": "url_pattern",
243
+ }
244
+
245
+ result = list(pairings.values())
246
+ result.sort(key=lambda item: (item["source_url"], item["target_url"]))
247
+ return result
248
+
249
+
250
+ __all__ = [
251
+ "detect_bilingual_pairings",
252
+ "extract_url_language_and_stem",
253
+ "is_supported_pair",
254
+ "normalize_language",
255
+ ]