Spaces:
Sleeping
Sleeping
File size: 3,531 Bytes
52a7b5c cdca445 52a7b5c cdca445 7e34bee cdca445 a6d1b6d 7e34bee cdca445 7e34bee cdca445 7e34bee cdca445 7e34bee cdca445 a6d1b6d 7e34bee cdca445 7e34bee cdca445 7e34bee a6d1b6d cdca445 a6d1b6d 7e34bee a6d1b6d 7e34bee 52a7b5c 7e34bee 52a7b5c 7e34bee 52a7b5c 7e34bee 52a7b5c 7e34bee 52a7b5c 7e34bee 52a7b5c 7e34bee 52a7b5c 7e34bee cdca445 7e34bee 52a7b5c 7e34bee cdca445 52a7b5c 7e34bee |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
import asyncio
import io
import json
import re
import threading
from agent_server.sanitizing_think_tags import scrub_think_tags
class QueueWriter(io.TextIOBase):
"""
File-like object that pushes each write to an asyncio.Queue immediately.
"""
def __init__(self, q: "asyncio.Queue"):
self.q = q
self._lock = threading.Lock()
self._buf = [] # accumulate until newline to reduce spam
def write(self, s: str):
if not s:
return 0
with self._lock:
self._buf.append(s)
# flush on newline to keep granularity reasonable
if "\n" in s:
chunk = "".join(self._buf)
self._buf.clear()
try:
self.q.put_nowait({"__stdout__": chunk})
except Exception:
pass
return len(s)
def flush(self):
with self._lock:
if self._buf:
chunk = "".join(self._buf)
self._buf.clear()
try:
self.q.put_nowait({"__stdout__": chunk})
except Exception:
pass
def _serialize_step(step) -> str:
"""
Best-effort pretty string for a smolagents MemoryStep / ActionStep.
Works even if attributes are missing on some versions.
"""
parts = []
sn = getattr(step, "step_number", None)
if sn is not None:
parts.append(f"Step {sn}")
thought_val = getattr(step, "thought", None)
if thought_val:
parts.append(f"Thought: {scrub_think_tags(str(thought_val))}")
tool_val = getattr(step, "tool", None)
if tool_val:
parts.append(f"Tool: {scrub_think_tags(str(tool_val))}")
code_val = getattr(step, "code", None)
if code_val:
code_str = scrub_think_tags(str(code_val)).strip()
parts.append("```python\n" + code_str + "\n```")
args = getattr(step, "args", None)
if args:
try:
parts.append(
"Args: " + scrub_think_tags(json.dumps(args, ensure_ascii=False))
)
except Exception:
parts.append("Args: " + scrub_think_tags(str(args)))
error = getattr(step, "error", None)
if error:
parts.append(f"Error: {scrub_think_tags(str(error))}")
obs = getattr(step, "observations", None)
if obs is not None:
if isinstance(obs, (list, tuple)):
obs_str = "\n".join(map(str, obs))
else:
obs_str = str(obs)
parts.append("Observation:\n" + scrub_think_tags(obs_str).strip())
# If this looks like a FinalAnswer step object, surface a clean final answer
try:
tname = type(step).__name__
except Exception:
tname = ""
if tname.lower().startswith("finalanswer"):
out = getattr(step, "output", None)
if out is not None:
return f"Final answer: {scrub_think_tags(str(out)).strip()}"
# Fallback: try to parse from string repr "FinalAnswerStep(output=...)"
s = scrub_think_tags(str(step))
m = re.search(r"FinalAnswer[^()]*\(\s*output\s*=\s*([^,)]+)", s)
if m:
return f"Final answer: {m.group(1).strip()}"
# If the only content would be an object repr like FinalAnswerStep(...), drop it;
# a cleaner "Final answer: ..." will come from the rule above or stdout.
joined = "\n".join(parts).strip()
if re.match(r"^FinalAnswer[^\n]+\)$", joined):
return ""
return joined or scrub_think_tags(str(step))
|