shenmali's picture
Upload folder using huggingface_hub
53d41ca verified
Raw
History Blame Contribute Delete
3.28 kB
import json
import time
from collections.abc import Iterator
from _core.llm import LLMClient
from _core.models import estimate_cost
from _core.tools import ToolRegistry
from _core.tracer import Step
SYSTEM_PROMPT = (
"You are a ReAct agent. Reason step by step about the user's question. "
"Use a tool when you need external information or computation. "
"When you can answer directly, respond with the final answer and do NOT call a tool."
)
class ReActAgent:
def __init__(self, llm: LLMClient, tools: ToolRegistry, max_steps: int = 6):
self.llm = llm
self.tools = tools
self.max_steps = max_steps
def run(self, question: str) -> Iterator[Step]:
messages: list[dict] = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": question},
]
for _ in range(self.max_steps):
start = time.monotonic()
resp = self.llm.chat(messages, tools=self.tools.to_openai_schema())
latency = int((time.monotonic() - start) * 1000)
cost = estimate_cost(self.llm.model, resp.prompt_tokens, resp.completion_tokens)
step_tokens = resp.prompt_tokens + resp.completion_tokens
if resp.tool_calls:
messages.append(
{
"role": "assistant",
"content": resp.content or "",
"tool_calls": [
{
"id": tc["id"],
"type": "function",
"function": {"name": tc["name"], "arguments": tc["arguments"]},
}
for tc in resp.tool_calls
],
}
)
if resp.content:
yield Step(
kind="thought",
content=resp.content,
tokens=step_tokens,
cost_usd=cost,
latency_ms=latency,
)
for tc in resp.tool_calls:
yield Step(kind="action", content=f"{tc['name']}({tc['arguments']})")
try:
args = json.loads(tc["arguments"]) if tc["arguments"] else {}
except json.JSONDecodeError as e:
# Thread the error back as an observation so the model can
# recover, rather than aborting the whole run.
result = f"Error: invalid tool arguments (not valid JSON): {e}"
else:
result = self.tools.execute(tc["name"], args)
yield Step(kind="observation", content=result)
messages.append({"role": "tool", "tool_call_id": tc["id"], "content": result})
continue
yield Step(
kind="final",
content=resp.content or "",
tokens=step_tokens,
cost_usd=cost,
latency_ms=latency,
)
return
yield Step(kind="final", content="Reached max steps without a final answer.")