Skip to content

Commit 9b46a55

Browse files
committed
agents: prompt and explain-error
1 parent dbd1003 commit 9b46a55

File tree

9 files changed

+171
-63
lines changed

9 files changed

+171
-63
lines changed

Makefile

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,21 @@ repl:
5555
# Adjust --max-requests based on your Azure tier (CLI default: 4; lower if you hit rate limits)
5656
prompt:
5757
jupyter-ai-agents prompt \
58-
--url http://localhost:8888 \
59-
--token MY_TOKEN \
60-
--model azure-openai:gpt-4o-mini \
58+
--verbose \
59+
--mcp-servers http://localhost:8888/mcp \
60+
--model anthropic:claude-sonnet-4-20250514 \
6161
--path notebook.ipynb \
62-
--max-requests 4 \
62+
--max-requests 20 \
6363
--max-tool-calls 10 \
6464
--input "Create a matplotlib example"
6565

6666
explain-error:
6767
jupyter-ai-agents explain-error \
68-
--url http://localhost:8888 \
69-
--token MY_TOKEN \
70-
--model azure-openai:gpt-4o-mini \
68+
--verbose \
69+
--mcp-servers http://localhost:8888/mcp \
70+
--model anthropic:claude-sonnet-4-20250514 \
7171
--path notebook.ipynb \
72-
--max-requests 4 \
72+
--max-requests 20 \
7373
--max-tool-calls 10
7474

7575
publish-pypi: # publish the pypi package

dev/content/notebook.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"cell_type": "code",
55
"execution_count": null,
6-
"id": "29a9d2af-9c57-449d-bbcb-2df77dfe9dfe",
6+
"id": "6e785ae0-34d9-462e-aa9a-22d7b40e15d8",
77
"metadata": {},
88
"outputs": [],
99
"source": []

jupyter_ai_agents/agents/cli/__init__.py

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2024-2025 Datalayer, Inc.
2+
#
3+
# BSD 3-Clause License
4+
5+
"""Pydantic AI agents to explain error."""
6+
7+
from jupyter_ai_agents.agents.explain_error.explain_error_agent import create_explain_error_agent, run_explain_error_agent
8+
9+
__all__ = [
10+
"create_explain_error_agent",
11+
"run_explain_error_agent",
12+
]

jupyter_ai_agents/agents/cli/explain_error_agent.py renamed to jupyter_ai_agents/agents/explain_error/explain_error_agent.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
Your response should:
4141
1. Briefly explain what caused the error
4242
2. Insert a corrected code cell at the appropriate location
43-
3. Execute it to verify the fix works"""
43+
3. Execute it to verify the fix works
44+
45+
IMPORTANT: When you have completed the fix successfully, provide a final text response summarizing what you did WITHOUT making any more tool calls. This signals completion and allows the program to exit properly."""
4446

4547

