code-puppy 0.0.123__py3-none-any.whl → 0.0.125__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.
@@ -101,15 +101,15 @@ class StatusBar(Static):
101
101
  token_color = "green"
102
102
  if self.token_count > 0 and self.token_capacity > 0:
103
103
  # Import here to avoid circular import
104
- from code_puppy.config import get_summarization_threshold
104
+ from code_puppy.config import get_compaction_threshold
105
105
 
106
- summarization_threshold = get_summarization_threshold()
106
+ get_compaction_threshold = get_compaction_threshold()
107
107
 
108
- if self.token_proportion > summarization_threshold:
108
+ if self.token_proportion > get_compaction_threshold:
109
109
  token_color = "red"
110
110
  token_status = f"🔴 {self.token_count}/{self.token_capacity} ({self.token_proportion:.1%})"
111
111
  elif self.token_proportion > (
112
- summarization_threshold - 0.15
112
+ get_compaction_threshold - 0.15
113
113
  ): # 15% before summarization threshold
114
114
  token_color = "yellow"
115
115
  token_status = f"🟡 {self.token_count}/{self.token_capacity} ({self.token_proportion:.1%})"
@@ -100,9 +100,20 @@ class SettingsScreen(ModalScreen):
100
100
  )
101
101
 
102
102
  with Container(classes="setting-row"):
103
- yield Static("Summary Threshold:", classes="setting-label")
103
+ yield Static("Compaction Strategy:", classes="setting-label")
104
+ yield Select(
105
+ [
106
+ ("Summarization", "summarization"),
107
+ ("Truncation", "truncation"),
108
+ ],
109
+ id="compaction-strategy-select",
110
+ classes="setting-input",
111
+ )
112
+
113
+ with Container(classes="setting-row"):
114
+ yield Static("Compaction Threshold:", classes="setting-label")
104
115
  yield Input(
105
- id="summary-threshold-input",
116
+ id="compaction-threshold-input",
106
117
  classes="setting-input",
107
118
  placeholder="e.g., 0.85",
108
119
  )
@@ -118,7 +129,8 @@ class SettingsScreen(ModalScreen):
118
129
  get_owner_name,
119
130
  get_protected_token_count,
120
131
  get_puppy_name,
121
- get_summarization_threshold,
132
+ get_compaction_strategy,
133
+ get_compaction_threshold,
122
134
  )
123
135
 
124
136
  # Load current values
@@ -126,12 +138,18 @@ class SettingsScreen(ModalScreen):
126
138
  owner_name_input = self.query_one("#owner-name-input", Input)
127
139
  model_select = self.query_one("#model-select", Select)
128
140
  protected_tokens_input = self.query_one("#protected-tokens-input", Input)
129
- summary_threshold_input = self.query_one("#summary-threshold-input", Input)
141
+ compaction_threshold_input = self.query_one(
142
+ "#compaction-threshold-input", Input
143
+ )
144
+ compaction_strategy_select = self.query_one(
145
+ "#compaction-strategy-select", Select
146
+ )
130
147
 
131
148
  puppy_name_input.value = get_puppy_name() or ""
132
149
  owner_name_input.value = get_owner_name() or ""
133
150
  protected_tokens_input.value = str(get_protected_token_count())
134
- summary_threshold_input.value = str(get_summarization_threshold())
151
+ compaction_threshold_input.value = str(get_compaction_threshold())
152
+ compaction_strategy_select.value = get_compaction_strategy()
135
153
 
136
154
  # Load available models
137
155
  self.load_model_options(model_select)
@@ -146,9 +164,7 @@ class SettingsScreen(ModalScreen):
146
164
  """Load available models into the model select widget."""
147
165
  try:
148
166
  # Use the same method that interactive mode uses to load models
149
- import os
150
167
 
151
- from code_puppy.config import CONFIG_DIR
152
168
  from code_puppy.model_factory import ModelFactory
153
169
 
154
170
  # Load models using the same path and method as interactive mode
@@ -171,7 +187,11 @@ class SettingsScreen(ModalScreen):
171
187
  @on(Button.Pressed, "#save-button")
172
188
  def save_settings(self) -> None:
173
189
  """Save the modified settings."""
174
- from code_puppy.config import set_config_value, set_model_name
190
+ from code_puppy.config import (
191
+ set_config_value,
192
+ set_model_name,
193
+ get_model_context_length,
194
+ )
175
195
 
176
196
  try:
177
197
  # Get values from inputs
@@ -182,8 +202,8 @@ class SettingsScreen(ModalScreen):
182
202
  protected_tokens = self.query_one(
183
203
  "#protected-tokens-input", Input
184
204
  ).value.strip()
