code-puppy 0.0.169__py3-none-any.whl → 0.0.366__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.
- code_puppy/__init__.py +7 -1
- code_puppy/agents/__init__.py +8 -8
- code_puppy/agents/agent_c_reviewer.py +155 -0
- code_puppy/agents/agent_code_puppy.py +9 -2
- code_puppy/agents/agent_code_reviewer.py +90 -0
- code_puppy/agents/agent_cpp_reviewer.py +132 -0
- code_puppy/agents/agent_creator_agent.py +48 -9
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_javascript_reviewer.py +160 -0
- code_puppy/agents/agent_manager.py +146 -199
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_planning.py +163 -0
- code_puppy/agents/agent_python_programmer.py +165 -0
- code_puppy/agents/agent_python_reviewer.py +90 -0
- code_puppy/agents/agent_qa_expert.py +163 -0
- code_puppy/agents/agent_qa_kitten.py +208 -0
- code_puppy/agents/agent_security_auditor.py +181 -0
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +166 -0
- code_puppy/agents/base_agent.py +1713 -1
- code_puppy/agents/event_stream_handler.py +350 -0
- code_puppy/agents/json_agent.py +12 -1
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +232 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +174 -4
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/claude_cache_client.py +586 -0
- code_puppy/cli_runner.py +916 -0
- code_puppy/command_line/add_model_menu.py +1079 -0
- code_puppy/command_line/agent_menu.py +395 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +605 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +520 -0
- code_puppy/command_line/command_handler.py +233 -627
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +715 -0
- code_puppy/command_line/core_commands.py +792 -0
- code_puppy/command_line/diff_menu.py +863 -0
- code_puppy/command_line/load_context_completion.py +15 -22
- code_puppy/command_line/mcp/base.py +1 -4
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +9 -4
- code_puppy/command_line/mcp/help_command.py +6 -5
- code_puppy/command_line/mcp/install_command.py +16 -27
- code_puppy/command_line/mcp/install_menu.py +685 -0
- code_puppy/command_line/mcp/list_command.py +3 -3
- code_puppy/command_line/mcp/logs_command.py +174 -65
- code_puppy/command_line/mcp/remove_command.py +2 -2
- code_puppy/command_line/mcp/restart_command.py +12 -4
- code_puppy/command_line/mcp/search_command.py +17 -11
- code_puppy/command_line/mcp/start_all_command.py +22 -13
- code_puppy/command_line/mcp/start_command.py +50 -31
- code_puppy/command_line/mcp/status_command.py +6 -7
- code_puppy/command_line/mcp/stop_all_command.py +11 -8
- code_puppy/command_line/mcp/stop_command.py +11 -10
- code_puppy/command_line/mcp/test_command.py +2 -2
- code_puppy/command_line/mcp/utils.py +1 -1
- code_puppy/command_line/mcp/wizard_utils.py +22 -18
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +89 -30
- code_puppy/command_line/model_settings_menu.py +884 -0
- code_puppy/command_line/motd.py +14 -8
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +340 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +626 -75
- code_puppy/command_line/session_commands.py +296 -0
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +1181 -51
- code_puppy/error_logging.py +118 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +602 -0
- code_puppy/http_utils.py +220 -104
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +5 -594
- code_puppy/{mcp → mcp_}/__init__.py +17 -0
- code_puppy/{mcp → mcp_}/async_lifecycle.py +35 -4
- code_puppy/{mcp → mcp_}/blocking_startup.py +70 -43
- code_puppy/{mcp → mcp_}/captured_stdio_server.py +2 -2
- code_puppy/{mcp → mcp_}/config_wizard.py +5 -5
- code_puppy/{mcp → mcp_}/dashboard.py +15 -6
- code_puppy/{mcp → mcp_}/examples/retry_example.py +4 -1
- code_puppy/{mcp → mcp_}/managed_server.py +66 -39
- code_puppy/{mcp → mcp_}/manager.py +146 -52
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/{mcp → mcp_}/registry.py +6 -6
- code_puppy/{mcp → mcp_}/server_registry_catalog.py +25 -8
- code_puppy/messaging/__init__.py +199 -2
- code_puppy/messaging/bus.py +610 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +17 -48
- code_puppy/messaging/messages.py +500 -0
- code_puppy/messaging/queue_console.py +1 -24
- code_puppy/messaging/renderers.py +43 -146
- code_puppy/messaging/rich_renderer.py +1027 -0
- code_puppy/messaging/spinner/__init__.py +33 -5
- code_puppy/messaging/spinner/console_spinner.py +92 -52
- code_puppy/messaging/spinner/spinner_base.py +29 -0
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_factory.py +686 -80
- code_puppy/model_utils.py +167 -0
- code_puppy/models.json +86 -104
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +164 -10
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +136 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
- code_puppy/plugins/antigravity_oauth/storage.py +271 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +767 -0
- code_puppy/plugins/antigravity_oauth/utils.py +169 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
- code_puppy/plugins/claude_code_oauth/config.py +50 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/utils.py +518 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/prompts/codex_system_prompt.md +310 -0
- code_puppy/pydantic_patches.py +131 -0
- code_puppy/reopenable_async_client.py +8 -8
- code_puppy/round_robin_model.py +10 -15
- code_puppy/session_storage.py +294 -0
- code_puppy/status_display.py +21 -4
- code_puppy/summarization_agent.py +52 -14
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +139 -6
- code_puppy/tools/agent_tools.py +548 -49
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +289 -0
- code_puppy/tools/browser/browser_interactions.py +545 -0
- code_puppy/tools/browser/browser_locators.py +640 -0
- code_puppy/tools/browser/browser_manager.py +316 -0
- code_puppy/tools/browser/browser_navigation.py +251 -0
- code_puppy/tools/browser/browser_screenshot.py +179 -0
- code_puppy/tools/browser/browser_scripts.py +462 -0
- code_puppy/tools/browser/browser_workflows.py +221 -0
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +521 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +941 -153
- code_puppy/tools/common.py +1146 -6
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +288 -89
- code_puppy/tools/file_operations.py +352 -266
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +30 -11
- code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
- code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/METADATA +184 -67
- code_puppy-0.0.366.dist-info/RECORD +217 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/WHEEL +1 -1
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/entry_points.txt +1 -0
- code_puppy/agent.py +0 -231
- code_puppy/agents/agent_orchestrator.json +0 -26
- code_puppy/agents/runtime_manager.py +0 -272
- code_puppy/command_line/mcp/add_command.py +0 -183
- code_puppy/command_line/meta_command_handler.py +0 -153
- code_puppy/message_history_processor.py +0 -490
- code_puppy/messaging/spinner/textual_spinner.py +0 -101
- code_puppy/state_management.py +0 -200
- code_puppy/tui/__init__.py +0 -10
- code_puppy/tui/app.py +0 -986
- code_puppy/tui/components/__init__.py +0 -21
- code_puppy/tui/components/chat_view.py +0 -550
- code_puppy/tui/components/command_history_modal.py +0 -218
- code_puppy/tui/components/copy_button.py +0 -139
- code_puppy/tui/components/custom_widgets.py +0 -63
- code_puppy/tui/components/human_input_modal.py +0 -175
- code_puppy/tui/components/input_area.py +0 -167
- code_puppy/tui/components/sidebar.py +0 -309
- code_puppy/tui/components/status_bar.py +0 -182
- code_puppy/tui/messages.py +0 -27
- code_puppy/tui/models/__init__.py +0 -8
- code_puppy/tui/models/chat_message.py +0 -25
- code_puppy/tui/models/command_history.py +0 -89
- code_puppy/tui/models/enums.py +0 -24
- code_puppy/tui/screens/__init__.py +0 -15
- code_puppy/tui/screens/help.py +0 -130
- code_puppy/tui/screens/mcp_install_wizard.py +0 -803
- code_puppy/tui/screens/settings.py +0 -290
- code_puppy/tui/screens/tools.py +0 -74
- code_puppy-0.0.169.data/data/code_puppy/models.json +0 -128
- code_puppy-0.0.169.dist-info/RECORD +0 -112
- /code_puppy/{mcp → mcp_}/circuit_breaker.py +0 -0
- /code_puppy/{mcp → mcp_}/error_isolation.py +0 -0
- /code_puppy/{mcp → mcp_}/health_monitor.py +0 -0
- /code_puppy/{mcp → mcp_}/retry_manager.py +0 -0
- /code_puppy/{mcp → mcp_}/status_tracker.py +0 -0
- /code_puppy/{mcp → mcp_}/system_tools.py +0 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/licenses/LICENSE +0 -0
code_puppy/agent.py
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import uuid
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import Dict, Optional
|
|
4
|
-
|
|
5
|
-
from pydantic_ai import Agent
|
|
6
|
-
from pydantic_ai.settings import ModelSettings
|
|
7
|
-
from pydantic_ai.usage import UsageLimits
|
|
8
|
-
|
|
9
|
-
from code_puppy.message_history_processor import (
|
|
10
|
-
get_model_context_length,
|
|
11
|
-
message_history_accumulator,
|
|
12
|
-
)
|
|
13
|
-
from code_puppy.messaging.message_queue import (
|
|
14
|
-
emit_error,
|
|
15
|
-
emit_info,
|
|
16
|
-
emit_system_message,
|
|
17
|
-
)
|
|
18
|
-
from code_puppy.model_factory import ModelFactory
|
|
19
|
-
|
|
20
|
-
# Tool registration is imported on demand
|
|
21
|
-
from code_puppy.tools.common import console
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def load_puppy_rules():
|
|
25
|
-
global PUPPY_RULES
|
|
26
|
-
|
|
27
|
-
# Check for all 4 combinations of the rules file
|
|
28
|
-
possible_paths = ["AGENTS.md", "AGENT.md", "agents.md", "agent.md"]
|
|
29
|
-
|
|
30
|
-
for path_str in possible_paths:
|
|
31
|
-
puppy_rules_path = Path(path_str)
|
|
32
|
-
if puppy_rules_path.exists():
|
|
33
|
-
with open(puppy_rules_path, "r") as f:
|
|
34
|
-
puppy_rules = f.read()
|
|
35
|
-
return puppy_rules
|
|
36
|
-
|
|
37
|
-
# If none of the files exist, return None
|
|
38
|
-
return None
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Load at import
|
|
42
|
-
PUPPY_RULES = load_puppy_rules()
|
|
43
|
-
_LAST_MODEL_NAME = None
|
|
44
|
-
_code_generation_agent = None
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _load_mcp_servers(extra_headers: Optional[Dict[str, str]] = None):
|
|
48
|
-
"""Load MCP servers using the new manager while maintaining backward compatibility."""
|
|
49
|
-
from code_puppy.config import get_value, load_mcp_server_configs
|
|
50
|
-
from code_puppy.mcp import ServerConfig, get_mcp_manager
|
|
51
|
-
|
|
52
|
-
# Check if MCP servers are disabled
|
|
53
|
-
mcp_disabled = get_value("disable_mcp_servers")
|
|
54
|
-
if mcp_disabled and str(mcp_disabled).lower() in ("1", "true", "yes", "on"):
|
|
55
|
-
emit_system_message("[dim]MCP servers disabled via config[/dim]")
|
|
56
|
-
return []
|
|
57
|
-
|
|
58
|
-
# Get the MCP manager singleton
|
|
59
|
-
manager = get_mcp_manager()
|
|
60
|
-
|
|
61
|
-
# Load configurations from legacy file for backward compatibility
|
|
62
|
-
configs = load_mcp_server_configs()
|
|
63
|
-
if not configs:
|
|
64
|
-
# Check if manager already has servers (could be from new system)
|
|
65
|
-
existing_servers = manager.list_servers()
|
|
66
|
-
if not existing_servers:
|
|
67
|
-
emit_system_message("[dim]No MCP servers configured[/dim]")
|
|
68
|
-
return []
|
|
69
|
-
else:
|
|
70
|
-
# Register servers from legacy config with manager
|
|
71
|
-
for name, conf in configs.items():
|
|
72
|
-
try:
|
|
73
|
-
# Convert legacy format to new ServerConfig
|
|
74
|
-
server_config = ServerConfig(
|
|
75
|
-
id=conf.get("id", f"{name}_{hash(name)}"),
|
|
76
|
-
name=name,
|
|
77
|
-
type=conf.get("type", "sse"),
|
|
78
|
-
enabled=conf.get("enabled", True),
|
|
79
|
-
config=conf,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# Check if server already registered
|
|
83
|
-
existing = manager.get_server_by_name(name)
|
|
84
|
-
if not existing:
|
|
85
|
-
# Register new server
|
|
86
|
-
manager.register_server(server_config)
|
|
87
|
-
emit_system_message(f"[dim]Registered MCP server: {name}[/dim]")
|
|
88
|
-
else:
|
|
89
|
-
# Update existing server config if needed
|
|
90
|
-
if existing.config != server_config.config:
|
|
91
|
-
manager.update_server(existing.id, server_config)
|
|
92
|
-
emit_system_message(f"[dim]Updated MCP server: {name}[/dim]")
|
|
93
|
-
|
|
94
|
-
except Exception as e:
|
|
95
|
-
emit_error(f"Failed to register MCP server '{name}': {str(e)}")
|
|
96
|
-
continue
|
|
97
|
-
|
|
98
|
-
# Get pydantic-ai compatible servers from manager
|
|
99
|
-
servers = manager.get_servers_for_agent()
|
|
100
|
-
|
|
101
|
-
if servers:
|
|
102
|
-
emit_system_message(
|
|
103
|
-
f"[green]Successfully loaded {len(servers)} MCP server(s)[/green]"
|
|
104
|
-
)
|
|
105
|
-
else:
|
|
106
|
-
emit_system_message(
|
|
107
|
-
"[yellow]No MCP servers available (check if servers are enabled)[/yellow]"
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
return servers
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def reload_mcp_servers():
|
|
114
|
-
"""Reload MCP servers without restarting the agent."""
|
|
115
|
-
from code_puppy.mcp import get_mcp_manager
|
|
116
|
-
|
|
117
|
-
manager = get_mcp_manager()
|
|
118
|
-
# Reload configurations
|
|
119
|
-
_load_mcp_servers()
|
|
120
|
-
# Return updated servers
|
|
121
|
-
return manager.get_servers_for_agent()
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def reload_code_generation_agent(message_group: str | None):
|
|
125
|
-
"""Force-reload the agent, usually after a model change."""
|
|
126
|
-
if message_group is None:
|
|
127
|
-
message_group = str(uuid.uuid4())
|
|
128
|
-
global _code_generation_agent, _LAST_MODEL_NAME
|
|
129
|
-
from code_puppy.agents import clear_agent_cache
|
|
130
|
-
from code_puppy.config import clear_model_cache, get_model_name
|
|
131
|
-
|
|
132
|
-
# Clear both ModelFactory cache and config cache when force reloading
|
|
133
|
-
clear_model_cache()
|
|
134
|
-
clear_agent_cache()
|
|
135
|
-
|
|
136
|
-
# Check if current agent has a pinned model
|
|
137
|
-
from code_puppy.agents import get_current_agent_config
|
|
138
|
-
|
|
139
|
-
agent_config = get_current_agent_config()
|
|
140
|
-
agent_model_name = None
|
|
141
|
-
if hasattr(agent_config, "get_model_name"):
|
|
142
|
-
agent_model_name = agent_config.get_model_name()
|
|
143
|
-
|
|
144
|
-
# Use agent-specific model if pinned, otherwise use global model
|
|
145
|
-
model_name = agent_model_name if agent_model_name else get_model_name()
|
|
146
|
-
emit_info(
|
|
147
|
-
f"[bold cyan]Loading Model: {model_name}[/bold cyan]",
|
|
148
|
-
message_group=message_group,
|
|
149
|
-
)
|
|
150
|
-
models_config = ModelFactory.load_config()
|
|
151
|
-
model = ModelFactory.get_model(model_name, models_config)
|
|
152
|
-
|
|
153
|
-
# Get agent-specific system prompt
|
|
154
|
-
agent_config = get_current_agent_config()
|
|
155
|
-
emit_info(
|
|
156
|
-
f"[bold magenta]Loading Agent: {agent_config.display_name}[/bold magenta]",
|
|
157
|
-
message_group=message_group,
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
instructions = agent_config.get_system_prompt()
|
|
161
|
-
|
|
162
|
-
if PUPPY_RULES:
|
|
163
|
-
instructions += f"\n{PUPPY_RULES}"
|
|
164
|
-
|
|
165
|
-
mcp_servers = _load_mcp_servers()
|
|
166
|
-
|
|
167
|
-
# Configure model settings with max_tokens if set
|
|
168
|
-
model_settings_dict = {"seed": 42}
|
|
169
|
-
output_tokens = max(2048, min(int(0.05 * get_model_context_length()) - 1024, 16384))
|
|
170
|
-
console.print(f"Max output tokens per message: {output_tokens}")
|
|
171
|
-
model_settings_dict["max_tokens"] = output_tokens
|
|
172
|
-
|
|
173
|
-
model_settings = ModelSettings(**model_settings_dict)
|
|
174
|
-
agent = Agent(
|
|
175
|
-
model=model,
|
|
176
|
-
instructions=instructions,
|
|
177
|
-
output_type=str,
|
|
178
|
-
retries=3,
|
|
179
|
-
mcp_servers=mcp_servers,
|
|
180
|
-
history_processors=[message_history_accumulator],
|
|
181
|
-
model_settings=model_settings,
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
# Register tools specified by the agent
|
|
185
|
-
from code_puppy.tools import register_tools_for_agent
|
|
186
|
-
|
|
187
|
-
agent_tools = agent_config.get_available_tools()
|
|
188
|
-
register_tools_for_agent(agent, agent_tools)
|
|
189
|
-
_code_generation_agent = agent
|
|
190
|
-
_LAST_MODEL_NAME = model_name
|
|
191
|
-
return _code_generation_agent
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def get_code_generation_agent(force_reload=False, message_group: str | None = None):
|
|
195
|
-
"""
|
|
196
|
-
Retrieve the agent with the currently configured model.
|
|
197
|
-
Forces a reload if the model has changed, or if force_reload is passed.
|
|
198
|
-
"""
|
|
199
|
-
global _code_generation_agent, _LAST_MODEL_NAME
|
|
200
|
-
if message_group is None:
|
|
201
|
-
message_group = str(uuid.uuid4())
|
|
202
|
-
from code_puppy.config import get_model_name
|
|
203
|
-
|
|
204
|
-
# Get the global model name
|
|
205
|
-
global_model_name = get_model_name()
|
|
206
|
-
|
|
207
|
-
# Check if current agent has a pinned model
|
|
208
|
-
from code_puppy.agents import get_current_agent_config
|
|
209
|
-
|
|
210
|
-
agent_config = get_current_agent_config()
|
|
211
|
-
agent_model_name = None
|
|
212
|
-
if hasattr(agent_config, "get_model_name"):
|
|
213
|
-
agent_model_name = agent_config.get_model_name()
|
|
214
|
-
|
|
215
|
-
# Use agent-specific model if pinned, otherwise use global model
|
|
216
|
-
model_name = agent_model_name if agent_model_name else global_model_name
|
|
217
|
-
|
|
218
|
-
if _code_generation_agent is None or _LAST_MODEL_NAME != model_name or force_reload:
|
|
219
|
-
return reload_code_generation_agent(message_group)
|
|
220
|
-
return _code_generation_agent
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def get_custom_usage_limits():
|
|
224
|
-
"""
|
|
225
|
-
Returns custom usage limits with configurable request limit.
|
|
226
|
-
This centralizes the configuration of rate limiting for the agent.
|
|
227
|
-
Default pydantic-ai limit is 50, this increases it to the configured value (default 100).
|
|
228
|
-
"""
|
|
229
|
-
from code_puppy.config import get_message_limit
|
|
230
|
-
|
|
231
|
-
return UsageLimits(request_limit=get_message_limit())
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "agent-orchestrator-id",
|
|
3
|
-
"name": "agent-orchestrator",
|
|
4
|
-
"display_name": "Agent Orchestrator 🎭",
|
|
5
|
-
"description": "Coordinates and manages various specialized agents to accomplish tasks",
|
|
6
|
-
"system_prompt": [
|
|
7
|
-
"You are an agent orchestrator that coordinates various specialized agents.",
|
|
8
|
-
"When given a task, first list the available agents to understand what's at your disposal.",
|
|
9
|
-
"Then, invoke the most appropriate agent to handle the task. If needed, you can invoke multiple agents.",
|
|
10
|
-
"",
|
|
11
|
-
"#### `list_agents()`",
|
|
12
|
-
"Use this to list all available sub-agents that can be invoked",
|
|
13
|
-
"",
|
|
14
|
-
"#### `invoke_agent(agent_name: str, user_prompt: str)`",
|
|
15
|
-
"Use this to invoke another agent with a specific prompt. This allows agents to delegate tasks to specialized sub-agents.",
|
|
16
|
-
"Arguments:",
|
|
17
|
-
"- agent_name (required): Name of the agent to invoke",
|
|
18
|
-
"- user_prompt (required): The prompt to send to the invoked agent",
|
|
19
|
-
"Example usage:",
|
|
20
|
-
"```python",
|
|
21
|
-
"invoke_agent(agent_name=\"python-tutor\", user_prompt=\"Explain how to use list comprehensions\")",
|
|
22
|
-
"```"
|
|
23
|
-
],
|
|
24
|
-
"tools": ["list_agents", "invoke_agent", "agent_share_your_reasoning"],
|
|
25
|
-
"user_prompt": "What would you like me to coordinate for you?"
|
|
26
|
-
}
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Runtime agent manager that ensures proper agent instance updates.
|
|
3
|
-
|
|
4
|
-
This module provides a wrapper around the agent singleton that ensures
|
|
5
|
-
all references to the agent are properly updated when it's reloaded.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import asyncio
|
|
9
|
-
import signal
|
|
10
|
-
import sys
|
|
11
|
-
import uuid
|
|
12
|
-
from typing import Any, Optional
|
|
13
|
-
|
|
14
|
-
# ExceptionGroup is available in Python 3.11+
|
|
15
|
-
if sys.version_info >= (3, 11):
|
|
16
|
-
from builtins import ExceptionGroup
|
|
17
|
-
else:
|
|
18
|
-
# For Python 3.10 and below, we can define a simple fallback
|
|
19
|
-
class ExceptionGroup(Exception):
|
|
20
|
-
def __init__(self, message, exceptions):
|
|
21
|
-
super().__init__(message)
|
|
22
|
-
self.exceptions = exceptions
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import mcp
|
|
26
|
-
from pydantic_ai import Agent
|
|
27
|
-
from pydantic_ai.exceptions import UsageLimitExceeded
|
|
28
|
-
from pydantic_ai.usage import UsageLimits
|
|
29
|
-
|
|
30
|
-
from code_puppy.messaging.message_queue import emit_info
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class RuntimeAgentManager:
|
|
34
|
-
"""
|
|
35
|
-
Manages the runtime agent instance and ensures proper updates.
|
|
36
|
-
|
|
37
|
-
This class acts as a proxy that always returns the current agent instance,
|
|
38
|
-
ensuring that when the agent is reloaded, all code using this manager
|
|
39
|
-
automatically gets the updated instance.
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
def __init__(self):
|
|
43
|
-
"""Initialize the runtime agent manager."""
|
|
44
|
-
self._agent: Optional[Agent] = None
|
|
45
|
-
self._last_model_name: Optional[str] = None
|
|
46
|
-
|
|
47
|
-
def get_agent(self, force_reload: bool = False, message_group: str = "") -> Agent:
|
|
48
|
-
"""
|
|
49
|
-
Get the current agent instance.
|
|
50
|
-
|
|
51
|
-
This method always returns the most recent agent instance,
|
|
52
|
-
automatically handling reloads when the model changes.
|
|
53
|
-
|
|
54
|
-
Args:
|
|
55
|
-
force_reload: If True, force a reload of the agent
|
|
56
|
-
|
|
57
|
-
Returns:
|
|
58
|
-
The current agent instance
|
|
59
|
-
"""
|
|
60
|
-
from code_puppy.agent import get_code_generation_agent
|
|
61
|
-
|
|
62
|
-
# Always get the current singleton - this ensures we have the latest
|
|
63
|
-
current_agent = get_code_generation_agent(
|
|
64
|
-
force_reload=force_reload, message_group=message_group
|
|
65
|
-
)
|
|
66
|
-
self._agent = current_agent
|
|
67
|
-
|
|
68
|
-
return self._agent
|
|
69
|
-
|
|
70
|
-
def reload_agent(self) -> Agent:
|
|
71
|
-
"""
|
|
72
|
-
Force reload the agent.
|
|
73
|
-
|
|
74
|
-
This is typically called after MCP servers are started/stopped.
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
The newly loaded agent instance
|
|
78
|
-
"""
|
|
79
|
-
message_group = uuid.uuid4()
|
|
80
|
-
emit_info(
|
|
81
|
-
"[bold cyan]Reloading agent with updated configuration...[/bold cyan]",
|
|
82
|
-
message_group=message_group,
|
|
83
|
-
)
|
|
84
|
-
return self.get_agent(force_reload=True, message_group=message_group)
|
|
85
|
-
|
|
86
|
-
async def run_with_mcp(
|
|
87
|
-
self, prompt: str, usage_limits: Optional[UsageLimits] = None, **kwargs
|
|
88
|
-
) -> Any:
|
|
89
|
-
"""
|
|
90
|
-
Run the agent with MCP servers and full cancellation support.
|
|
91
|
-
|
|
92
|
-
This method ensures we're always using the current agent instance
|
|
93
|
-
and handles Ctrl+C interruption properly by creating a cancellable task.
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
prompt: The user prompt to process
|
|
97
|
-
usage_limits: Optional usage limits for the agent
|
|
98
|
-
**kwargs: Additional arguments to pass to agent.run (e.g., message_history)
|
|
99
|
-
|
|
100
|
-
Returns:
|
|
101
|
-
The agent's response
|
|
102
|
-
|
|
103
|
-
Raises:
|
|
104
|
-
asyncio.CancelledError: When execution is cancelled by user
|
|
105
|
-
"""
|
|
106
|
-
agent = self.get_agent()
|
|
107
|
-
group_id = str(uuid.uuid4())
|
|
108
|
-
|
|
109
|
-
# Function to run agent with MCP
|
|
110
|
-
async def run_agent_task():
|
|
111
|
-
try:
|
|
112
|
-
async with agent:
|
|
113
|
-
return await agent.run(prompt, usage_limits=usage_limits, **kwargs)
|
|
114
|
-
except* UsageLimitExceeded as ule:
|
|
115
|
-
emit_info(f"Usage limit exceeded: {str(ule)}", group_id=group_id)
|
|
116
|
-
emit_info(
|
|
117
|
-
"The agent has reached its usage limit. You can ask it to continue by saying 'please continue' or similar.",
|
|
118
|
-
group_id=group_id,
|
|
119
|
-
)
|
|
120
|
-
except* mcp.shared.exceptions.McpError as mcp_error:
|
|
121
|
-
emit_info(f"MCP server error: {str(mcp_error)}", group_id=group_id)
|
|
122
|
-
emit_info(f"{str(mcp_error)}", group_id=group_id)
|
|
123
|
-
emit_info(
|
|
124
|
-
"Try disabling any malfunctioning MCP servers", group_id=group_id
|
|
125
|
-
)
|
|
126
|
-
except* asyncio.exceptions.CancelledError:
|
|
127
|
-
emit_info("Cancelled")
|
|
128
|
-
except* InterruptedError as ie:
|
|
129
|
-
emit_info(f"Interrupted: {str(ie)}")
|
|
130
|
-
except* Exception as other_error:
|
|
131
|
-
# Filter out CancelledError and UsageLimitExceeded from the exception group - let it propagate
|
|
132
|
-
remaining_exceptions = []
|
|
133
|
-
|
|
134
|
-
def collect_non_cancelled_exceptions(exc):
|
|
135
|
-
if isinstance(exc, ExceptionGroup):
|
|
136
|
-
for sub_exc in exc.exceptions:
|
|
137
|
-
collect_non_cancelled_exceptions(sub_exc)
|
|
138
|
-
elif not isinstance(
|
|
139
|
-
exc, (asyncio.CancelledError, UsageLimitExceeded)
|
|
140
|
-
):
|
|
141
|
-
remaining_exceptions.append(exc)
|
|
142
|
-
emit_info(f"Unexpected error: {str(exc)}", group_id=group_id)
|
|
143
|
-
emit_info(f"{str(exc.args)}", group_id=group_id)
|
|
144
|
-
|
|
145
|
-
collect_non_cancelled_exceptions(other_error)
|
|
146
|
-
|
|
147
|
-
# If there are CancelledError exceptions in the group, re-raise them
|
|
148
|
-
cancelled_exceptions = []
|
|
149
|
-
|
|
150
|
-
def collect_cancelled_exceptions(exc):
|
|
151
|
-
if isinstance(exc, ExceptionGroup):
|
|
152
|
-
for sub_exc in exc.exceptions:
|
|
153
|
-
collect_cancelled_exceptions(sub_exc)
|
|
154
|
-
elif isinstance(exc, asyncio.CancelledError):
|
|
155
|
-
cancelled_exceptions.append(exc)
|
|
156
|
-
|
|
157
|
-
collect_cancelled_exceptions(other_error)
|
|
158
|
-
|
|
159
|
-
if cancelled_exceptions:
|
|
160
|
-
# Re-raise the first CancelledError to propagate cancellation
|
|
161
|
-
raise cancelled_exceptions[0]
|
|
162
|
-
|
|
163
|
-
# Create the task FIRST
|
|
164
|
-
agent_task = asyncio.create_task(run_agent_task())
|
|
165
|
-
|
|
166
|
-
# Import shell process killer
|
|
167
|
-
from code_puppy.tools.command_runner import kill_all_running_shell_processes
|
|
168
|
-
|
|
169
|
-
# Ensure the interrupt handler only acts once per task
|
|
170
|
-
def keyboard_interrupt_handler(sig, frame):
|
|
171
|
-
"""Signal handler for Ctrl+C - replicating exact original logic"""
|
|
172
|
-
|
|
173
|
-
# First, nuke any running shell processes triggered by tools
|
|
174
|
-
try:
|
|
175
|
-
killed = kill_all_running_shell_processes()
|
|
176
|
-
if killed:
|
|
177
|
-
emit_info(f"Cancelled {killed} running shell process(es).")
|
|
178
|
-
else:
|
|
179
|
-
# Only cancel the agent task if no shell processes were killed
|
|
180
|
-
if not agent_task.done():
|
|
181
|
-
agent_task.cancel()
|
|
182
|
-
except Exception as e:
|
|
183
|
-
emit_info(f"Shell kill error: {e}")
|
|
184
|
-
# If shell kill failed, still try to cancel the agent task
|
|
185
|
-
if not agent_task.done():
|
|
186
|
-
agent_task.cancel()
|
|
187
|
-
# Don't call the original handler
|
|
188
|
-
# This prevents the application from exiting
|
|
189
|
-
|
|
190
|
-
try:
|
|
191
|
-
# Save original handler and set our custom one AFTER task is created
|
|
192
|
-
original_handler = signal.signal(signal.SIGINT, keyboard_interrupt_handler)
|
|
193
|
-
|
|
194
|
-
# Wait for the task to complete or be cancelled
|
|
195
|
-
result = await agent_task
|
|
196
|
-
return result
|
|
197
|
-
except asyncio.CancelledError:
|
|
198
|
-
# Task was cancelled by our handler
|
|
199
|
-
raise
|
|
200
|
-
except KeyboardInterrupt:
|
|
201
|
-
# Handle direct keyboard interrupt during await
|
|
202
|
-
if not agent_task.done():
|
|
203
|
-
agent_task.cancel()
|
|
204
|
-
try:
|
|
205
|
-
await agent_task
|
|
206
|
-
except asyncio.CancelledError:
|
|
207
|
-
pass
|
|
208
|
-
raise asyncio.CancelledError()
|
|
209
|
-
finally:
|
|
210
|
-
# Restore original signal handler
|
|
211
|
-
if original_handler:
|
|
212
|
-
signal.signal(signal.SIGINT, original_handler)
|
|
213
|
-
|
|
214
|
-
async def run(
|
|
215
|
-
self, prompt: str, usage_limits: Optional[UsageLimits] = None, **kwargs
|
|
216
|
-
) -> Any:
|
|
217
|
-
"""
|
|
218
|
-
Run the agent without explicitly managing MCP servers.
|
|
219
|
-
|
|
220
|
-
Args:
|
|
221
|
-
prompt: The user prompt to process
|
|
222
|
-
usage_limits: Optional usage limits for the agent
|
|
223
|
-
**kwargs: Additional arguments to pass to agent.run (e.g., message_history)
|
|
224
|
-
|
|
225
|
-
Returns:
|
|
226
|
-
The agent's response
|
|
227
|
-
"""
|
|
228
|
-
agent = self.get_agent()
|
|
229
|
-
try:
|
|
230
|
-
return await agent.run(prompt, usage_limits=usage_limits, **kwargs)
|
|
231
|
-
except UsageLimitExceeded as ule:
|
|
232
|
-
group_id = str(uuid.uuid4())
|
|
233
|
-
emit_info(f"Usage limit exceeded: {str(ule)}", group_id=group_id)
|
|
234
|
-
emit_info(
|
|
235
|
-
"The agent has reached its usage limit. You can ask it to continue by saying 'please continue' or similar.",
|
|
236
|
-
group_id=group_id,
|
|
237
|
-
)
|
|
238
|
-
# Return None or some default value to indicate the limit was reached
|
|
239
|
-
return None
|
|
240
|
-
|
|
241
|
-
def __getattr__(self, name: str) -> Any:
|
|
242
|
-
"""
|
|
243
|
-
Proxy all other attribute access to the current agent.
|
|
244
|
-
|
|
245
|
-
This allows the manager to be used as a drop-in replacement
|
|
246
|
-
for direct agent access.
|
|
247
|
-
|
|
248
|
-
Args:
|
|
249
|
-
name: The attribute name to access
|
|
250
|
-
|
|
251
|
-
Returns:
|
|
252
|
-
The attribute from the current agent
|
|
253
|
-
"""
|
|
254
|
-
agent = self.get_agent()
|
|
255
|
-
return getattr(agent, name)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
# Global singleton instance
|
|
259
|
-
_runtime_manager: Optional[RuntimeAgentManager] = None
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def get_runtime_agent_manager() -> RuntimeAgentManager:
|
|
263
|
-
"""
|
|
264
|
-
Get the global runtime agent manager instance.
|
|
265
|
-
|
|
266
|
-
Returns:
|
|
267
|
-
The singleton RuntimeAgentManager instance
|
|
268
|
-
"""
|
|
269
|
-
global _runtime_manager
|
|
270
|
-
if _runtime_manager is None:
|
|
271
|
-
_runtime_manager = RuntimeAgentManager()
|
|
272
|
-
return _runtime_manager
|