Alex J. Chan commited on
Commit
dda3a8b
Β·
2 Parent(s): 51f6814 187b3a2

Merge pull request #15 from convergence-ai/alex/stream

Browse files
src/proxy_lite/cli.py CHANGED
@@ -3,7 +3,6 @@ import asyncio
3
  import base64
4
  import os
5
  from pathlib import Path
6
- from typing import Optional
7
 
8
  from proxy_lite import Runner, RunnerConfig
9
  from proxy_lite.gif_maker import create_run_gif
 
3
  import base64
4
  import os
5
  from pathlib import Path
 
6
 
7
  from proxy_lite import Runner, RunnerConfig
8
  from proxy_lite.gif_maker import create_run_gif
src/proxy_lite/logger.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import logging
2
  import sys
3
  from typing import Literal
@@ -7,6 +8,18 @@ from rich.logging import RichHandler
7
 
8
 
9
  class StructuredLogger(logging.Logger):
 
 
 
 
 
 
 
 
 
 
 
 
10
  def _log(
11
  self,
12
  level,
@@ -31,6 +44,7 @@ class StructuredLogger(logging.Logger):
31
  json_fields["exception_message"] = str(exc_value)
32
 
33
  json_fields.update(extra)
 
34
  super()._log(
35
  level,
36
  msg,
@@ -50,32 +64,29 @@ def create_logger(
50
  unique_name = f"{name}-{str(uuid4())[:8]}"
51
  logger = logging.getLogger(unique_name)
52
  logger.setLevel(level)
53
- handler = RichHandler(
 
 
54
  rich_tracebacks=True,
55
  markup=True,
56
  show_path=False,
57
  show_time=False,
58
  log_time_format="[%s]",
59
  )
 
60
  if detailed_name:
61
- handler.setFormatter(logging.Formatter("%(name)s:\n%(message)s\n------"))
62
  else:
63
- handler.setFormatter(logging.Formatter("%(message)s\n------"))
64
- logger.addHandler(handler)
 
65
  logger.propagate = False
 
66
  return logger
67
 
68
 
69
  # Set StructuredLogger as the default logger class
70
  logging.setLoggerClass(StructuredLogger)
71
 
72
- logger = logging.getLogger(__name__)
73
- logger.setLevel(logging.INFO)
74
- logger.propagate = True
75
- handler = RichHandler(
76
- rich_tracebacks=True,
77
- markup=True,
78
- show_path=False,
79
- show_time=False,
80
- )
81
- logger.addHandler(handler)
 
1
+ import asyncio
2
  import logging
3
  import sys
4
  from typing import Literal
 
8
 
9
 
10
  class StructuredLogger(logging.Logger):
11
+ async def stream_message(self, message: str) -> None:
12
+ """Streams the message character by character asynchronously."""
13
+ try:
14
+ sys.stdout.write("\r") # Overwrite current line
15
+ for char in message:
16
+ sys.stdout.write(char)
17
+ sys.stdout.flush()
18
+ await asyncio.sleep(0.002)
19
+ sys.stdout.write("\n")
20
+ except Exception:
21
+ pass
22
+
23
  def _log(
24
  self,
25
  level,
 
44
  json_fields["exception_message"] = str(exc_value)
45
 
46
  json_fields.update(extra)
47
+
48
  super()._log(
49
  level,
50
  msg,
 
64
  unique_name = f"{name}-{str(uuid4())[:8]}"
65
  logger = logging.getLogger(unique_name)
66
  logger.setLevel(level)
67
+
68
+ # Standard RichHandler for structured logs
69
+ rich_handler = RichHandler(
70
  rich_tracebacks=True,
71
  markup=True,
72
  show_path=False,
73
  show_time=False,
74
  log_time_format="[%s]",
75
  )
76
+
77
  if detailed_name:
78
+ rich_handler.setFormatter(logging.Formatter("%(name)s:\n%(message)s"))
79
  else:
80
+ rich_handler.setFormatter(logging.Formatter("-----\n%(message)s"))
81
+
82
+ logger.addHandler(rich_handler)
83
  logger.propagate = False
84
+
85
  return logger
86
 
87
 
88
  # Set StructuredLogger as the default logger class
89
  logging.setLoggerClass(StructuredLogger)
90
 
91
+ # Initialize logger
92
+ logger = create_logger(__name__, level="INFO")
 
 
 
 
 
 
 
 
src/proxy_lite/solvers/simple_solver.py CHANGED
@@ -81,13 +81,15 @@ class SimpleSolver(BaseSolver):
81
  observation_match = re.search(r"<observation>(.*?)</observation>", text_content, re.DOTALL)
82
  observation_content = observation_match.group(1).strip() if observation_match else ""
83
 
84
- self.logger.info(f"🌐 [bold blue]Observation:[/] {observation_content}")
 
85
 
86
  # Extract text between thinking tags if present
87
  thinking_match = re.search(r"<thinking>(.*?)</thinking>", text_content, re.DOTALL)
88
  thinking_content = thinking_match.group(1).strip() if thinking_match else text_content
89
 
90
- self.logger.info(f"πŸ€– [bold purple]Action:[/] {thinking_content}")
 
91
 
92
  return Action(tool_calls=message.tool_calls, text=text_content)
93
 
 
81
  observation_match = re.search(r"<observation>(.*?)</observation>", text_content, re.DOTALL)
82
  observation_content = observation_match.group(1).strip() if observation_match else ""
83
 
84
+ self.logger.info("🌐 [bold blue]Observation:[/]")
85
+ await self.logger.stream_message(observation_content)
86
 
87
  # Extract text between thinking tags if present
88
  thinking_match = re.search(r"<thinking>(.*?)</thinking>", text_content, re.DOTALL)
89
  thinking_content = thinking_match.group(1).strip() if thinking_match else text_content
90
 
91
+ self.logger.info("🧠 [bold purple]Thinking:[/]")
92
+ await self.logger.stream_message(thinking_content)
93
 
94
  return Action(tool_calls=message.tool_calls, text=text_content)
95