hdsp-jupyter-extension 2.0.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 (121) hide show
  1. agent_server/__init__.py +8 -0
  2. agent_server/core/__init__.py +92 -0
  3. agent_server/core/api_key_manager.py +427 -0
  4. agent_server/core/code_validator.py +1238 -0
  5. agent_server/core/context_condenser.py +308 -0
  6. agent_server/core/embedding_service.py +254 -0
  7. agent_server/core/error_classifier.py +577 -0
  8. agent_server/core/llm_client.py +95 -0
  9. agent_server/core/llm_service.py +649 -0
  10. agent_server/core/notebook_generator.py +274 -0
  11. agent_server/core/prompt_builder.py +35 -0
  12. agent_server/core/rag_manager.py +742 -0
  13. agent_server/core/reflection_engine.py +489 -0
  14. agent_server/core/retriever.py +248 -0
  15. agent_server/core/state_verifier.py +452 -0
  16. agent_server/core/summary_generator.py +484 -0
  17. agent_server/core/task_manager.py +198 -0
  18. agent_server/knowledge/__init__.py +9 -0
  19. agent_server/knowledge/watchdog_service.py +352 -0
  20. agent_server/main.py +160 -0
  21. agent_server/prompts/__init__.py +60 -0
  22. agent_server/prompts/file_action_prompts.py +113 -0
  23. agent_server/routers/__init__.py +9 -0
  24. agent_server/routers/agent.py +591 -0
  25. agent_server/routers/chat.py +188 -0
  26. agent_server/routers/config.py +100 -0
  27. agent_server/routers/file_resolver.py +293 -0
  28. agent_server/routers/health.py +42 -0
  29. agent_server/routers/rag.py +163 -0
  30. agent_server/schemas/__init__.py +60 -0
  31. hdsp_agent_core/__init__.py +158 -0
  32. hdsp_agent_core/factory.py +252 -0
  33. hdsp_agent_core/interfaces.py +203 -0
  34. hdsp_agent_core/knowledge/__init__.py +31 -0
  35. hdsp_agent_core/knowledge/chunking.py +356 -0
  36. hdsp_agent_core/knowledge/libraries/dask.md +188 -0
  37. hdsp_agent_core/knowledge/libraries/matplotlib.md +164 -0
  38. hdsp_agent_core/knowledge/libraries/polars.md +68 -0
  39. hdsp_agent_core/knowledge/loader.py +337 -0
  40. hdsp_agent_core/llm/__init__.py +13 -0
  41. hdsp_agent_core/llm/service.py +556 -0
  42. hdsp_agent_core/managers/__init__.py +22 -0
  43. hdsp_agent_core/managers/config_manager.py +133 -0
  44. hdsp_agent_core/managers/session_manager.py +251 -0
  45. hdsp_agent_core/models/__init__.py +115 -0
  46. hdsp_agent_core/models/agent.py +316 -0
  47. hdsp_agent_core/models/chat.py +41 -0
  48. hdsp_agent_core/models/common.py +95 -0
  49. hdsp_agent_core/models/rag.py +368 -0
  50. hdsp_agent_core/prompts/__init__.py +63 -0
  51. hdsp_agent_core/prompts/auto_agent_prompts.py +1260 -0
  52. hdsp_agent_core/prompts/cell_action_prompts.py +98 -0
  53. hdsp_agent_core/services/__init__.py +18 -0
  54. hdsp_agent_core/services/agent_service.py +438 -0
  55. hdsp_agent_core/services/chat_service.py +205 -0
  56. hdsp_agent_core/services/rag_service.py +262 -0
  57. hdsp_agent_core/tests/__init__.py +1 -0
  58. hdsp_agent_core/tests/conftest.py +102 -0
  59. hdsp_agent_core/tests/test_factory.py +251 -0
  60. hdsp_agent_core/tests/test_services.py +326 -0
  61. hdsp_jupyter_extension-2.0.0.data/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +7 -0
  62. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/build_log.json +738 -0
  63. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/install.json +5 -0
  64. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/package.json +134 -0
  65. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js +4369 -0
  66. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +1 -0
  67. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js +12496 -0
  68. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js.map +1 -0
  69. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js +94 -0
  70. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js.map +1 -0
  71. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js +94 -0
  72. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js.map +1 -0
  73. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.dae97cde171e13b8c834.js +623 -0
  74. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.dae97cde171e13b8c834.js.map +1 -0
  75. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/style.js +4 -0
  76. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js +507 -0
  77. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js.map +1 -0
  78. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js +2071 -0
  79. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +1 -0
  80. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js +1059 -0
  81. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +1 -0
  82. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +376 -0
  83. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +1 -0
  84. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +60336 -0
  85. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +1 -0
  86. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js +7132 -0
  87. hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +1 -0
  88. hdsp_jupyter_extension-2.0.0.dist-info/METADATA +152 -0
  89. hdsp_jupyter_extension-2.0.0.dist-info/RECORD +121 -0
  90. hdsp_jupyter_extension-2.0.0.dist-info/WHEEL +4 -0
  91. hdsp_jupyter_extension-2.0.0.dist-info/licenses/LICENSE +21 -0
  92. jupyter_ext/__init__.py +233 -0
  93. jupyter_ext/_version.py +4 -0
  94. jupyter_ext/config.py +111 -0
  95. jupyter_ext/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +7 -0
  96. jupyter_ext/handlers.py +632 -0
  97. jupyter_ext/labextension/build_log.json +738 -0
  98. jupyter_ext/labextension/package.json +134 -0
  99. jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js +4369 -0
  100. jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +1 -0
  101. jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js +12496 -0
  102. jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js.map +1 -0
  103. jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js +94 -0
  104. jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js.map +1 -0
  105. jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js +94 -0
  106. jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js.map +1 -0
  107. jupyter_ext/labextension/static/remoteEntry.dae97cde171e13b8c834.js +623 -0
  108. jupyter_ext/labextension/static/remoteEntry.dae97cde171e13b8c834.js.map +1 -0
  109. jupyter_ext/labextension/static/style.js +4 -0
  110. jupyter_ext/labextension/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js +507 -0
  111. jupyter_ext/labextension/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js.map +1 -0
  112. jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js +2071 -0
  113. jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +1 -0
  114. jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js +1059 -0
  115. jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +1 -0
  116. jupyter_ext/labextension/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +376 -0
  117. jupyter_ext/labextension/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +1 -0
  118. jupyter_ext/labextension/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +60336 -0
  119. jupyter_ext/labextension/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +1 -0
  120. jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js +7132 -0
  121. jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +1 -0
