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.
- agent_server/__init__.py +8 -0
- agent_server/core/__init__.py +92 -0
- agent_server/core/api_key_manager.py +427 -0
- agent_server/core/code_validator.py +1238 -0
- agent_server/core/context_condenser.py +308 -0
- agent_server/core/embedding_service.py +254 -0
- agent_server/core/error_classifier.py +577 -0
- agent_server/core/llm_client.py +95 -0
- agent_server/core/llm_service.py +649 -0
- agent_server/core/notebook_generator.py +274 -0
- agent_server/core/prompt_builder.py +35 -0
- agent_server/core/rag_manager.py +742 -0
- agent_server/core/reflection_engine.py +489 -0
- agent_server/core/retriever.py +248 -0
- agent_server/core/state_verifier.py +452 -0
- agent_server/core/summary_generator.py +484 -0
- agent_server/core/task_manager.py +198 -0
- agent_server/knowledge/__init__.py +9 -0
- agent_server/knowledge/watchdog_service.py +352 -0
- agent_server/main.py +160 -0
- agent_server/prompts/__init__.py +60 -0
- agent_server/prompts/file_action_prompts.py +113 -0
- agent_server/routers/__init__.py +9 -0
- agent_server/routers/agent.py +591 -0
- agent_server/routers/chat.py +188 -0
- agent_server/routers/config.py +100 -0
- agent_server/routers/file_resolver.py +293 -0
- agent_server/routers/health.py +42 -0
- agent_server/routers/rag.py +163 -0
- agent_server/schemas/__init__.py +60 -0
- hdsp_agent_core/__init__.py +158 -0
- hdsp_agent_core/factory.py +252 -0
- hdsp_agent_core/interfaces.py +203 -0
- hdsp_agent_core/knowledge/__init__.py +31 -0
- hdsp_agent_core/knowledge/chunking.py +356 -0
- hdsp_agent_core/knowledge/libraries/dask.md +188 -0
- hdsp_agent_core/knowledge/libraries/matplotlib.md +164 -0
- hdsp_agent_core/knowledge/libraries/polars.md +68 -0
- hdsp_agent_core/knowledge/loader.py +337 -0
- hdsp_agent_core/llm/__init__.py +13 -0
- hdsp_agent_core/llm/service.py +556 -0
- hdsp_agent_core/managers/__init__.py +22 -0
- hdsp_agent_core/managers/config_manager.py +133 -0
- hdsp_agent_core/managers/session_manager.py +251 -0
- hdsp_agent_core/models/__init__.py +115 -0
- hdsp_agent_core/models/agent.py +316 -0
- hdsp_agent_core/models/chat.py +41 -0
- hdsp_agent_core/models/common.py +95 -0
- hdsp_agent_core/models/rag.py +368 -0
- hdsp_agent_core/prompts/__init__.py +63 -0
- hdsp_agent_core/prompts/auto_agent_prompts.py +1260 -0
- hdsp_agent_core/prompts/cell_action_prompts.py +98 -0
- hdsp_agent_core/services/__init__.py +18 -0
- hdsp_agent_core/services/agent_service.py +438 -0
- hdsp_agent_core/services/chat_service.py +205 -0
- hdsp_agent_core/services/rag_service.py +262 -0
- hdsp_agent_core/tests/__init__.py +1 -0
- hdsp_agent_core/tests/conftest.py +102 -0
- hdsp_agent_core/tests/test_factory.py +251 -0
- hdsp_agent_core/tests/test_services.py +326 -0
- hdsp_jupyter_extension-2.0.0.data/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +7 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/build_log.json +738 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/install.json +5 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/package.json +134 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js +4369 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js +12496 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js.map +1 -0
- 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
- 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
- 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
- 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
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.dae97cde171e13b8c834.js +623 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.dae97cde171e13b8c834.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/style.js +4 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- hdsp_jupyter_extension-2.0.0.dist-info/METADATA +152 -0
- hdsp_jupyter_extension-2.0.0.dist-info/RECORD +121 -0
- hdsp_jupyter_extension-2.0.0.dist-info/WHEEL +4 -0
- hdsp_jupyter_extension-2.0.0.dist-info/licenses/LICENSE +21 -0
- jupyter_ext/__init__.py +233 -0
- jupyter_ext/_version.py +4 -0
- jupyter_ext/config.py +111 -0
- jupyter_ext/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +7 -0
- jupyter_ext/handlers.py +632 -0
- jupyter_ext/labextension/build_log.json +738 -0
- jupyter_ext/labextension/package.json +134 -0
- jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js +4369 -0
- jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +1 -0
- jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js +12496 -0
- jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js.map +1 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js +94 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js.map +1 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js +94 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js.map +1 -0
- jupyter_ext/labextension/static/remoteEntry.dae97cde171e13b8c834.js +623 -0
- jupyter_ext/labextension/static/remoteEntry.dae97cde171e13b8c834.js.map +1 -0
- jupyter_ext/labextension/static/style.js +4 -0
- jupyter_ext/labextension/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js +507 -0
- jupyter_ext/labextension/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js +2071 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js +1059 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +376 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +60336 -0
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js +7132 -0
- 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
|