code-puppy 0.0.174__py3-none-any.whl → 0.0.175__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/agents/agent_manager.py +8 -3
- code_puppy/agents/base_agent.py +1 -0
- code_puppy/http_utils.py +4 -7
- {code_puppy-0.0.174.dist-info → code_puppy-0.0.175.dist-info}/METADATA +1 -1
- {code_puppy-0.0.174.dist-info → code_puppy-0.0.175.dist-info}/RECORD +9 -10
- code_puppy/agent.py +0 -239
- {code_puppy-0.0.174.data → code_puppy-0.0.175.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.174.dist-info → code_puppy-0.0.175.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.174.dist-info → code_puppy-0.0.175.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.174.dist-info → code_puppy-0.0.175.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,8 @@ import os
|
|
6
6
|
import pkgutil
|
7
7
|
import uuid
|
8
8
|
from pathlib import Path
|
9
|
-
from
|
9
|
+
from pydantic_ai.messages import ModelMessage
|
10
|
+
from typing import Dict, Optional, Type, Union, List
|
10
11
|
|
11
12
|
from code_puppy.callbacks import on_agent_reload
|
12
13
|
from code_puppy.messaging import emit_warning
|
@@ -15,6 +16,7 @@ from code_puppy.agents.json_agent import JSONAgent, discover_json_agents
|
|
15
16
|
|
16
17
|
# Registry of available agents (Python classes and JSON file paths)
|
17
18
|
_AGENT_REGISTRY: Dict[str, Union[Type[BaseAgent], str]] = {}
|
19
|
+
_AGENT_HISTORIES: Dict[str, List[ModelMessage]] = {}
|
18
20
|
_CURRENT_AGENT: Optional[BaseAgent] = None
|
19
21
|
|
20
22
|
# Terminal session-based agent selection
|
@@ -248,7 +250,9 @@ def set_current_agent(agent_name: str) -> bool:
|
|
248
250
|
True if the agent was set successfully, False if agent not found.
|
249
251
|
"""
|
250
252
|
global _CURRENT_AGENT
|
251
|
-
|
253
|
+
curr_agent = get_current_agent()
|
254
|
+
if curr_agent != None:
|
255
|
+
_AGENT_HISTORIES[curr_agent.name] = curr_agent.get_message_history()
|
252
256
|
# Generate a message group ID for agent switching
|
253
257
|
message_group_id = str(uuid.uuid4())
|
254
258
|
_discover_agents(message_group_id=message_group_id)
|
@@ -264,7 +268,8 @@ def set_current_agent(agent_name: str) -> bool:
|
|
264
268
|
session_id = get_terminal_session_id()
|
265
269
|
_SESSION_AGENTS_CACHE[session_id] = agent_name
|
266
270
|
_save_session_data(_SESSION_AGENTS_CACHE)
|
267
|
-
|
271
|
+
if agent_obj.name in _AGENT_HISTORIES:
|
272
|
+
agent_obj.set_message_history(_AGENT_HISTORIES[agent_obj.name])
|
268
273
|
on_agent_reload(agent_obj.id, agent_name)
|
269
274
|
return True
|
270
275
|
|
code_puppy/agents/base_agent.py
CHANGED
@@ -501,6 +501,7 @@ class BaseAgent(ABC):
|
|
501
501
|
dropped_count += 1
|
502
502
|
continue
|
503
503
|
pruned.append(msg)
|
504
|
+
return pruned
|
504
505
|
|
505
506
|
def message_history_processor(self, messages: List[ModelMessage]) -> List[ModelMessage]:
|
506
507
|
# First, prune any interrupted/mismatched tool-call conversations
|
code_puppy/http_utils.py
CHANGED
@@ -64,7 +64,7 @@ def create_client(
|
|
64
64
|
emit_info(
|
65
65
|
f"HTTP retry: Retrying request due to status code {response.status_code}"
|
66
66
|
)
|
67
|
-
|
67
|
+
return True
|
68
68
|
|
69
69
|
transport = TenacityTransport(
|
70
70
|
config=RetryConfig(
|
@@ -106,16 +106,13 @@ def create_async_client(
|
|
106
106
|
emit_info(
|
107
107
|
f"HTTP retry: Retrying request due to status code {response.status_code}"
|
108
108
|
)
|
109
|
-
|
109
|
+
return True
|
110
110
|
|
111
111
|
transport = AsyncTenacityTransport(
|
112
112
|
config=RetryConfig(
|
113
113
|
retry=lambda e: isinstance(e, httpx.HTTPStatusError)
|
114
114
|
and e.response.status_code in retry_status_codes,
|
115
|
-
wait=wait_retry_after(
|
116
|
-
fallback_strategy=wait_exponential(multiplier=1, max=60),
|
117
|
-
max_wait=300,
|
118
|
-
),
|
115
|
+
wait=wait_retry_after(10),
|
119
116
|
stop=stop_after_attempt(10),
|
120
117
|
reraise=True,
|
121
118
|
),
|
@@ -188,7 +185,7 @@ def create_reopenable_async_client(
|
|
188
185
|
emit_info(
|
189
186
|
f"HTTP retry: Retrying request due to status code {response.status_code}"
|
190
187
|
)
|
191
|
-
|
188
|
+
return True
|
192
189
|
|
193
190
|
transport = AsyncTenacityTransport(
|
194
191
|
config=RetryConfig(
|
@@ -1,9 +1,8 @@
|
|
1
1
|
code_puppy/__init__.py,sha256=ehbM1-wMjNmOXk_DBhhJECFyBv2dRHwwo7ucjHeM68E,107
|
2
2
|
code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
|
3
|
-
code_puppy/agent.py,sha256=CGdK-vIwRQh27c552Glc39eqhnXJh2ZeE9D2BaYkhms,8505
|
4
3
|
code_puppy/callbacks.py,sha256=6wYB6K_fGSCkKKEFaYOYkJT45WaV5W_NhUIzcvVH_nU,5060
|
5
4
|
code_puppy/config.py,sha256=ruPGe9f3kFI9F563NQOKuNQY12kw5eXPzbm2utJ0DO8,20639
|
6
|
-
code_puppy/http_utils.py,sha256=
|
5
|
+
code_puppy/http_utils.py,sha256=YLd8Y16idbI32JGeBXG8n5rT4o4X_zxk9FgUvK9XFo8,8248
|
7
6
|
code_puppy/main.py,sha256=hVPFNN8QOuct2FFH6OMGQ3E04cmEnmO88ffTbbpfuWY,20516
|
8
7
|
code_puppy/model_factory.py,sha256=z9vQbcGllgMwU0On8rPvzYxkygW2Uyd3NJmRzbKv-is,13759
|
9
8
|
code_puppy/models.json,sha256=iXmLZGflnQcu2DRh4WUlgAhoXdvoxUc7KBhB8YxawXM,3088
|
@@ -16,9 +15,9 @@ code_puppy/version_checker.py,sha256=bjLDmgGPrl7XnYwX1u13O8uFlsfikV90PK6nbA9Z9QU
|
|
16
15
|
code_puppy/agents/__init__.py,sha256=37co1Xux3E10ynvbCBkyeTZAZpjEb-ci2uUy6nzM1hA,518
|
17
16
|
code_puppy/agents/agent_code_puppy.py,sha256=sbuQxLzlkMbPOyLbILbXOAHecRsxbFdQt13HJ_GEqQo,7972
|
18
17
|
code_puppy/agents/agent_creator_agent.py,sha256=eNOlJssQdyoQm1F7d-TZWcMXpkYmZ-w9WN-NDFXCgtw,23042
|
19
|
-
code_puppy/agents/agent_manager.py,sha256=
|
18
|
+
code_puppy/agents/agent_manager.py,sha256=ZT6B7x4hRKdmEzP085_xR8OL8j0YpboWazrs0U7mP1I,11409
|
20
19
|
code_puppy/agents/agent_qa_kitten.py,sha256=5PeFFSwCFlTUvP6h5bGntx0xv5NmRwBiw0HnMqY8nLI,9107
|
21
|
-
code_puppy/agents/base_agent.py,sha256=
|
20
|
+
code_puppy/agents/base_agent.py,sha256=Rv1jXxeFsXfwdyGoVWwoZRgovbi_laex6OivLzaGHpQ,36723
|
22
21
|
code_puppy/agents/json_agent.py,sha256=y6AYE3Fx9LhmemcPzt46d7359MNnkGIjU83YBGNer2g,4533
|
23
22
|
code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
|
24
23
|
code_puppy/command_line/command_handler.py,sha256=z4p62jwdJXUJVVzTHStTOFyzv-PwjjNhu8bziIo8gns,27191
|
@@ -119,9 +118,9 @@ code_puppy/tui/screens/help.py,sha256=eJuPaOOCp7ZSUlecearqsuX6caxWv7NQszUh0tZJjB
|
|
119
118
|
code_puppy/tui/screens/mcp_install_wizard.py,sha256=vObpQwLbXjQsxmSg-WCasoev1usEi0pollKnL0SHu9U,27693
|
120
119
|
code_puppy/tui/screens/settings.py,sha256=W22sevojC1_HZBoeoJTH3HWkehini3bGi_ic0OKgeLI,10685
|
121
120
|
code_puppy/tui/screens/tools.py,sha256=3pr2Xkpa9Js6Yhf1A3_wQVRzFOui-KDB82LwrsdBtyk,1715
|
122
|
-
code_puppy-0.0.
|
123
|
-
code_puppy-0.0.
|
124
|
-
code_puppy-0.0.
|
125
|
-
code_puppy-0.0.
|
126
|
-
code_puppy-0.0.
|
127
|
-
code_puppy-0.0.
|
121
|
+
code_puppy-0.0.175.data/data/code_puppy/models.json,sha256=iXmLZGflnQcu2DRh4WUlgAhoXdvoxUc7KBhB8YxawXM,3088
|
122
|
+
code_puppy-0.0.175.dist-info/METADATA,sha256=eyUu2t6tGGAB4Urt_lhXVDtSkT0yw8aZ_IJEa2B-BAc,20103
|
123
|
+
code_puppy-0.0.175.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
124
|
+
code_puppy-0.0.175.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
|
125
|
+
code_puppy-0.0.175.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
126
|
+
code_puppy-0.0.175.dist-info/RECORD,,
|
code_puppy/agent.py
DELETED
@@ -1,239 +0,0 @@
|
|
1
|
-
import uuid
|
2
|
-
from pathlib import Path
|
3
|
-
from pydantic_ai.models.openai import OpenAIModelSettings, OpenAIResponsesModelSettings
|
4
|
-
from typing import Dict, Optional
|
5
|
-
|
6
|
-
from pydantic_ai import Agent
|
7
|
-
from pydantic_ai.settings import ModelSettings
|
8
|
-
from pydantic_ai.usage import UsageLimits
|
9
|
-
|
10
|
-
from code_puppy.message_history_processor import message_history_accumulator
|
11
|
-
from code_puppy.messaging.message_queue import (
|
12
|
-
emit_error,
|
13
|
-
emit_info,
|
14
|
-
emit_system_message,
|
15
|
-
)
|
16
|
-
from code_puppy.model_factory import ModelFactory
|
17
|
-
|
18
|
-
# Tool registration is imported on demand
|
19
|
-
from code_puppy.tools.common import console
|
20
|
-
|
21
|
-
|
22
|
-
def load_puppy_rules():
|
23
|
-
global PUPPY_RULES
|
24
|
-
|
25
|
-
# Check for all 4 combinations of the rules file
|
26
|
-
possible_paths = ["AGENTS.md", "AGENT.md", "agents.md", "agent.md"]
|
27
|
-
|
28
|
-
for path_str in possible_paths:
|
29
|
-
puppy_rules_path = Path(path_str)
|
30
|
-
if puppy_rules_path.exists():
|
31
|
-
with open(puppy_rules_path, "r") as f:
|
32
|
-
puppy_rules = f.read()
|
33
|
-
return puppy_rules
|
34
|
-
|
35
|
-
# If none of the files exist, return None
|
36
|
-
return None
|
37
|
-
|
38
|
-
|
39
|
-
# Load at import
|
40
|
-
PUPPY_RULES = load_puppy_rules()
|
41
|
-
_LAST_MODEL_NAME = None
|
42
|
-
_code_generation_agent = None
|
43
|
-
|
44
|
-
|
45
|
-
def _load_mcp_servers(extra_headers: Optional[Dict[str, str]] = None):
|
46
|
-
"""Load MCP servers using the new manager while maintaining backward compatibility."""
|
47
|
-
from code_puppy.config import get_value, load_mcp_server_configs
|
48
|
-
from code_puppy.mcp_ import ServerConfig, get_mcp_manager
|
49
|
-
|
50
|
-
# Check if MCP servers are disabled
|
51
|
-
mcp_disabled = get_value("disable_mcp_servers")
|
52
|
-
if mcp_disabled and str(mcp_disabled).lower() in ("1", "true", "yes", "on"):
|
53
|
-
emit_system_message("[dim]MCP servers disabled via config[/dim]")
|
54
|
-
return []
|
55
|
-
|
56
|
-
# Get the MCP manager singleton
|
57
|
-
manager = get_mcp_manager()
|
58
|
-
|
59
|
-
# Load configurations from legacy file for backward compatibility
|
60
|
-
configs = load_mcp_server_configs()
|
61
|
-
if not configs:
|
62
|
-
# Check if manager already has servers (could be from new system)
|
63
|
-
existing_servers = manager.list_servers()
|
64
|
-
if not existing_servers:
|
65
|
-
emit_system_message("[dim]No MCP servers configured[/dim]")
|
66
|
-
return []
|
67
|
-
else:
|
68
|
-
# Register servers from legacy config with manager
|
69
|
-
for name, conf in configs.items():
|
70
|
-
try:
|
71
|
-
# Convert legacy format to new ServerConfig
|
72
|
-
server_config = ServerConfig(
|
73
|
-
id=conf.get("id", f"{name}_{hash(name)}"),
|
74
|
-
name=name,
|
75
|
-
type=conf.get("type", "sse"),
|
76
|
-
enabled=conf.get("enabled", True),
|
77
|
-
config=conf,
|
78
|
-
)
|
79
|
-
|
80
|
-
# Check if server already registered
|
81
|
-
existing = manager.get_server_by_name(name)
|
82
|
-
if not existing:
|
83
|
-
# Register new server
|
84
|
-
manager.register_server(server_config)
|
85
|
-
emit_system_message(f"[dim]Registered MCP server: {name}[/dim]")
|
86
|
-
else:
|
87
|
-
# Update existing server config if needed
|
88
|
-
if existing.config != server_config.config:
|
89
|
-
manager.update_server(existing.id, server_config)
|
90
|
-
emit_system_message(f"[dim]Updated MCP server: {name}[/dim]")
|
91
|
-
|
92
|
-
except Exception as e:
|
93
|
-
emit_error(f"Failed to register MCP server '{name}': {str(e)}")
|
94
|
-
continue
|
95
|
-
|
96
|
-
# Get pydantic-ai compatible servers from manager
|
97
|
-
servers = manager.get_servers_for_agent()
|
98
|
-
|
99
|
-
if servers:
|
100
|
-
emit_system_message(
|
101
|
-
f"[green]Successfully loaded {len(servers)} MCP server(s)[/green]"
|
102
|
-
)
|
103
|
-
else:
|
104
|
-
emit_system_message(
|
105
|
-
"[yellow]No MCP servers available (check if servers are enabled)[/yellow]"
|
106
|
-
)
|
107
|
-
|
108
|
-
return servers
|
109
|
-
|
110
|
-
|
111
|
-
def reload_mcp_servers():
|
112
|
-
"""Reload MCP servers without restarting the agent."""
|
113
|
-
from code_puppy.mcp_ import get_mcp_manager
|
114
|
-
|
115
|
-
manager = get_mcp_manager()
|
116
|
-
# Reload configurations
|
117
|
-
_load_mcp_servers()
|
118
|
-
# Return updated servers
|
119
|
-
return manager.get_servers_for_agent()
|
120
|
-
|
121
|
-
|
122
|
-
def reload_code_generation_agent(message_group: str | None):
|
123
|
-
"""Force-reload the agent, usually after a model change."""
|
124
|
-
if message_group is None:
|
125
|
-
message_group = str(uuid.uuid4())
|
126
|
-
global _code_generation_agent, _LAST_MODEL_NAME
|
127
|
-
from code_puppy.agents import clear_agent_cache
|
128
|
-
from code_puppy.config import clear_model_cache, get_global_model_name
|
129
|
-
|
130
|
-
# Clear both ModelFactory cache and config cache when force reloading
|
131
|
-
clear_model_cache()
|
132
|
-
clear_agent_cache()
|
133
|
-
|
134
|
-
# Check if current agent has a pinned model
|
135
|
-
from code_puppy.agents import get_current_agent
|
136
|
-
|
137
|
-
agent_config = get_current_agent()
|
138
|
-
agent_model_name = None
|
139
|
-
if hasattr(agent_config, "get_model_name"):
|
140
|
-
agent_model_name = agent_config.get_model_name()
|
141
|
-
|
142
|
-
# Use agent-specific model if pinned, otherwise use global model
|
143
|
-
model_name = agent_model_name if agent_model_name else get_global_model_name()
|
144
|
-
emit_info(
|
145
|
-
f"[bold cyan]Loading Model: {model_name}[/bold cyan]",
|
146
|
-
message_group=message_group,
|
147
|
-
)
|
148
|
-
models_config = ModelFactory.load_config()
|
149
|
-
model = ModelFactory.get_model(model_name, models_config)
|
150
|
-
|
151
|
-
# Get agent-specific system prompt
|
152
|
-
agent_config = get_current_agent()
|
153
|
-
emit_info(
|
154
|
-
f"[bold magenta]Loading Agent: {agent_config.display_name}[/bold magenta]",
|
155
|
-
message_group=message_group,
|
156
|
-
)
|
157
|
-
|
158
|
-
instructions = agent_config.get_system_prompt()
|
159
|
-
|
160
|
-
if PUPPY_RULES:
|
161
|
-
instructions += f"\n{PUPPY_RULES}"
|
162
|
-
|
163
|
-
mcp_servers = _load_mcp_servers()
|
164
|
-
|
165
|
-
# Configure model settings with max_tokens if set
|
166
|
-
model_settings_dict = {"seed": 42}
|
167
|
-
# Get current agent to use its method
|
168
|
-
from code_puppy.agents import get_current_agent
|
169
|
-
current_agent = get_current_agent()
|
170
|
-
output_tokens = max(2048, min(int(0.05 * current_agent.get_model_context_length()) - 1024, 16384))
|
171
|
-
console.print(f"Max output tokens per message: {output_tokens}")
|
172
|
-
model_settings_dict["max_tokens"] = output_tokens
|
173
|
-
|
174
|
-
|
175
|
-
model_settings = ModelSettings(**model_settings_dict)
|
176
|
-
if "gpt-5" in model_name:
|
177
|
-
model_settings_dict["openai_reasoning_effort"] = "off"
|
178
|
-
model_settings_dict["extra_body"] = {
|
179
|
-
"verbosity": "low"
|
180
|
-
}
|
181
|
-
model_settings = OpenAIModelSettings(**model_settings_dict)
|
182
|
-
agent = Agent(
|
183
|
-
model=model,
|
184
|
-
instructions=instructions,
|
185
|
-
output_type=str,
|
186
|
-
retries=3,
|
187
|
-
mcp_servers=mcp_servers,
|
188
|
-
history_processors=[message_history_accumulator],
|
189
|
-
model_settings=model_settings,
|
190
|
-
)
|
191
|
-
|
192
|
-
# Register tools specified by the agent
|
193
|
-
from code_puppy.tools import register_tools_for_agent
|
194
|
-
|
195
|
-
agent_tools = agent_config.get_available_tools()
|
196
|
-
register_tools_for_agent(agent, agent_tools)
|
197
|
-
_code_generation_agent = agent
|
198
|
-
_LAST_MODEL_NAME = model_name
|
199
|
-
return _code_generation_agent
|
200
|
-
|
201
|
-
|
202
|
-
def get_code_generation_agent(force_reload=False, message_group: str | None = None):
|
203
|
-
"""
|
204
|
-
Retrieve the agent with the currently configured model.
|
205
|
-
Forces a reload if the model has changed, or if force_reload is passed.
|
206
|
-
"""
|
207
|
-
global _code_generation_agent, _LAST_MODEL_NAME
|
208
|
-
if message_group is None:
|
209
|
-
message_group = str(uuid.uuid4())
|
210
|
-
from code_puppy.config import get_global_model_name
|
211
|
-
|
212
|
-
# Get the global model name
|
213
|
-
global_model_name = get_global_model_name()
|
214
|
-
|
215
|
-
# Check if current agent has a pinned model
|
216
|
-
from code_puppy.agents import get_current_agent
|
217
|
-
|
218
|
-
agent_config = get_current_agent()
|
219
|
-
agent_model_name = None
|
220
|
-
if hasattr(agent_config, "get_model_name"):
|
221
|
-
agent_model_name = agent_config.get_model_name()
|
222
|
-
|
223
|
-
# Use agent-specific model if pinned, otherwise use global model
|
224
|
-
model_name = agent_model_name if agent_model_name else global_model_name
|
225
|
-
|
226
|
-
if _code_generation_agent is None or _LAST_MODEL_NAME != model_name or force_reload:
|
227
|
-
return reload_code_generation_agent(message_group)
|
228
|
-
return _code_generation_agent
|
229
|
-
|
230
|
-
|
231
|
-
def get_custom_usage_limits():
|
232
|
-
"""
|
233
|
-
Returns custom usage limits with configurable request limit.
|
234
|
-
This centralizes the configuration of rate limiting for the agent.
|
235
|
-
Default pydantic-ai limit is 50, this increases it to the configured value (default 100).
|
236
|
-
"""
|
237
|
-
from code_puppy.config import get_message_limit
|
238
|
-
|
239
|
-
return UsageLimits(request_limit=get_message_limit())
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|