code-puppy 0.0.169__py3-none-any.whl → 0.0.171__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/agent.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import uuid
2
2
  from pathlib import Path
3
+ from pydantic_ai.models.openai import OpenAIModelSettings, OpenAIResponsesModelSettings
3
4
  from typing import Dict, Optional
4
5
 
5
6
  from pydantic_ai import Agent
@@ -170,7 +171,14 @@ def reload_code_generation_agent(message_group: str | None):
170
171
  console.print(f"Max output tokens per message: {output_tokens}")
171
172
  model_settings_dict["max_tokens"] = output_tokens
172
173
 
174
+
173
175
  model_settings = ModelSettings(**model_settings_dict)
176
+ if "gpt-5" in model_name:
177
+ model_settings_dict["openai_reasoning_effort"] = "high"
178
+ model_settings_dict["extra_body"] = {
179
+ "verbosity": "low"
180
+ }
181
+ model_settings = OpenAIModelSettings(**model_settings_dict)
174
182
  agent = Agent(
175
183
  model=model,
176
184
  instructions=instructions,
@@ -3,7 +3,15 @@ import queue
3
3
  from typing import Any, List, Set, Tuple
4
4
 
5
5
  import pydantic
6
- from pydantic_ai.messages import ModelMessage, ModelRequest, TextPart, ToolCallPart
6
+ from pydantic_ai.messages import (
7
+ ModelMessage,
8
+ ModelRequest,
9
+ TextPart,
10
+ ToolCallPart,
11
+ ToolCallPartDelta,
12
+ ToolReturn,
13
+ ToolReturnPart,
14
+ )
7
15
 
8
16
  from code_puppy.config import (
9
17
  get_model_name,
@@ -82,9 +90,46 @@ def estimate_tokens_for_message(message: ModelMessage) -> int:
82
90
 
83
91
 
84
92
  def filter_huge_messages(messages: List[ModelMessage]) -> List[ModelMessage]:
85
- filtered = [m for m in messages if estimate_tokens_for_message(m) < 50000]
86
- pruned = prune_interrupted_tool_calls(filtered)
87
- return pruned
93
+ if not messages:
94
+ return []
95
+
96
+ # Never drop the system prompt, even if it is extremely large.
97
+ system_message, *rest = messages
98
+ filtered_rest = [
99
+ m for m in rest if estimate_tokens_for_message(m) < 50000
100
+ ]
101
+ return [system_message] + filtered_rest
102
+
103
+
104
+ def _is_tool_call_part(part: Any) -> bool:
105
+ if isinstance(part, (ToolCallPart, ToolCallPartDelta)):
106
+ return True
107
+
108
+ part_kind = (getattr(part, "part_kind", "") or "").replace("_", "-")
109
+ if part_kind == "tool-call":
110
+ return True
111
+
112
+ has_tool_name = getattr(part, "tool_name", None) is not None
113
+ has_args = getattr(part, "args", None) is not None
114
+ has_args_delta = getattr(part, "args_delta", None) is not None
115
+
116
+ return bool(has_tool_name and (has_args or has_args_delta))
117
+
118
+
119
+ def _is_tool_return_part(part: Any) -> bool:
120
+ if isinstance(part, (ToolReturnPart, ToolReturn)):
121
+ return True
122
+
123
+ part_kind = (getattr(part, "part_kind", "") or "").replace("_", "-")
124
+ if part_kind in {"tool-return", "tool-result"}:
125
+ return True
126
+
127
+ if getattr(part, "tool_call_id", None) is None:
128
+ return False
129
+
130
+ has_content = getattr(part, "content", None) is not None
131
+ has_content_delta = getattr(part, "content_delta", None) is not None
132
+ return bool(has_content or has_content_delta)
88
133
 
89
134
 
90
135
  def split_messages_for_protected_summarization(
@@ -126,19 +171,18 @@ def split_messages_for_protected_summarization(
126
171
  if protected_token_count + message_tokens > protected_tokens_limit:
127
172
  break
128
173
 
129
- protected_messages.insert(0, message) # Insert at beginning to maintain order
174
+ protected_messages.append(message)
130
175
  protected_token_count += message_tokens
131
176
 
132
- # Add system message at the beginning of protected messages
177
+ # Messages that were added while scanning backwards are currently in reverse order.
178
+ # Reverse them to restore chronological ordering, then prepend the system prompt.
179
+ protected_messages.reverse()
133
180
  protected_messages.insert(0, system_message)
134
181
 
135
- # Messages to summarize are everything between system message and protected zone
136
- protected_start_idx = (
137
- len(messages) - len(protected_messages) + 1
138
- ) # +1 because system message is protected
139
- messages_to_summarize = messages[
140
- 1:protected_start_idx
141
- ] # Start from 1 to skip system message
182
+ # Messages to summarize are everything between the system message and the
183
+ # protected tail zone we just constructed.
184
+ protected_start_idx = max(1, len(messages) - (len(protected_messages) - 1))
185
+ messages_to_summarize = messages[1:protected_start_idx]
142
186
 
143
187
  emit_info(
144
188
  f"🔒 Protecting {len(protected_messages)} recent messages ({protected_token_count} tokens, limit: {protected_tokens_limit})"
@@ -164,43 +208,28 @@ def deduplicate_tool_returns(messages: List[ModelMessage]) -> List[ModelMessage]
164
208
  removed_count = 0
165
209
 
166
210
  for msg in messages:
167
- # Check if this message has any parts we need to filter
168
211
  if not hasattr(msg, "parts") or not msg.parts:
169
212
  deduplicated.append(msg)
170
213
  continue
171
214
 
172
- # Filter parts within this message
173
215
  filtered_parts = []
174
216
  msg_had_duplicates = False
175
217
 
176
218
  for part in msg.parts:
177
219
  tool_call_id = getattr(part, "tool_call_id", None)
178
- part_kind = getattr(part, "part_kind", None)
179
-
180
- # Check if this is a tool-return part
181
- if tool_call_id and part_kind in {
182
- "tool-return",
183
- "tool-result",
184
- "tool_result",
185
- }:
220
+ if tool_call_id and _is_tool_return_part(part):
186
221
  if tool_call_id in seen_tool_returns:
187
- # This is a duplicate return, skip it
188
222
  msg_had_duplicates = True
189
223
  removed_count += 1
190
224
  continue
191
- else:
192
- # First occurrence of this return, keep it
193
- seen_tool_returns.add(tool_call_id)
194
- filtered_parts.append(part)
195
- else:
196
- # Not a tool return, always keep
197
- filtered_parts.append(part)
225
+ seen_tool_returns.add(tool_call_id)
226
+ filtered_parts.append(part)
227
+
228
+ if not filtered_parts:
229
+ continue
198
230
 
199
- # If we filtered out parts, create a new message with filtered parts
200
- if msg_had_duplicates and filtered_parts:
201
- # Create a new message with the same attributes but filtered parts
231
+ if msg_had_duplicates:
202
232
  new_msg = type(msg)(parts=filtered_parts)
203
- # Copy over other attributes if they exist
204
233
  for attr_name in dir(msg):
205
234
  if (
206
235
  not attr_name.startswith("_")
@@ -210,12 +239,10 @@ def deduplicate_tool_returns(messages: List[ModelMessage]) -> List[ModelMessage]
210
239
  try:
211
240
  setattr(new_msg, attr_name, getattr(msg, attr_name))
212
241
  except (AttributeError, TypeError):
213
- # Skip attributes that can't be set
214
242
  pass
215
243
  deduplicated.append(new_msg)
216
- elif filtered_parts: # No duplicates but has parts
244
+ else:
217
245
  deduplicated.append(msg)
218
- # If no parts remain after filtering, drop the entire message
219
246
 
220
247
  if removed_count > 0:
221
248
  emit_warning(f"Removed {removed_count} duplicate tool-return part(s)")
@@ -224,23 +251,35 @@ def deduplicate_tool_returns(messages: List[ModelMessage]) -> List[ModelMessage]
224
251
 
225
252
 
226
253
  def summarize_messages(
227
- messages: List[ModelMessage], with_protection=True
254
+ messages: List[ModelMessage], with_protection: bool = True
228
255
  ) -> Tuple[List[ModelMessage], List[ModelMessage]]:
229
256
  """
230
257
  Summarize messages while protecting recent messages up to PROTECTED_TOKENS.
231
258
 
232
259
  Returns:
233
- List of messages: [system_message, summary_of_old_messages, ...protected_recent_messages]
260
+ Tuple of (compacted_messages, summarized_source_messages)
261
+ where compacted_messages always preserves the original system message
262
+ as the first entry.
234
263
  """
235
- messages_to_summarize, protected_messages = messages, []
264
+ messages_to_summarize: List[ModelMessage]
265
+ protected_messages: List[ModelMessage]
266
+
236
267
  if with_protection:
237
268
  messages_to_summarize, protected_messages = (
238
269
  split_messages_for_protected_summarization(messages)
239
270
  )
271
+ else:
272
+ messages_to_summarize = messages[1:] if messages else []
273
+ protected_messages = messages[:1]
274
+
275
+ if not messages:
276
+ return [], []
277
+
278
+ system_message = messages[0]
240
279
 
241
280
  if not messages_to_summarize:
242
- # Nothing to summarize, return protected messages as-is
243
- return protected_messages, messages_to_summarize
281
+ # Nothing to summarize, so just return the original sequence
282
+ return prune_interrupted_tool_calls(messages), []
244
283
 
245
284
  instructions = (
246
285
  "The input will be a log of Agentic AI steps that have been taken"
@@ -257,12 +296,24 @@ def summarize_messages(
257
296
  new_messages = run_summarization_sync(
258
297
  instructions, message_history=messages_to_summarize
259
298
  )
260
- # Return: [system_message, summary, ...protected_recent_messages]
261
- result = new_messages + protected_messages[1:]
262
- return prune_interrupted_tool_calls(result), messages_to_summarize
299
+
300
+ if not isinstance(new_messages, list):
301
+ emit_warning(
302
+ "Summarization agent returned non-list output; wrapping into message request"
303
+ )
304
+ new_messages = [ModelRequest([TextPart(str(new_messages))])]
305
+
306
+ compacted: List[ModelMessage] = [system_message] + list(new_messages)
307
+
308
+ # Drop the system message from protected_messages because we already included it
309
+ protected_tail = [msg for msg in protected_messages if msg is not system_message]
310
+
311
+ compacted.extend(protected_tail)
312
+
313
+ return prune_interrupted_tool_calls(compacted), messages_to_summarize
263
314
  except Exception as e:
264
315
  emit_error(f"Summarization failed during compaction: {e}")
265
- return messages, messages_to_summarize # Return original messages on failure
316
+ return messages, [] # Return original messages on failure
266
317
 
267
318
 
268
319
  def summarize_message(message: ModelMessage) -> ModelMessage:
@@ -329,11 +380,10 @@ def prune_interrupted_tool_calls(messages: List[ModelMessage]) -> List[ModelMess
329
380
  tool_call_id = getattr(part, "tool_call_id", None)
330
381
  if not tool_call_id:
331
382
  continue
332
- # Heuristic: if it's an explicit ToolCallPart or has a tool_name/args,
333
- # consider it a call; otherwise it's a return/result.
334
- if part.part_kind == "tool-call":
383
+
384
+ if _is_tool_call_part(part) and not _is_tool_return_part(part):
335
385
  tool_call_ids.add(tool_call_id)
336
- else:
386
+ elif _is_tool_return_part(part):
337
387
  tool_return_ids.add(tool_call_id)
338
388
 
339
389
  mismatched: Set[str] = tool_call_ids.symmetric_difference(tool_return_ids)
@@ -362,12 +412,17 @@ def prune_interrupted_tool_calls(messages: List[ModelMessage]) -> List[ModelMess
362
412
 
363
413
 
364
414
  def message_history_processor(messages: List[ModelMessage]) -> List[ModelMessage]:
365
- # First, prune any interrupted/mismatched tool-call conversations
366
- total_current_tokens = sum(estimate_tokens_for_message(msg) for msg in messages)
415
+ cleaned_history = prune_interrupted_tool_calls(
416
+ deduplicate_tool_returns(messages)
417
+ )
418
+
419
+ total_current_tokens = sum(
420
+ estimate_tokens_for_message(msg) for msg in cleaned_history
421
+ )
367
422
 
368
423
  model_max = get_model_context_length()
369
424
 
370
- proportion_used = total_current_tokens / model_max
425
+ proportion_used = total_current_tokens / model_max if model_max else 0
371
426
 
372
427
  # Check if we're in TUI mode and can update the status bar
373
428
  from code_puppy.state_management import get_tui_app_instance, is_tui_mode
@@ -406,17 +461,15 @@ def message_history_processor(messages: List[ModelMessage]) -> List[ModelMessage
406
461
  compaction_strategy = get_compaction_strategy()
407
462
 
408
463
  if proportion_used > compaction_threshold:
464
+ filtered_history = filter_huge_messages(cleaned_history)
465
+
409
466
  if compaction_strategy == "truncation":
410
- # Use truncation instead of summarization
411
467
  protected_tokens = get_protected_token_count()
412
- result_messages = truncation(
413
- filter_huge_messages(messages), protected_tokens
414
- )
415
- summarized_messages = [] # No summarization in truncation mode
468
+ result_messages = truncation(filtered_history, protected_tokens)
469
+ summarized_messages: List[ModelMessage] = []
416
470
  else:
417
- # Default to summarization
418
471
  result_messages, summarized_messages = summarize_messages(
419
- filter_huge_messages(messages)
472
+ filtered_history
420
473
  )
421
474
 
422
475
  final_token_count = sum(
@@ -447,7 +500,9 @@ def message_history_processor(messages: List[ModelMessage]) -> List[ModelMessage
447
500
  for m in summarized_messages:
448
501
  add_compacted_message_hash(hash_message(m))
449
502
  return result_messages
450
- return messages
503
+
504
+ set_message_history(cleaned_history)
505
+ return cleaned_history
451
506
 
452
507
 
453
508
  def truncation(
@@ -475,16 +530,17 @@ def truncation(
475
530
 
476
531
 
477
532
  def message_history_accumulator(messages: List[Any]):
478
- _message_history = get_message_history()
479
- message_history_hashes = set([hash_message(m) for m in _message_history])
480
- for msg in messages:
481
- if (
482
- hash_message(msg) not in message_history_hashes
483
- and hash_message(msg) not in get_compacted_message_hashes()
484
- ):
485
- _message_history.append(msg)
486
-
487
- # Apply message history trimming using the main processor
488
- # This ensures we maintain global state while still managing context limits
489
- message_history_processor(_message_history)
490
- return get_message_history()
533
+ existing_history = list(get_message_history())
534
+ seen_hashes = {hash_message(message) for message in existing_history}
535
+ compacted_hashes = get_compacted_message_hashes()
536
+
537
+ for message in messages:
538
+ message_hash = hash_message(message)
539
+ if message_hash in seen_hashes or message_hash in compacted_hashes:
540
+ continue
541
+ existing_history.append(message)
542
+ seen_hashes.add(message_hash)
543
+
544
+ updated_history = message_history_processor(existing_history)
545
+ set_message_history(updated_history)
546
+ return updated_history
@@ -1,44 +1,32 @@
1
- from typing import Any, List
1
+ import json
2
+ from types import ModuleType
3
+ from typing import Any, List, Set
2
4
 
3
- # Legacy global state - maintained for backward compatibility
4
- _message_history: List[Any] = []
5
- _compacted_message_hashes = set()
5
+ import pydantic
6
6
 
7
- # Flag to control whether to use agent-specific history (True) or global history (False)
8
- _use_agent_specific_history = True
9
7
  _tui_mode: bool = False
10
8
  _tui_app_instance: Any = None
11
9
 
12
10
 
11
+ def _require_agent_manager() -> ModuleType:
12
+ """Import the agent manager module, raising if it is unavailable."""
13
+ try:
14
+ from code_puppy.agents import agent_manager
15
+ except Exception as error: # pragma: no cover - import errors surface immediately
16
+ raise RuntimeError("Agent manager module unavailable") from error
17
+ return agent_manager
18
+
19
+
13
20
  def add_compacted_message_hash(message_hash: str) -> None:
14
21
  """Add a message hash to the set of compacted message hashes."""
15
- if _use_agent_specific_history:
16
- try:
17
- from code_puppy.agents.agent_manager import (
18
- add_current_agent_compacted_message_hash,
19
- )
20
-
21
- add_current_agent_compacted_message_hash(message_hash)
22
- return
23
- except Exception:
24
- # Fallback to global if agent system fails
25
- pass
26
- _compacted_message_hashes.add(message_hash)
22
+ manager = _require_agent_manager()
23
+ manager.add_current_agent_compacted_message_hash(message_hash)
27
24
 
28
25
 
29
- def get_compacted_message_hashes():
26
+ def get_compacted_message_hashes() -> Set[str]:
30
27
  """Get the set of compacted message hashes."""
31
- if _use_agent_specific_history:
32
- try:
33
- from code_puppy.agents.agent_manager import (
34
- get_current_agent_compacted_message_hashes,
35
- )
36
-
37
- return get_current_agent_compacted_message_hashes()
38
- except Exception:
39
- # Fallback to global if agent system fails
40
- pass
41
- return _compacted_message_hashes
28
+ manager = _require_agent_manager()
29
+ return manager.get_current_agent_compacted_message_hashes()
42
30
 
43
31
 
44
32
  def set_tui_mode(enabled: bool) -> None:
@@ -89,112 +77,81 @@ def get_tui_mode() -> bool:
89
77
 
90
78
 
91
79
  def get_message_history() -> List[Any]:
92
- """Get message history - uses agent-specific history if enabled, otherwise global."""
93
- if _use_agent_specific_history:
94
- try:
95
- from code_puppy.agents.agent_manager import (
96
- get_current_agent_message_history,
97
- )
98
-
99
- return get_current_agent_message_history()
100
- except Exception:
101
- # Fallback to global if agent system fails
102
- return _message_history
103
- return _message_history
80
+ """Get message history for the active agent."""
81
+ manager = _require_agent_manager()
82
+ return manager.get_current_agent_message_history()
104
83
 
105
84
 
106
85
  def set_message_history(history: List[Any]) -> None:
107
- """Set message history - uses agent-specific history if enabled, otherwise global."""
108
- if _use_agent_specific_history:
109
- try:
110
- from code_puppy.agents.agent_manager import (
111
- set_current_agent_message_history,
112
- )
113
-
114
- set_current_agent_message_history(history)
115
- return
116
- except Exception:
117
- # Fallback to global if agent system fails
118
- pass
119
- global _message_history
120
- _message_history = history
86
+ """Replace the message history for the active agent."""
87
+ manager = _require_agent_manager()
88
+ manager.set_current_agent_message_history(history)
121
89
 
122
90
 
123
91
  def clear_message_history() -> None:
124
- """Clear message history - uses agent-specific history if enabled, otherwise global."""
125
- if _use_agent_specific_history:
126
- try:
127
- from code_puppy.agents.agent_manager import (
128
- clear_current_agent_message_history,
129
- )
130
-
131
- clear_current_agent_message_history()
132
- return
133
- except Exception:
134
- # Fallback to global if agent system fails
135
- pass
136
- global _message_history
137
- _message_history = []
92
+ """Clear message history for the active agent."""
93
+ manager = _require_agent_manager()
94
+ manager.clear_current_agent_message_history()
138
95
 
139
96
 
140
97
  def append_to_message_history(message: Any) -> None:
141
- """Append to message history - uses agent-specific history if enabled, otherwise global."""
142
- if _use_agent_specific_history:
143
- try:
144
- from code_puppy.agents.agent_manager import (
145
- append_to_current_agent_message_history,
146
- )
147
-
148
- append_to_current_agent_message_history(message)
149
- return
150
- except Exception:
151
- # Fallback to global if agent system fails
152
- pass
153
- _message_history.append(message)
98
+ """Append a message to the active agent's history."""
99
+ manager = _require_agent_manager()
100
+ manager.append_to_current_agent_message_history(message)
154
101
 
155
102
 
156
103
  def extend_message_history(history: List[Any]) -> None:
157
- """Extend message history - uses agent-specific history if enabled, otherwise global."""
158
- if _use_agent_specific_history:
159
- try:
160
- from code_puppy.agents.agent_manager import (
161
- extend_current_agent_message_history,
162
- )
163
-
164
- extend_current_agent_message_history(history)
165
- return
166
- except Exception:
167
- # Fallback to global if agent system fails
168
- pass
169
- _message_history.extend(history)
170
-
171
-
172
- def set_use_agent_specific_history(enabled: bool) -> None:
173
- """Enable or disable agent-specific message history.
174
-
175
- Args:
176
- enabled: True to use per-agent history, False to use global history.
177
- """
178
- global _use_agent_specific_history
179
- _use_agent_specific_history = enabled
180
-
181
-
182
- def is_using_agent_specific_history() -> bool:
183
- """Check if agent-specific message history is enabled.
184
-
185
- Returns:
186
- True if using per-agent history, False if using global history.
187
- """
188
- return _use_agent_specific_history
189
-
190
-
191
- def hash_message(message):
192
- hashable_entities = []
193
- for part in message.parts:
194
- if hasattr(part, "timestamp"):
195
- hashable_entities.append(part.timestamp.isoformat())
196
- elif hasattr(part, "tool_call_id"):
197
- hashable_entities.append(part.tool_call_id)
198
- else:
199
- hashable_entities.append(part.content)
200
- return hash(",".join(hashable_entities))
104
+ """Extend the active agent's message history."""
105
+ manager = _require_agent_manager()
106
+ manager.extend_current_agent_message_history(history)
107
+
108
+
109
+ def _stringify_part(part: Any) -> str:
110
+ """Create a stable string representation for a message part.
111
+
112
+ We deliberately ignore timestamps so identical content hashes the same even when
113
+ emitted at different times. This prevents status updates from blowing up the
114
+ history when they are repeated with new timestamps."""
115
+
116
+ attributes: List[str] = [part.__class__.__name__]
117
+
118
+ # Role/instructions help disambiguate parts that otherwise share content
119
+ if hasattr(part, "role") and part.role:
120
+ attributes.append(f"role={part.role}")
121
+ if hasattr(part, "instructions") and part.instructions:
122
+ attributes.append(f"instructions={part.instructions}")
123
+
124
+ if hasattr(part, "tool_call_id") and part.tool_call_id:
125
+ attributes.append(f"tool_call_id={part.tool_call_id}")
126
+
127
+ if hasattr(part, "tool_name") and part.tool_name:
128
+ attributes.append(f"tool_name={part.tool_name}")
129
+
130
+ content = getattr(part, "content", None)
131
+ if content is None:
132
+ attributes.append("content=None")
133
+ elif isinstance(content, str):
134
+ attributes.append(f"content={content}")
135
+ elif isinstance(content, pydantic.BaseModel):
136
+ attributes.append(f"content={json.dumps(content.model_dump(), sort_keys=True)}")
137
+ elif isinstance(content, dict):
138
+ attributes.append(f"content={json.dumps(content, sort_keys=True)}")
139
+ else:
140
+ attributes.append(f"content={repr(content)}")
141
+
142
+ return "|".join(attributes)
143
+
144
+
145
+ def hash_message(message: Any) -> int:
146
+ """Create a stable hash for a model message that ignores timestamps."""
147
+ role = getattr(message, "role", None)
148
+ instructions = getattr(message, "instructions", None)
149
+ header_bits: List[str] = []
150
+ if role:
151
+ header_bits.append(f"role={role}")
152
+ if instructions:
153
+ header_bits.append(f"instructions={instructions}")
154
+
155
+ part_strings = [_stringify_part(part) for part in getattr(message, "parts", [])]
156
+ canonical = "||".join(header_bits + part_strings)
157
+ return hash(canonical)
code_puppy/tui/app.py CHANGED
@@ -430,8 +430,10 @@ class CodePuppyTUI(App):
430
430
  else:
431
431
  # Only cancel the agent task if NO processes were killed
432
432
  self._current_worker.cancel()
433
- state_management._message_history = prune_interrupted_tool_calls(
434
- state_management.get_message_history()
433
+ state_management.set_message_history(
434
+ prune_interrupted_tool_calls(
435
+ state_management.get_message_history()
436
+ )
435
437
  )
436
438
  self.add_system_message("⚠️ Processing cancelled by user")
437
439
  # Stop spinner and clear state only when agent is actually cancelled
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.169
3
+ Version: 0.0.171
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -24,7 +24,7 @@ Requires-Dist: logfire>=0.7.1
24
24
  Requires-Dist: openai>=1.99.1
25
25
  Requires-Dist: pathspec>=0.11.0
26
26
  Requires-Dist: prompt-toolkit>=3.0.38
27
- Requires-Dist: pydantic-ai>=1.0.0
27
+ Requires-Dist: pydantic-ai>=1.0.10
28
28
  Requires-Dist: pydantic>=2.4.0
29
29
  Requires-Dist: pyjwt>=2.8.0
30
30
  Requires-Dist: pytest-cov>=6.1.1
@@ -1,16 +1,16 @@
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=wCARhq7PcD38c4l83fuXm4Us_6c1TXVa0U7TnBnNPEY,8064
3
+ code_puppy/agent.py,sha256=Bc5tFgizALEHTknleMaX2CMZE9DJM-Rq7oFb-__a5R0,8402
4
4
  code_puppy/callbacks.py,sha256=6wYB6K_fGSCkKKEFaYOYkJT45WaV5W_NhUIzcvVH_nU,5060
5
5
  code_puppy/config.py,sha256=J8XU0iOPtfzrkDD49b4TdrdHoQmW2kaP-25PPbGGdKU,16386
6
6
  code_puppy/http_utils.py,sha256=XJ1ItAW3I4zdJNzybV17Z8Y58NoFyAJEjUg7I69SXlw,8096
7
7
  code_puppy/main.py,sha256=tYLfhUjPTJ-4S1r-pr-jSbn6kIU1iYvt2Z8lxI7zDFY,22220
8
- code_puppy/message_history_processor.py,sha256=zzeOSUC1Wpsry-z2MD6pQWk2wt1axGSOKaGR2v22_qQ,18825
8
+ code_puppy/message_history_processor.py,sha256=u9RsoL4M_U53Y1n-vNGaRum4CXVl46dMUn67l3qlRSE,19826
9
9
  code_puppy/model_factory.py,sha256=z9vQbcGllgMwU0On8rPvzYxkygW2Uyd3NJmRzbKv-is,13759
10
10
  code_puppy/models.json,sha256=iXmLZGflnQcu2DRh4WUlgAhoXdvoxUc7KBhB8YxawXM,3088
11
11
  code_puppy/reopenable_async_client.py,sha256=4UJRaMp5np8cbef9F0zKQ7TPKOfyf5U-Kv-0zYUWDho,8274
12
12
  code_puppy/round_robin_model.py,sha256=SEN3VSwTgC5wHjx2sZsHQLPWOycf4jGwzB-EydgqkdY,5643
13
- code_puppy/state_management.py,sha256=o4mNBCPblRyVrNBH-992-1YqffgH6AKHU7iZRqgP1LI,5925
13
+ code_puppy/state_management.py,sha256=dJtxGfOVJdhJ-PcPwwFsGr4tOOq_xHIbz7LoU-2lMco,4936
14
14
  code_puppy/status_display.py,sha256=F6eEAkGePDp4StM2BWj-uLLQTDGtJrf0IufzCeP1rRg,8336
15
15
  code_puppy/summarization_agent.py,sha256=kos4_YK-l_YjYRq4Fs4X5YoTUbmAcDhhPqefL-rdenI,3197
16
16
  code_puppy/version_checker.py,sha256=bjLDmgGPrl7XnYwX1u13O8uFlsfikV90PK6nbA9Z9QU,1150
@@ -84,7 +84,7 @@ code_puppy/tools/file_modifications.py,sha256=EaDWcv6gi8wAvpgyeJdKSKPWg9fTpZoEkx
84
84
  code_puppy/tools/file_operations.py,sha256=dEnsGCbDF12ctegCm9Kiu-mgNCrvopf64ij_CQbikW4,32460
85
85
  code_puppy/tools/tools_content.py,sha256=bsBqW-ppd1XNAS_g50B3UHDQBWEALC1UneH6-afz1zo,2365
86
86
  code_puppy/tui/__init__.py,sha256=XesAxIn32zLPOmvpR2wIDxDAnnJr81a5pBJB4cZp1Xs,321
87
- code_puppy/tui/app.py,sha256=nPOzwlusjdWzBfu__EbC3Q0etkPrqRq-2g-mk4IcfG4,39378
87
+ code_puppy/tui/app.py,sha256=6yNOC7_93WjfwUxI6LBLmhTZl1FAXO03ErsSq5HxBVs,39434
88
88
  code_puppy/tui/messages.py,sha256=zQoToWI0eWdT36NEsY6RdCFzcDfAmfvoPlHv8jiCbgo,720
89
89
  code_puppy/tui/components/__init__.py,sha256=uj5pnk3s6SEN3SbFI0ZnzaA2KK1NNg8TfUj6U-Z732U,455
90
90
  code_puppy/tui/components/chat_view.py,sha256=NfyNXuN2idPht1rKJB4YhHVXb1AIRNO5q_nLdt8Ocug,19913
@@ -104,9 +104,9 @@ code_puppy/tui/screens/help.py,sha256=eJuPaOOCp7ZSUlecearqsuX6caxWv7NQszUh0tZJjB
104
104
  code_puppy/tui/screens/mcp_install_wizard.py,sha256=xqwN5omltMkfxWZwXj3D2PbXbtrxUi1dT0XT77oxOKk,27685
105
105
  code_puppy/tui/screens/settings.py,sha256=GMpv-qa08rorAE9mj3AjmqjZFPhmeJ_GWd-DBHG6iAA,10671
106
106
  code_puppy/tui/screens/tools.py,sha256=3pr2Xkpa9Js6Yhf1A3_wQVRzFOui-KDB82LwrsdBtyk,1715
107
- code_puppy-0.0.169.data/data/code_puppy/models.json,sha256=iXmLZGflnQcu2DRh4WUlgAhoXdvoxUc7KBhB8YxawXM,3088
108
- code_puppy-0.0.169.dist-info/METADATA,sha256=-cW4srdgLTHw8gOC4LFKiZ6RjmO9SQVKRHnp8vl8N3s,20039
109
- code_puppy-0.0.169.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
110
- code_puppy-0.0.169.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
111
- code_puppy-0.0.169.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
112
- code_puppy-0.0.169.dist-info/RECORD,,
107
+ code_puppy-0.0.171.data/data/code_puppy/models.json,sha256=iXmLZGflnQcu2DRh4WUlgAhoXdvoxUc7KBhB8YxawXM,3088
108
+ code_puppy-0.0.171.dist-info/METADATA,sha256=pi25vuxOTIXHF9NclB1jHqYy7OXmpK8ylrScDBnKlEM,20040
109
+ code_puppy-0.0.171.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
110
+ code_puppy-0.0.171.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
111
+ code_puppy-0.0.171.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
112
+ code_puppy-0.0.171.dist-info/RECORD,,
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  code-puppy = code_puppy.main:main_entry
3
+ pup = code_puppy.main:main_entry