185
- summary_threshold = self.query_one(
186
- "#summary-threshold-input", Input
205
+ compaction_threshold = self.query_one(
206
+ "#compaction-threshold-input", Input
187
207
  ).value.strip()
188
208
 
189
209
  # Validate and save
@@ -201,31 +221,46 @@ class SettingsScreen(ModalScreen):
201
221
  # Validate and save protected tokens
202
222
  if protected_tokens.isdigit():
203
223
  tokens_value = int(protected_tokens)
224
+ model_context_length = get_model_context_length()
225
+ max_protected_tokens = int(model_context_length * 0.75)
226
+
204
227
  if tokens_value >= 1000: # Minimum validation
205
- set_config_value("protected_token_count", protected_tokens)
228
+ if tokens_value <= max_protected_tokens: # Maximum validation
229
+ set_config_value("protected_token_count", protected_tokens)
230
+ else:
231
+ raise ValueError(
232
+ f"Protected tokens must not exceed 75% of model context length ({max_protected_tokens} tokens for current model)"
233
+ )
206
234
  else:
207
235
  raise ValueError("Protected tokens must be at least 1000")
208
236
  elif protected_tokens: # If not empty but not digit
209
237
  raise ValueError("Protected tokens must be a valid number")
210
238
 
211
- # Validate and save summary threshold
212
- if summary_threshold:
239
+ # Validate and save compaction threshold
240
+ if compaction_threshold:
213
241
  try:
214
- threshold_value = float(summary_threshold)
215
- if 0.1 <= threshold_value <= 0.95: # Same bounds as config function
216
- set_config_value("summarization_threshold", summary_threshold)
242
+ threshold_value = float(compaction_threshold)
243
+ if 0.8 <= threshold_value <= 0.95: # Same bounds as config function
244
+ set_config_value("compaction_threshold", compaction_threshold)
217
245
  else:
218
246
  raise ValueError(
219
- "Summary threshold must be between 0.1 and 0.95"
247
+ "Compaction threshold must be between 0.8 and 0.95"
220
248
  )
221
249
  except ValueError as ve:
222
250
  if "must be between" in str(ve):
223
251
  raise ve
224
252
  else:
225
253
  raise ValueError(
226
- "Summary threshold must be a valid decimal number"
254
+ "Compaction threshold must be a valid decimal number"
227
255
  )
228
256
 
257
+ # Save compaction strategy
258
+ compaction_strategy = self.query_one(
259
+ "#compaction-strategy-select", Select
260
+ ).value
261
+ if compaction_strategy in ["summarization", "truncation"]:
262
+ set_config_value("compaction_strategy", compaction_strategy)
263
+
229
264
  # Return success message with model change info
230
265
  message = "Settings saved successfully!"
231
266
  if selected_model:
@@ -0,0 +1,72 @@
1
+ """Tests for the /agent command handling in TUI mode."""
2
+
3
+ from unittest.mock import patch, MagicMock
4
+
5
+ from code_puppy.tui.app import CodePuppyTUI
6
+
7
+
8
+ class TestTUIAgentCommand:
9
+ """Test the TUI's handling of /agent commands."""
10
+
11
+ @patch("code_puppy.tui.app.get_code_generation_agent")
12
+ @patch("code_puppy.tui.app.handle_command")
13
+ def test_tui_handles_agent_command(self, mock_handle_command, mock_get_agent):
14
+ """Test that TUI properly delegates /agent commands to command handler."""
15
+ # Create a TUI app instance
16
+ app = CodePuppyTUI()
17
+
18
+ # Mock the agent
19
+ mock_agent_instance = MagicMock()
20
+ mock_get_agent.return_value = mock_agent_instance
21
+
22
+ # Mock handle_command to simulate successful processing
23
+ mock_handle_command.return_value = True
24
+
25
+ # Simulate processing an /agent command
26
+ message = "/agent code-puppy"
27
+ app.agent = mock_agent_instance
28
+
29
+ # Call the method that processes messages
30
+ # We'll need to mock some UI elements to avoid complex setup
31
+ with (
32
+ patch.object(app, "add_user_message"),
33
+ patch.object(app, "_update_submit_cancel_button"),
34
+ patch.object(app, "start_agent_progress"),
35
+ patch.object(app, "stop_agent_progress"),
36
+ patch.object(app, "refresh_history_display"),
37
+ ):
38
+ import asyncio
39
+
40
+ # Create an event loop for the async test
41
+ loop = asyncio.get_event_loop()
42
+ loop.run_until_complete(app.process_message(message))
43
+
44
+ # Verify that handle_command was called with the correct argument
45
+ mock_handle_command.assert_called_once_with(message)
46
+
47
+ # Verify that get_code_generation_agent was called to refresh the agent instance
48
+ mock_get_agent.assert_called()
49
+
50
+ @patch("code_puppy.tui.app.get_code_generation_agent")
51
+ def test_tui_refreshes_agent_after_command(self, mock_get_agent):
52
+ """Test that TUI refreshes its agent instance after processing /agent command."""
53
+ # Create a TUI app instance
54
+ app = CodePuppyTUI()
55
+
56
+ # Set initial agent
57
+ initial_agent = MagicMock()
58
+ app.agent = initial_agent
59
+
60
+ # Mock get_code_generation_agent to return a new agent instance
61
+ new_agent = MagicMock()
62
+ mock_get_agent.return_value = new_agent
63
+
64
+ # Simulate that an /agent command was processed
65
+ with patch("code_puppy.tui.app.handle_command"):
66
+ import asyncio
67
+
68
+ loop = asyncio.get_event_loop()
69
+ loop.run_until_complete(app.process_message("/agent code-puppy"))
70
+
71
+ # Verify that the agent was refreshed
72
+ mock_get_agent.assert_called()