4648
class ExplainErrorAgentDeps:
@@ -95,8 +97,11 @@ def create_explain_error_agent(
9597
if error_cell_index != -1:
9698
system_prompt += f"\n\nError occurred at cell index: {error_cell_index}"
9799

98-
# Add reminder to be efficient
99-
system_prompt += "\n\nBe efficient: Fix the error in as few steps as possible."
100+
# Add reminder to be efficient and complete properly
101+
system_prompt += (
102+
"\n\nBe efficient: Fix the error in as few steps as possible. "
103+
"When the fix is complete, provide a final summary WITHOUT tool calls to signal completion."
104+
)
100105

101106
# Create agent with MCP toolset
102107
agent = Agent(
@@ -117,7 +122,9 @@ async def run_explain_error_agent(
117122
notebook_content: str = "",
118123
error_info: dict[str, Any] | None = None,
119124
error_cell_index: int = -1,
125+
notebook_path: str = "",
120126
max_tool_calls: int = 10,
127+
max_requests: int = 3,
121128
) -> str:
122129
"""
123130
Run the explain error agent to analyze and fix an error.
@@ -128,40 +135,71 @@ async def run_explain_error_agent(
128135
notebook_content: Content of notebook cells
129136
error_info: Additional error information
130137
error_cell_index: Index where error occurred
138+
notebook_path: Path to the notebook file
131139
max_tool_calls: Maximum number of tool calls to prevent excessive API usage
140+
max_requests: Maximum number of API requests (default: 3)
132141
133142
Returns:
134143
Agent's response with explanation and fix
135144
"""
136145
import asyncio
137-
from pydantic_ai import UsageLimits
146+
import os
147+
from pydantic_ai import UsageLimitExceeded, UsageLimits
138148

139149
deps = ExplainErrorAgentDeps(
140150
notebook_content=notebook_content,
141151
error_info=error_info,
142152
error_cell_index=error_cell_index,
143153
)
144154

145-
logger.info(f"Running explain error agent for error: {error_description[:50]}... (max_tool_calls={max_tool_calls})")
155+
logger.info(f"Running explain error agent for error: {error_description[:50]}... (max_tool_calls={max_tool_calls}, max_requests={max_requests})")
156+
157+
# Prepend notebook connection instruction if path is provided
158+
if notebook_path:
159+
notebook_name = os.path.splitext(os.path.basename(notebook_path))[0]
160+
161+
# Prepend instruction to connect to the notebook first
162+
enhanced_description = (
163+
f"First, use the use_notebook tool to connect to the notebook at path '{notebook_path}' "
164+
f"with notebook_name '{notebook_name}' and mode 'connect'. "
165+
f"Then, analyze and fix this error: {error_description}"
166+
)
167+
logger.info(f"Enhanced input to connect to notebook: {notebook_path}")
168+
else:
169+
enhanced_description = error_description
146170

147171
try:
148172
# Create usage limits to prevent excessive API calls
149173
# Use strict limits: fewer requests to avoid rate limiting
150174
usage_limits = UsageLimits(
151175
tool_calls_limit=max_tool_calls,
152-
request_limit=3, # Strict limit: max 3 API requests to avoid rate limiting
176+
request_limit=max_requests, # Strict limit to avoid rate limiting
153177
)
154178

155179
# Add timeout to prevent hanging on retries
156180
result = await asyncio.wait_for(
157-
agent.run(error_description, deps=deps, usage_limits=usage_limits),
181+
agent.run(enhanced_description, deps=deps, usage_limits=usage_limits),
158182
timeout=120.0 # 2 minute timeout
159183
)
160184
logger.info("Explain error agent completed successfully")
161185
return result.response
162186
except asyncio.TimeoutError:
163187
logger.error("Explain error agent timed out after 120 seconds")
164188
return "Error: Operation timed out. The agent may have hit rate limits or is taking too long."
189+
except UsageLimitExceeded as e:
190+
logger.error(f"Explain error agent hit usage limits: {e}")
191+
return (
192+
"Error: Reached the configured usage limits.\n"
193+
f"Increase --max-requests (currently {max_requests}) or --max-tool-calls (currently {max_tool_calls}) "
194+
"if your model provider allows more usage."
195+
)
196+
except UsageLimitExceeded as e:
197+
logger.error(f"Explain error agent hit usage limits: {e}")
198+
return (
199+
"Error: Reached the configured usage limits.\n"
200+
f"Increase --max-requests (currently {max_requests}) or --max-tool-calls (currently {max_tool_calls}) "
201+
"if your model provider allows more usage."
202+
)
165203
except Exception as e:
166204
logger.error(f"Error running explain error agent: {e}", exc_info=True)
167205
return f"Error: {str(e)}"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2024-2025 Datalayer, Inc.
2+
#
3+
# BSD 3-Clause License
4+
5+
"""Pydantic AI agents for prompt handling."""
6+
7+
from jupyter_ai_agents.agents.prompt.prompt_agent import create_prompt_agent, run_prompt_agent
8+
9+
__all__ = [
10+
"create_prompt_agent",
11+
"run_prompt_agent",
12+
]

jupyter_ai_agents/agents/cli/prompt_agent.py renamed to jupyter_ai_agents/agents/prompt/prompt_agent.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
3. Write the main code
3838
4. Add markdown explanations
3939
40-
Execute each code cell as you create it to ensure it works properly."""
40+
Execute each code cell as you create it to ensure it works properly.
41+
42+
IMPORTANT: When you have completed the task successfully, provide a final text response summarizing what you did WITHOUT making any more tool calls. This signals completion and allows the program to exit properly."""
4143

4244

4345
class PromptAgentDeps:
@@ -83,8 +85,12 @@ def create_prompt_agent(
8385
if notebook_context.get('current_cell_index', -1) != -1:
8486
system_prompt += f"\n\nUser instruction was given at cell index: {notebook_context['current_cell_index']}"
8587

86-
# Add reminder to be efficient
87-
system_prompt += "\n\nBe efficient: Complete the task in as few steps as possible. Don't over-verify or re-check unnecessarily."
88+
# Add reminder to be efficient and complete properly
89+
system_prompt += (
90+
"\n\nBe efficient: Complete the task in as few steps as possible. "
91+
"Don't over-verify or re-check unnecessarily. "
92+
"When the task is done, provide a final summary WITHOUT tool calls to signal completion."
93+
)
8894

8995
# Create agent with MCP toolset
9096
agent = Agent(
@@ -112,20 +118,36 @@ async def run_prompt_agent(
112118
Args:
113119
agent: The configured prompt agent
114120
user_input: User's instruction/prompt
115-
notebook_context: Optional notebook context
121+
notebook_context: Optional notebook context (should include 'notebook_path')
116122
max_tool_calls: Maximum number of tool calls to prevent excessive API usage
117123
max_requests: Maximum number of API requests (default: 4, lower if needed for strict rate limits)
118124
119125
Returns:
120126
Agent's response
121127
"""
122128
import asyncio
129+
import os
123130
from pydantic_ai import UsageLimitExceeded, UsageLimits
124131

125132
deps = PromptAgentDeps(notebook_context)
126133

127134
logger.info(f"Running prompt agent with input: {user_input[:50]}... (max_tool_calls={max_tool_calls}, max_requests={max_requests})")
128135

136+
# Prepend notebook connection instruction if path is provided
137+
if notebook_context and notebook_context.get('notebook_path'):
138+
notebook_path = notebook_context['notebook_path']
139+
notebook_name = os.path.splitext(os.path.basename(notebook_path))[0]
140+
141+
# Prepend instruction to connect to the notebook first
142+
enhanced_input = (
143+
f"First, use the use_notebook tool to connect to the notebook at path '{notebook_path}' "
144+
f"with notebook_name '{notebook_name}' and mode 'connect'. "
145+
f"Then, {user_input}"
146+
)
147+
logger.info(f"Enhanced input to connect to notebook: {notebook_path}")
148+
else:
149+
enhanced_input = user_input
150+
129151
# Warn about low limits
130152
if max_requests <= 2:
131153
logger.warning(
@@ -144,7 +166,7 @@ async def run_prompt_agent(
144166

145167
# Add timeout to prevent hanging on retries
146168
result = await asyncio.wait_for(
147-
agent.run(user_input, deps=deps, usage_limits=usage_limits),
169+
agent.run(enhanced_input, deps=deps, usage_limits=usage_limits),
148170
timeout=120.0 # 2 minute timeout
149171
)
150172
logger.info("Prompt agent completed successfully")

0 commit comments

Comments
 (0)