| from textwrap import dedent |
| from typing import Any, Dict, List, Optional |
|
|
| from smolagents.tools import Tool |
|
|
|
|
| |
| |
| |
| class NextAction: |
| CONTINUE = "continue" |
| VALIDATE = "validate" |
| FINAL_ANSWER = "final_answer" |
|
|
|
|
| |
| |
| |
| class ThinkTool(Tool): |
| name = "think" |
| description = ( |
| "Internal scratch‑pad. Use this to reason step‑by‑step before " |
| "calling other tools or replying to the user." |
| ) |
| inputs = { |
| "title": {"type": "string", "description": "Concise title"}, |
| "thought": {"type": "string", "description": "Detailed reasoning"}, |
| "action": { |
| "type": "string", |
| "description": "Intended next action", |
| "nullable": True, |
| }, |
| "confidence": { |
| "type": "number", |
| "description": "Confidence 0–1", |
| "nullable": True, |
| }, |
| "run_id": { |
| "type": "string", |
| "description": "Execution identifier", |
| "nullable": True, |
| }, |
| } |
| output_type = "string" |
|
|
| def __init__(self): |
| super().__init__() |
| self._history: Dict[str, List[Dict[str, Any]]] = {} |
|
|
| def forward( |
| self, |
| title: str, |
| thought: str, |
| action: Optional[str] = None, |
| confidence: float = 0.8, |
| run_id: str = "default", |
| ) -> str: |
| """Store and pretty‑print reasoning history.""" |
| step = { |
| "title": title, |
| "reasoning": thought, |
| "action": action, |
| "confidence": confidence, |
| } |
| self._history.setdefault(run_id, []).append(step) |
|
|
| |
| formatted = "" |
| for idx, s in enumerate(self._history[run_id], 1): |
| formatted += ( |
| dedent( |
| f"""\ |
| Step {idx}: |
| Title: {s["title"]} |
| Reasoning: {s["reasoning"]} |
| Action: {s["action"]} |
| Confidence: {s["confidence"]} |
| """ |
| ) |
| + "\n" |
| ) |
| return formatted.strip() |
|
|
|
|
| |
| |
| |
| class AnalyzeTool(Tool): |
| name = "analyze" |
| description = ( |
| "Evaluate the result of previous actions and decide whether to " |
| "continue, validate, or provide a final answer. " |
| ) |
| inputs = { |
| "title": {"type": "string", "description": "Concise title"}, |
| "result": {"type": "string", "description": "Outcome being analysed"}, |
| "analysis": {"type": "string", "description": "Your analysis"}, |
| "next_action": { |
| "type": "string", |
| "description": "'continue' | 'validate' | 'final_answer'", |
| "nullable": True, |
| }, |
| "confidence": { |
| "type": "number", |
| "description": "Confidence 0–1", |
| "nullable": True, |
| }, |
| "run_id": { |
| "type": "string", |
| "description": "Execution identifier", |
| "nullable": True, |
| }, |
| } |
| output_type = "string" |
|
|
| def __init__(self): |
| super().__init__() |
| self._history: Dict[str, List[Dict[str, Any]]] = {} |
|
|
| def forward( |
| self, |
| title: str, |
| result: str, |
| analysis: str, |
| next_action: str = NextAction.CONTINUE, |
| confidence: float = 0.8, |
| run_id: str = "default", |
| ) -> str: |
| if next_action not in { |
| NextAction.CONTINUE, |
| NextAction.VALIDATE, |
| NextAction.FINAL_ANSWER, |
| }: |
| raise ValueError( |
| f"next_action must be one of " |
| f"{NextAction.CONTINUE}, {NextAction.VALIDATE}, " |
| f"{NextAction.FINAL_ANSWER}" |
| ) |
|
|
| step = { |
| "title": title, |
| "result": result, |
| "reasoning": analysis, |
| "next_action": next_action, |
| "confidence": confidence, |
| } |
| self._history.setdefault(run_id, []).append(step) |
|
|
| formatted = "" |
| for idx, s in enumerate(self._history[run_id], 1): |
| formatted += ( |
| dedent( |
| f"""\ |
| Step {idx}: |
| Title: {s["title"]} |
| Result: {s.get("result")} |
| Reasoning: {s["reasoning"]} |
| Next action: {s.get("next_action")} |
| Confidence: {s["confidence"]} |
| """ |
| ) |
| + "\n" |
| ) |
| return formatted.strip() |
|
|
|
|
| |
| |
| |
| class ReasoningToolkit: |
| """ |
| Convenience wrapper so you can write: |
| |
| from reasoning_tools import ReasoningToolkit |
| toolkit = ReasoningToolkit() |
| agent = CodeAgent(tools=toolkit.tools, model=...) |
| """ |
|
|
| DEFAULT_INSTRUCTIONS = dedent( |
| """\ |
| You have access to two internal tools – **think** and **analyze** – |
| for chain‑of‑thought reasoning. **Always** call `think` before |
| external tool calls or final answers, then call `analyze` to |
| decide whether to continue, validate, or finish.""" |
| ) |
|
|
| def __init__(self, think: bool = True, analyze: bool = True): |
| self.tools: List[Tool] = [] |
| if think: |
| self.tools.append(ThinkTool()) |
| if analyze: |
| self.tools.append(AnalyzeTool()) |
|
|
| def with_instructions(self, extra: str | None = None) -> str: |
| return self.DEFAULT_INSTRUCTIONS + ("\n" + extra if extra else "") |
|
|