File size: 6,410 Bytes
652eb00 |
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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
from textwrap import dedent
from typing import Any, Dict, List, Optional
from smolagents.tools import Tool # SmolAgents base class
# ---------------------------------------------------------------------
# Helper enum – kept as str literals so we avoid any Agno dependency.
# ---------------------------------------------------------------------
class NextAction:
CONTINUE = "continue"
VALIDATE = "validate"
FINAL_ANSWER = "final_answer"
# ---------------------------------------------------------------------
# THINK TOOL -----------------------------------------------------------
# ---------------------------------------------------------------------
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( # noqa: N802 (SmolAgents allows camelCase here)
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)
# Pretty print full chain so the LLM can “see” prior steps
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()
# ---------------------------------------------------------------------
# ANALYZE TOOL ---------------------------------------------------------
# ---------------------------------------------------------------------
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()
# ---------------------------------------------------------------------
# TOOLKIT WRAPPER ------------------------------------------------------
# ---------------------------------------------------------------------
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 "")
|