code-puppy 0.0.319__py3-none-any.whl → 0.0.320__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.
@@ -1267,13 +1267,9 @@ class BaseAgent(ABC):
1267
1267
  ctx: The run context.
1268
1268
  events: Async iterable of streaming events (PartStartEvent, PartDeltaEvent, etc.).
1269
1269
  """
1270
- import os
1271
- import time as time_module
1272
-
1273
1270
  from pydantic_ai import PartDeltaEvent, PartStartEvent
1274
1271
  from pydantic_ai.messages import TextPartDelta, ThinkingPartDelta
1275
1272
  from rich.console import Console
1276
- from rich.live import Live
1277
1273
  from rich.markdown import Markdown
1278
1274
  from rich.markup import escape
1279
1275
 
@@ -1288,14 +1284,6 @@ class BaseAgent(ABC):
1288
1284
  # Fallback if console not set (shouldn't happen in normal use)
1289
1285
  console = Console()
1290
1286
 
1291
- # Disable Live display in test mode or non-interactive environments
1292
- # This fixes issues with pexpect PTY where Live() hangs
1293
- use_live_display = (
1294
- console.is_terminal
1295
- and os.environ.get("CODE_PUPPY_TEST_FAST", "").lower() not in ("1", "true")
1296
- and os.environ.get("CI", "").lower() not in ("1", "true")
1297
- )
1298
-
1299
1287
  # Track which part indices we're currently streaming (for Text/Thinking parts)
1300
1288
  streaming_parts: set[int] = set()
1301
1289
  thinking_parts: set[int] = (
@@ -1303,11 +1291,9 @@ class BaseAgent(ABC):
1303
1291
  ) # Track which parts are thinking (for dim style)
1304
1292
  text_parts: set[int] = set() # Track which parts are text
1305
1293
  banner_printed: set[int] = set() # Track if banner was already printed
1306
- text_buffer: dict[int, list[str]] = {} # Buffer text for markdown
1307
- live_displays: dict[int, Live] = {} # Live displays for streaming markdown
1294
+ text_buffer: dict[int, list[str]] = {} # Buffer text for final markdown render
1295
+ token_count: dict[int, int] = {} # Track token count per text part
1308
1296
  did_stream_anything = False # Track if we streamed any content
1309
- last_render_time: dict[int, float] = {} # Track last render time per part
1310
- render_interval = 0.1 # Only re-render markdown every 100ms (throttle)
1311
1297
 
1312
1298
  def _print_thinking_banner() -> None:
1313
1299
  """Print the THINKING banner with spinner pause and line clear."""
@@ -1372,9 +1358,11 @@ class BaseAgent(ABC):
1372
1358
  streaming_parts.add(event.index)
1373
1359
  text_parts.add(event.index)
1374
1360
  text_buffer[event.index] = [] # Initialize buffer
1361
+ token_count[event.index] = 0 # Initialize token counter
1375
1362
  # Buffer initial content if present
1376
1363
  if part.content and part.content.strip():
1377
1364
  text_buffer[event.index].append(part.content)
1365
+ token_count[event.index] += 1
1378
1366
 
1379
1367
  # PartDeltaEvent - stream the content as it arrives
1380
1368
  elif isinstance(event, PartDeltaEvent):
@@ -1382,43 +1370,23 @@ class BaseAgent(ABC):
1382
1370
  delta = event.delta
1383
1371
  if isinstance(delta, (TextPartDelta, ThinkingPartDelta)):
1384
1372
  if delta.content_delta:
1385
- # For text parts, stream markdown with Live display
1373
+ # For text parts, show token counter then render at end
1386
1374
  if event.index in text_parts:
1387
- # Print banner and start Live on first content
1375
+ import sys
1376
+
1377
+ # Print banner on first content
1388
1378
  if event.index not in banner_printed:
1389
1379
  _print_response_banner()
1390
1380
  banner_printed.add(event.index)
1391
- # Only use Live display if enabled (disabled in test/CI)
1392
- if use_live_display:
1393
- live = Live(
1394
- Markdown(""),
1395
- console=console,
1396
- refresh_per_second=10,
1397
- vertical_overflow="visible", # Allow scrolling for long content
1398
- )
1399
- live.start()
1400
- live_displays[event.index] = live
1401
- # Accumulate text and throttle markdown rendering
1402
- # (Markdown parsing is O(n), doing it on every token = O(n²) death)
1381
+ # Accumulate text for final markdown render
1403
1382
  text_buffer[event.index].append(delta.content_delta)
1404
- now = time_module.monotonic()
1405
- last_render = last_render_time.get(event.index, 0)
1406
-
1407
- # Only re-render if enough time has passed (throttle)
1408
- # Skip Live updates when not using live display
1409
- if (
1410
- use_live_display
1411
- and now - last_render >= render_interval
1412
- ):
1413
- content = "".join(text_buffer[event.index])
1414
- if event.index in live_displays:
1415
- try:
1416
- live_displays[event.index].update(
1417
- Markdown(content)
1418
- )
1419
- last_render_time[event.index] = now
1420
- except Exception:
1421
- pass
1383
+ token_count[event.index] += 1
1384
+ # Update token counter in place (single line)
1385
+ count = token_count[event.index]
1386
+ sys.stdout.write(
1387
+ f"\r\x1b[K ⏳ Receiving... {count} tokens"
1388
+ )
1389
+ sys.stdout.flush()
1422
1390
  else:
1423
1391
  # For thinking parts, stream immediately (dim)
1424
1392
  if event.index not in banner_printed:
@@ -1430,36 +1398,24 @@ class BaseAgent(ABC):
1430
1398
  # PartEndEvent - finish the streaming with a newline
1431
1399
  elif isinstance(event, PartEndEvent):
1432
1400
  if event.index in streaming_parts:
1433
- # For text parts, do final render then stop the Live display
1401
+ # For text parts, clear counter line and render markdown
1434
1402
  if event.index in text_parts:
1435
- # Final render to ensure we show complete content
1436
- # (throttling may have skipped the last few tokens)
1437
- if event.index in live_displays and event.index in text_buffer:
1438
- try:
1439
- final_content = "".join(text_buffer[event.index])
1440
- live_displays[event.index].update(
1441
- Markdown(final_content)
1442
- )
1443
- except Exception:
1444
- pass
1445
- if event.index in live_displays:
1446
- try:
1447
- live_displays[event.index].stop()
1448
- except Exception:
1449
- pass
1450
- del live_displays[event.index]
1451
- # When not using Live display, print the final content as markdown
1452
- elif event.index in text_buffer:
1403
+ import sys
1404
+
1405
+ # Clear the token counter line
1406
+ sys.stdout.write("\r\x1b[K")
1407
+ sys.stdout.flush()
1408
+ # Render the final markdown nicely
1409
+ if event.index in text_buffer:
1453
1410
  try:
1454
1411
  final_content = "".join(text_buffer[event.index])
1455
1412
  if final_content.strip():
1456
1413
  console.print(Markdown(final_content))
1457
1414
  except Exception:
1458
1415
  pass
1459
- if event.index in text_buffer:
1460
1416
  del text_buffer[event.index]
1461
- # Clean up render time tracking
1462
- last_render_time.pop(event.index, None)
1417
+ # Clean up token count
1418
+ token_count.pop(event.index, None)
1463
1419
  # For thinking parts, just print newline
1464
1420
  elif event.index in banner_printed:
1465
1421
  console.print() # Final newline after streaming
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.319
3
+ Version: 0.0.320
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
@@ -39,7 +39,7 @@ code_puppy/agents/agent_qa_expert.py,sha256=5Ikb4U3SZQknUEfwlHZiyZXKqnffnOTQagr_
39
39
  code_puppy/agents/agent_qa_kitten.py,sha256=5PeFFSwCFlTUvP6h5bGntx0xv5NmRwBiw0HnMqY8nLI,9107
40
40
  code_puppy/agents/agent_security_auditor.py,sha256=SpiYNA0XAsIwBj7S2_EQPRslRUmF_-b89pIJyW7DYtY,12022
41
41
  code_puppy/agents/agent_typescript_reviewer.py,sha256=vsnpp98xg6cIoFAEJrRTUM_i4wLEWGm5nJxs6fhHobM,10275
42
- code_puppy/agents/base_agent.py,sha256=62TcOKownogs8KVcFGuhuxp0Xvj9VrsaqLNwz1t96zs,80331
42
+ code_puppy/agents/base_agent.py,sha256=sT5bNdF2c-SnSdy1pwfZKnv2P-jwB7i67h353UqUN20,77624
43
43
  code_puppy/agents/json_agent.py,sha256=lhopDJDoiSGHvD8A6t50hi9ZBoNRKgUywfxd0Po_Dzc,4886
44
44
  code_puppy/agents/prompt_reviewer.py,sha256=JJrJ0m5q0Puxl8vFsyhAbY9ftU9n6c6UxEVdNct1E-Q,5558
45
45
  code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
@@ -159,10 +159,10 @@ code_puppy/tools/browser/browser_scripts.py,sha256=sNb8eLEyzhasy5hV4B9OjM8yIVMLV
159
159
  code_puppy/tools/browser/browser_workflows.py,sha256=nitW42vCf0ieTX1gLabozTugNQ8phtoFzZbiAhw1V90,6491
160
160
  code_puppy/tools/browser/camoufox_manager.py,sha256=RZjGOEftE5sI_tsercUyXFSZI2wpStXf-q0PdYh2G3I,8680
161
161
  code_puppy/tools/browser/vqa_agent.py,sha256=DBn9HKloILqJSTSdNZzH_PYWT0B2h9VwmY6akFQI_uU,2913
162
- code_puppy-0.0.319.data/data/code_puppy/models.json,sha256=mTpmJH0UJlmX8M2KVPbxMWb99de3IxKXCWO-B23b6xo,3101
163
- code_puppy-0.0.319.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
164
- code_puppy-0.0.319.dist-info/METADATA,sha256=XgkIpN2_glvNl1BcGksFRRikU_PCKdR-eQkDAIkr1ms,28030
165
- code_puppy-0.0.319.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
166
- code_puppy-0.0.319.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
167
- code_puppy-0.0.319.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
168
- code_puppy-0.0.319.dist-info/RECORD,,
162
+ code_puppy-0.0.320.data/data/code_puppy/models.json,sha256=mTpmJH0UJlmX8M2KVPbxMWb99de3IxKXCWO-B23b6xo,3101
163
+ code_puppy-0.0.320.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
164
+ code_puppy-0.0.320.dist-info/METADATA,sha256=Ah_-U-dxX8DnEHjQ0G8--g_1lDfyI3WMGcjnJOEfiBk,28030
165
+ code_puppy-0.0.320.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
166
+ code_puppy-0.0.320.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
167
+ code_puppy-0.0.320.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
168
+ code_puppy-0.0.320.dist-info/RECORD,,