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,274 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Notebook Generator - Creates Jupyter notebooks from prompts using LLM
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NotebookGenerator:
|
|
14
|
+
"""Generate Jupyter notebooks from natural language prompts"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, llm_service, task_manager):
|
|
17
|
+
self.llm_service = llm_service
|
|
18
|
+
self.task_manager = task_manager
|
|
19
|
+
|
|
20
|
+
def _extract_json_from_response(self, response_text: str) -> Optional[Dict]:
|
|
21
|
+
"""Extract JSON object from LLM response text"""
|
|
22
|
+
content = response_text.strip()
|
|
23
|
+
json_match = re.search(r"\{.*\}", content, re.DOTALL)
|
|
24
|
+
if json_match:
|
|
25
|
+
try:
|
|
26
|
+
return json.loads(json_match.group())
|
|
27
|
+
except json.JSONDecodeError:
|
|
28
|
+
return None
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
async def _call_llm_for_json(self, prompt: str, fallback: dict) -> dict:
|
|
32
|
+
"""Call LLM and extract JSON response, with fallback"""
|
|
33
|
+
response_text = await self.llm_service.generate_response(prompt)
|
|
34
|
+
result = self._extract_json_from_response(response_text)
|
|
35
|
+
return result if result else fallback
|
|
36
|
+
|
|
37
|
+
def _remove_code_block_markers(self, content: str) -> str:
|
|
38
|
+
"""Remove markdown code block markers from content"""
|
|
39
|
+
content = re.sub(r"^```python\n", "", content)
|
|
40
|
+
content = re.sub(r"^```\n", "", content)
|
|
41
|
+
content = re.sub(r"\n```$", "", content)
|
|
42
|
+
return content.strip()
|
|
43
|
+
|
|
44
|
+
async def generate_notebook(
|
|
45
|
+
self, task_id: str, prompt: str, output_dir: str = None
|
|
46
|
+
) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Generate a notebook from a prompt
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
task_id: Task ID for progress tracking
|
|
52
|
+
prompt: Natural language prompt for notebook generation
|
|
53
|
+
output_dir: Directory to save the notebook (default: current directory)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Path to generated notebook
|
|
57
|
+
"""
|
|
58
|
+
try:
|
|
59
|
+
self.task_manager.start_task(task_id)
|
|
60
|
+
|
|
61
|
+
# Step 1: Analyze prompt (10%)
|
|
62
|
+
self.task_manager.update_progress(task_id, 10, "프롬프트 분석 중...")
|
|
63
|
+
analysis = await self._analyze_prompt(prompt)
|
|
64
|
+
|
|
65
|
+
# Step 2: Generate notebook structure (20%)
|
|
66
|
+
self.task_manager.update_progress(task_id, 20, "노트북 구조 생성 중...")
|
|
67
|
+
notebook_plan = await self._create_notebook_plan(prompt, analysis)
|
|
68
|
+
|
|
69
|
+
# Step 3: Generate cells (30-80%)
|
|
70
|
+
self.task_manager.update_progress(task_id, 30, "셀 생성 중...")
|
|
71
|
+
cells = await self._generate_cells(task_id, notebook_plan)
|
|
72
|
+
|
|
73
|
+
# Step 4: Create notebook file (90%)
|
|
74
|
+
self.task_manager.update_progress(task_id, 90, "노트북 저장 중...")
|
|
75
|
+
notebook_path = await self._save_notebook(
|
|
76
|
+
cells, output_dir, analysis.get("title", "generated")
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Step 5: Complete (100%)
|
|
80
|
+
self.task_manager.complete_task(task_id, notebook_path)
|
|
81
|
+
|
|
82
|
+
return notebook_path
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
self.task_manager.fail_task(task_id, str(e))
|
|
86
|
+
raise
|
|
87
|
+
|
|
88
|
+
async def _analyze_prompt(self, prompt: str) -> Dict[str, Any]:
|
|
89
|
+
"""Analyze the prompt to understand requirements"""
|
|
90
|
+
analysis_prompt = f"""다음 요청을 분석하고 JSON 형식으로 응답해주세요:
|
|
91
|
+
|
|
92
|
+
요청: {prompt}
|
|
93
|
+
|
|
94
|
+
다음 정보를 추출해주세요:
|
|
95
|
+
1. 노트북 제목 (title)
|
|
96
|
+
2. 주요 작업 목표 (objective)
|
|
97
|
+
3. 사용할 라이브러리들 (libraries) - 배열
|
|
98
|
+
4. 예상되는 셀 개수 (estimated_cells)
|
|
99
|
+
5. 데이터 분석 유형 (analysis_type: exploration, modeling, visualization, etc.)
|
|
100
|
+
|
|
101
|
+
JSON만 응답하세요:"""
|
|
102
|
+
|
|
103
|
+
fallback = {
|
|
104
|
+
"title": "Generated Notebook",
|
|
105
|
+
"objective": prompt,
|
|
106
|
+
"libraries": ["pandas", "numpy"],
|
|
107
|
+
"estimated_cells": 10,
|
|
108
|
+
"analysis_type": "general",
|
|
109
|
+
}
|
|
110
|
+
return await self._call_llm_for_json(analysis_prompt, fallback)
|
|
111
|
+
|
|
112
|
+
async def _create_notebook_plan(
|
|
113
|
+
self, prompt: str, analysis: Dict[str, Any]
|
|
114
|
+
) -> Dict[str, Any]:
|
|
115
|
+
"""Create a structured plan for the notebook"""
|
|
116
|
+
plan_prompt = f"""다음 요청에 대한 Jupyter 노트북 계획을 작성해주세요:
|
|
117
|
+
|
|
118
|
+
요청: {prompt}
|
|
119
|
+
|
|
120
|
+
분석 결과:
|
|
121
|
+
- 목표: {analysis['objective']}
|
|
122
|
+
- 라이브러리: {', '.join(analysis['libraries'])}
|
|
123
|
+
- 분석 유형: {analysis['analysis_type']}
|
|
124
|
+
|
|
125
|
+
노트북의 각 셀에 대한 계획을 JSON 형식으로 작성해주세요:
|
|
126
|
+
{{
|
|
127
|
+
"cells": [
|
|
128
|
+
{{
|
|
129
|
+
"type": "markdown",
|
|
130
|
+
"purpose": "제목 및 개요",
|
|
131
|
+
"content_hint": "간단한 설명"
|
|
132
|
+
}},
|
|
133
|
+
{{
|
|
134
|
+
"type": "code",
|
|
135
|
+
"purpose": "라이브러리 임포트",
|
|
136
|
+
"content_hint": "import 문들"
|
|
137
|
+
}},
|
|
138
|
+
...
|
|
139
|
+
]
|
|
140
|
+
}}
|
|
141
|
+
|
|
142
|
+
JSON만 응답하세요:"""
|
|
143
|
+
|
|
144
|
+
fallback = {
|
|
145
|
+
"cells": [
|
|
146
|
+
{
|
|
147
|
+
"type": "markdown",
|
|
148
|
+
"purpose": "제목",
|
|
149
|
+
"content_hint": analysis["title"],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"type": "code",
|
|
153
|
+
"purpose": "라이브러리 임포트",
|
|
154
|
+
"content_hint": "import pandas, numpy",
|
|
155
|
+
},
|
|
156
|
+
{"type": "code", "purpose": "데이터 로드", "content_hint": "load data"},
|
|
157
|
+
{"type": "code", "purpose": "분석", "content_hint": "analysis code"},
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
return await self._call_llm_for_json(plan_prompt, fallback)
|
|
161
|
+
|
|
162
|
+
async def _generate_cells(
|
|
163
|
+
self, task_id: str, plan: Dict[str, Any]
|
|
164
|
+
) -> List[Dict[str, Any]]:
|
|
165
|
+
"""Generate actual cell content based on plan"""
|
|
166
|
+
cells = []
|
|
167
|
+
planned_cells = plan.get("cells", [])
|
|
168
|
+
total_cells = len(planned_cells)
|
|
169
|
+
|
|
170
|
+
for idx, cell_plan in enumerate(planned_cells):
|
|
171
|
+
# Update progress (30% -> 80%)
|
|
172
|
+
progress = 30 + int((idx / total_cells) * 50)
|
|
173
|
+
self.task_manager.update_progress(
|
|
174
|
+
task_id, progress, f"셀 생성 중... ({idx+1}/{total_cells})"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
cell_type = cell_plan.get("type", "code")
|
|
178
|
+
purpose = cell_plan.get("purpose", "")
|
|
179
|
+
content_hint = cell_plan.get("content_hint", "")
|
|
180
|
+
|
|
181
|
+
if cell_type == "markdown":
|
|
182
|
+
content = await self._generate_markdown_cell(purpose, content_hint)
|
|
183
|
+
else:
|
|
184
|
+
content = await self._generate_code_cell(purpose, content_hint)
|
|
185
|
+
|
|
186
|
+
# Jupyter notebook source requires each line to end with \n (except the last line)
|
|
187
|
+
lines = content.split("\n")
|
|
188
|
+
source_lines = (
|
|
189
|
+
[line + "\n" for line in lines[:-1]] + [lines[-1]] if lines else []
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
cells.append(
|
|
193
|
+
{"cell_type": cell_type, "metadata": {}, "source": source_lines}
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Add execution_count for code cells
|
|
197
|
+
if cell_type == "code":
|
|
198
|
+
cells[-1]["execution_count"] = None
|
|
199
|
+
cells[-1]["outputs"] = []
|
|
200
|
+
|
|
201
|
+
return cells
|
|
202
|
+
|
|
203
|
+
async def _generate_markdown_cell(self, purpose: str, hint: str) -> str:
|
|
204
|
+
"""Generate markdown cell content"""
|
|
205
|
+
prompt = f"""다음 Jupyter 노트북 마크다운 셀을 작성해주세요:
|
|
206
|
+
|
|
207
|
+
목적: {purpose}
|
|
208
|
+
힌트: {hint}
|
|
209
|
+
|
|
210
|
+
마크다운만 작성하고, 코드 블록이나 다른 설명 없이 내용만 응답하세요:"""
|
|
211
|
+
|
|
212
|
+
response_text = await self.llm_service.generate_response(prompt)
|
|
213
|
+
return response_text.strip()
|
|
214
|
+
|
|
215
|
+
async def _generate_code_cell(self, purpose: str, hint: str) -> str:
|
|
216
|
+
"""Generate code cell content"""
|
|
217
|
+
prompt = f"""다음 Jupyter 노트북 코드 셀을 작성해주세요:
|
|
218
|
+
|
|
219
|
+
목적: {purpose}
|
|
220
|
+
힌트: {hint}
|
|
221
|
+
|
|
222
|
+
실행 가능한 Python 코드만 작성하고, 설명이나 마크다운 없이 코드만 응답하세요.
|
|
223
|
+
주석은 한국어로 간단히 작성하세요:"""
|
|
224
|
+
|
|
225
|
+
response_text = await self.llm_service.generate_response(prompt)
|
|
226
|
+
return self._remove_code_block_markers(response_text.strip())
|
|
227
|
+
|
|
228
|
+
async def _save_notebook(
|
|
229
|
+
self, cells: List[Dict], output_dir: str, base_name: str
|
|
230
|
+
) -> str:
|
|
231
|
+
"""Save notebook to file"""
|
|
232
|
+
# Prepare output directory
|
|
233
|
+
if output_dir is None:
|
|
234
|
+
output_dir = os.getcwd()
|
|
235
|
+
|
|
236
|
+
output_path = Path(output_dir)
|
|
237
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
238
|
+
|
|
239
|
+
# Generate filename
|
|
240
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
241
|
+
safe_name = re.sub(r"[^\w\s-]", "", base_name)
|
|
242
|
+
safe_name = re.sub(r"[\s_]+", "_", safe_name)
|
|
243
|
+
filename = f"{safe_name}_{timestamp}.ipynb"
|
|
244
|
+
|
|
245
|
+
notebook_path = output_path / filename
|
|
246
|
+
|
|
247
|
+
# Create notebook structure
|
|
248
|
+
notebook = {
|
|
249
|
+
"cells": cells,
|
|
250
|
+
"metadata": {
|
|
251
|
+
"kernelspec": {
|
|
252
|
+
"display_name": "Python 3",
|
|
253
|
+
"language": "python",
|
|
254
|
+
"name": "python3",
|
|
255
|
+
},
|
|
256
|
+
"language_info": {
|
|
257
|
+
"codemirror_mode": {"name": "ipython", "version": 3},
|
|
258
|
+
"file_extension": ".py",
|
|
259
|
+
"mimetype": "text/x-python",
|
|
260
|
+
"name": "python",
|
|
261
|
+
"nbconvert_exporter": "python",
|
|
262
|
+
"pygments_lexer": "ipython3",
|
|
263
|
+
"version": "3.8.0",
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
"nbformat": 4,
|
|
267
|
+
"nbformat_minor": 4,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# Save to file
|
|
271
|
+
with open(notebook_path, "w", encoding="utf-8") as f:
|
|
272
|
+
json.dump(notebook, f, indent=2, ensure_ascii=False)
|
|
273
|
+
|
|
274
|
+
return str(notebook_path)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt Builder - Construct prompts for different actions
|
|
3
|
+
프롬프트 템플릿은 prompts 모듈에서 관리됨
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from hdsp_agent_core.prompts.cell_action_prompts import (
|
|
7
|
+
format_chat_prompt,
|
|
8
|
+
format_custom_prompt,
|
|
9
|
+
format_explain_prompt,
|
|
10
|
+
format_fix_prompt,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PromptBuilder:
|
|
15
|
+
"""Build LLM prompts based on action types"""
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def build_explain_prompt(cell_content: str) -> str:
|
|
19
|
+
"""Build prompt for explaining code"""
|
|
20
|
+
return format_explain_prompt(cell_content)
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def build_fix_prompt(cell_content: str) -> str:
|
|
24
|
+
"""Build prompt for fixing code errors"""
|
|
25
|
+
return format_fix_prompt(cell_content)
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def build_custom_prompt(custom_prompt: str, cell_content: str) -> str:
|
|
29
|
+
"""Build prompt for custom user request"""
|
|
30
|
+
return format_custom_prompt(custom_prompt, cell_content)
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def build_chat_prompt(message: str, context: dict = None) -> str:
|
|
34
|
+
"""Build prompt for general chat"""
|
|
35
|
+
return format_chat_prompt(message, context)
|