@@ -0,0 +1,337 @@
1
+ """
2
+ Knowledge Base - Deterministic library detection for API guides
3
+
4
+ Keyword matching + regex based library detection for loading appropriate API guides.
5
+ (No LLM calls - saves tokens and improves reliability)
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import List, Dict, Optional, Set
10
+ import re
11
+
12
+ # Library descriptions (reference)
13
+ LIBRARY_DESCRIPTIONS: Dict[str, str] = {
14
+ 'matplotlib': 'Visualization, graphs, charts, plot, histogram, scatter plot, EDA, data visualization, used with seaborn',
15
+ 'dask': 'Large-scale data processing, pandas replacement, distributed processing, lazy evaluation, dd.read_csv',
16
+ 'polars': 'High-performance DataFrame, pandas replacement, Rust-based, pl.read_csv',
17
+ 'pyspark': 'Spark-based distributed processing, big data, SparkSession',
18
+ 'vaex': 'Large-scale data exploration, out-of-core processing',
19
+ 'modin': 'pandas acceleration, parallel processing',
20
+ 'ray': 'Distributed computing, parallel processing framework',
21
+ }
22
+
23
+
24
+ class LibraryDetector:
25
+ """
26
+ Deterministic library detection (no LLM calls).
27
+ Uses keyword matching + regex to detect required libraries from user requests.
28
+ """
29
+
30
+ # Explicit library mention patterns (highest priority)
31
+ EXPLICIT_PATTERNS: Dict[str, str] = {
32
+ r'\bdask\b': 'dask',
33
+ r'\bpolars\b': 'polars',
34
+ r'\bpyspark\b': 'pyspark',
35
+ r'\bvaex\b': 'vaex',
36
+ r'\bmodin\b': 'modin',
37
+ r'\bray\b': 'ray',
38
+ r'\bmatplotlib\b': 'matplotlib',
39
+ r'\bseaborn\b': 'matplotlib', # seaborn -> matplotlib guide
40
+ r'\bplt\.': 'matplotlib',
41
+ r'\bdd\.read': 'dask',
42
+ r'\bpl\.read': 'polars',
43
+ r'\bpl\.DataFrame': 'polars',
44
+ }
45
+
46
+ # Keyword scores per library (0.0 ~ 1.0)
47
+ KEYWORD_SCORES: Dict[str, Dict[str, float]] = {
48
+ 'dask': {
49
+ '대용량': 0.7,
50
+ 'big data': 0.7,
51
+ 'bigdata': 0.7,
52
+ '빅데이터': 0.7,
53
+ 'lazy': 0.8,
54
+ 'lazy evaluation': 0.9,
55
+ 'out-of-core': 0.9,
56
+ 'out of core': 0.9,
57
+ '분산 처리': 0.6,
58
+ 'distributed': 0.6,
59
+ 'parallel dataframe': 0.8,
60
+ '병렬 데이터프레임': 0.8,
61
+ },
62
+ 'polars': {
63
+ 'rust 기반': 0.9,
64
+ 'rust-based': 0.9,
65
+ 'fast dataframe': 0.7,
66
+ '고성능 dataframe': 0.7,
67
+ '빠른 데이터프레임': 0.7,
68
+ },
69
+ 'matplotlib': {
70
+ '시각화': 0.7,
71
+ 'visualization': 0.7,
72
+ 'visualize': 0.7,
73
+ 'plot': 0.7,
74
+ 'chart': 0.7,
75
+ 'graph': 0.6,
76
+ '그래프': 0.6,
77
+ '차트': 0.7,
78
+ 'histogram': 0.8,
79
+ '히스토그램': 0.8,
80
+ 'scatter': 0.8,
81
+ '산점도': 0.8,
82
+ 'line plot': 0.8,
83
+ '라인 플롯': 0.8,
84
+ 'bar chart': 0.8,
85
+ '막대 그래프': 0.8,
86
+ 'eda': 0.5,
87
+ '탐색적 데이터 분석': 0.6,
88
+ 'figure': 0.5,
89
+ 'subplot': 0.8,
90
+ 'heatmap': 0.7,
91
+ '히트맵': 0.7,
92
+ },
93
+ 'pyspark': {
94
+ 'spark': 0.9,
95
+ 'sparksession': 0.95,
96
+ 'spark session': 0.95,
97
+ 'rdd': 0.9,
98
+ 'hadoop': 0.7,
99
+ '클러스터': 0.6,
100
+ 'cluster': 0.6,
101
+ },
102
+ 'vaex': {
103
+ 'vaex': 1.0,
104
+ 'memory mapping': 0.8,
105
+ '메모리 매핑': 0.8,
106
+ },
107
+ 'modin': {
108
+ 'modin': 1.0,
109
+ 'pandas 가속': 0.8,
110
+ 'pandas acceleration': 0.8,
111
+ },
112
+ 'ray': {
113
+ 'ray': 0.9,
114
+ '분산 컴퓨팅': 0.7,
115
+ 'distributed computing': 0.7,
116
+ },
117
+ }
118
+
119
+ # Score threshold
120
+ SCORE_THRESHOLD = 0.7
121
+
122
+ def detect(
123
+ self,
124
+ request: str,
125
+ available_libraries: List[str],
126
+ imported_libraries: List[str] = None
127
+ ) -> List[str]:
128
+ """
129
+ Detect required libraries from user request.
130
+
131
+ Args:
132
+ request: User's natural language request
133
+ available_libraries: List of libraries with available guides
134
+ imported_libraries: Already imported libraries (optional)
135
+
136
+ Returns:
137
+ List of detected libraries
138
+ """
139
+ request_lower = request.lower()
140
+ detected: Set[str] = set()
141
+
142
+ # Step 1: Explicit pattern matching (highest priority)
143
+ for pattern, lib in self.EXPLICIT_PATTERNS.items():
144
+ if lib in available_libraries and re.search(pattern, request, re.IGNORECASE):
145
+ detected.add(lib)
146
+
147
+ # Step 2: Keyword scoring
148
+ for lib, keywords in self.KEYWORD_SCORES.items():
149
+ if lib in detected or lib not in available_libraries:
150
+ continue
151
+
152
+ max_score = 0.0
153
+ for keyword, score in keywords.items():
154
+ if keyword.lower() in request_lower:
155
+ max_score = max(max_score, score)
156
+
157
+ if max_score >= self.SCORE_THRESHOLD:
158
+ detected.add(lib)
159
+
160
+ # Step 3: Consider already imported libraries
161
+ if imported_libraries:
162
+ for lib in imported_libraries:
163
+ lib_lower = lib.lower()
164
+ # seaborn -> matplotlib
165
+ if lib_lower == 'seaborn' and 'matplotlib' in available_libraries:
166
+ detected.add('matplotlib')
167
+ elif lib_lower in available_libraries:
168
+ detected.add(lib_lower)
169
+
170
+ return list(detected)
171
+
172
+
173
+ # LibraryDetector singleton instance
174
+ _library_detector_instance: Optional[LibraryDetector] = None
175
+
176
+
177
+ def get_library_detector() -> LibraryDetector:
178
+ """Get singleton LibraryDetector"""
179
+ global _library_detector_instance
180
+ if _library_detector_instance is None:
181
+ _library_detector_instance = LibraryDetector()
182
+ return _library_detector_instance
183
+
184
+
185
+ # LLM library detection prompt
186
+ LIBRARY_DETECTION_PROMPT = '''Analyze the user's request and determine which libraries to use for code generation.
187
+
188
+ ## Available Library API Guides:
189
+ {library_list}
190
+
191
+ ## User Request:
192
+ {request}
193
+
194
+ ## Notebook Context:
195
+ - Already imported libraries: {imported_libraries}
196
+
197
+ ## Instructions:
198
+ 1. Analyze the user request **semantically**
199
+ 2. Select only libraries that will actually be used in code generation
200
+ 3. Example: "Apply dask" → select dask
201
+ 4. Example: "EDA with visualization" → select matplotlib
202
+ 5. Example: "Use dask instead of pandas" → select dask
203
+
204
+ ## Output Format (JSON only):
205
+ {{"libraries": ["library1", "library2"]}}
206
+
207
+ Empty array is also valid: {{"libraries": []}}
208
+ '''
209
+
210
+
211
+ class KnowledgeBase:
212
+ """Library knowledge loader"""
213
+
214
+ def __init__(self, knowledge_dir: Optional[str] = None):
215
+ if knowledge_dir:
216
+ self.knowledge_dir = Path(knowledge_dir)
217
+ else:
218
+ # Default path: knowledge/libraries
219
+ self.knowledge_dir = Path(__file__).parent / 'libraries'
220
+
221
+ self._cache: Dict[str, str] = {}
222
+
223
+ def get_library_list_for_prompt(self) -> str:
224
+ """Generate library list for LLM detection"""
225
+ available = self.list_available_libraries()
226
+ lines = []
227
+ for lib in available:
228
+ desc = LIBRARY_DESCRIPTIONS.get(lib, 'Other library')
229
+ lines.append(f"- **{lib}**: {desc}")
230
+ return "\n".join(lines)
231
+
232
+ def get_detection_prompt(self, request: str, imported_libraries: List[str] = None) -> str:
233
+ """Generate LLM library detection prompt"""
234
+ library_list = self.get_library_list_for_prompt()
235
+ imported = ", ".join(imported_libraries) if imported_libraries else "None"
236
+
237
+ return LIBRARY_DETECTION_PROMPT.format(
238
+ library_list=library_list,
239
+ request=request,
240
+ imported_libraries=imported
241
+ )
242
+
243
+ def load_library_guide(self, library: str) -> Optional[str]:
244
+ """
245
+ Load library guide file.
246
+
247
+ Args:
248
+ library: Library name (e.g., 'dask', 'polars')
249
+
250
+ Returns:
251
+ Guide content or None
252
+ """
253
+ # Check cache
254
+ if library in self._cache:
255
+ return self._cache[library]
256
+
257
+ # Load file
258
+ file_path = self.knowledge_dir / f'{library}.md'
259
+ if file_path.exists():
260
+ content = file_path.read_text(encoding='utf-8')
261
+ self._cache[library] = content
262
+ return content
263
+
264
+ return None
265
+
266
+ def load_libraries_knowledge(self, libraries: List[str]) -> str:
267
+ """
268
+ Load guides for specified libraries.
269
+
270
+ Args:
271
+ libraries: List of library names
272
+
273
+ Returns:
274
+ Combined guide string
275
+ """
276
+ if not libraries:
277
+ return ''
278
+
279
+ guides = []
280
+ for lib in sorted(libraries):
281
+ guide = self.load_library_guide(lib)
282
+ if guide:
283
+ guides.append(f"## {lib.upper()} Library API Guide\n\n{guide}")
284
+
285
+ if not guides:
286
+ return ''
287
+
288
+ return "\n\n---\n\n".join(guides)
289
+
290
+ def format_knowledge_section(self, libraries: List[str]) -> str:
291
+ """
292
+ Format knowledge section for prompt injection.
293
+
294
+ Args:
295
+ libraries: List of libraries detected by LLM
296
+
297
+ Returns:
298
+ Formatted knowledge section (empty string if none)
299
+ """
300
+ knowledge = self.load_libraries_knowledge(libraries)
301
+
302
+ if not knowledge:
303
+ return ''
304
+
305
+ return f"""
306
+ ## 📚 Library API Reference (MUST follow!)
307
+
308
+ Follow the API usage in the guides below. Avoid ❌ incorrect code and use ✅ correct code.
309
+
310
+ {knowledge}
311
+
312
+ ---
313
+ """
314
+
315
+ def list_available_libraries(self) -> List[str]:
316
+ """List available library guides"""
317
+ if not self.knowledge_dir.exists():
318
+ return []
319
+
320
+ return [f.stem for f in self.knowledge_dir.glob('*.md')]
321
+
322
+
323
+ # Singleton instance
324
+ _knowledge_base_instance: Optional[KnowledgeBase] = None
325
+
326
+
327
+ def get_knowledge_base() -> KnowledgeBase:
328
+ """Get singleton KnowledgeBase"""
329
+ global _knowledge_base_instance
330
+ if _knowledge_base_instance is None:
331
+ _knowledge_base_instance = KnowledgeBase()
332
+ return _knowledge_base_instance
333
+
334
+
335
+ # Alias (backward compatibility)
336
+ KnowledgeLoader = KnowledgeBase
337
+ get_knowledge_loader = get_knowledge_base
@@ -0,0 +1,13 @@
1
+ """
2
+ HDSP Agent Core - LLM Service
3
+
4
+ Multi-provider LLM interaction abstraction layer.
5
+ """
6
+
7
+ from .service import LLMService, call_llm, call_llm_stream
8
+
9
+ __all__ = [
10
+ "LLMService",
11
+ "call_llm",
12
+ "call_llm_stream",
13
+ ]