188 lines
5.7 KiB
Python
188 lines
5.7 KiB
Python
"""
|
|
Example 2: Tool Usage with Trace Capture
|
|
|
|
Demonstrates how M2.1's interleaved thinking reasons between tool calls.
|
|
This is where interleaved thinking really shines - you can see the model
|
|
adapting to tool outputs in real-time.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from dotenv import load_dotenv
|
|
|
|
from reasoning_trace_optimizer import TraceCapture
|
|
from reasoning_trace_optimizer.capture import format_trace_for_display
|
|
|
|
# Load environment variables from the project root
|
|
env_path = Path(__file__).parent.parent / ".env"
|
|
load_dotenv(env_path)
|
|
|
|
|
|
# Define mock tools
|
|
TOOLS = [
|
|
{
|
|
"name": "get_weather",
|
|
"description": "Get current weather for a location. Returns temperature and conditions.",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"location": {
|
|
"type": "string",
|
|
"description": "City name, e.g., 'San Francisco, CA'",
|
|
}
|
|
},
|
|
"required": ["location"],
|
|
},
|
|
},
|
|
{
|
|
"name": "get_forecast",
|
|
"description": "Get 3-day weather forecast for a location.",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"location": {
|
|
"type": "string",
|
|
"description": "City name",
|
|
},
|
|
"days": {
|
|
"type": "integer",
|
|
"description": "Number of days (1-3)",
|
|
"default": 3,
|
|
},
|
|
},
|
|
"required": ["location"],
|
|
},
|
|
},
|
|
]
|
|
|
|
|
|
# Mock tool executor
|
|
def execute_tool(name: str, input_data: dict) -> str:
|
|
"""Execute a mock tool and return results."""
|
|
if name == "get_weather":
|
|
location = input_data.get("location", "Unknown")
|
|
# Simulate different weather for different cities
|
|
if "san francisco" in location.lower():
|
|
return json.dumps({
|
|
"location": location,
|
|
"temperature": "18°C",
|
|
"conditions": "Foggy",
|
|
"humidity": "85%",
|
|
})
|
|
elif "new york" in location.lower():
|
|
return json.dumps({
|
|
"location": location,
|
|
"temperature": "22°C",
|
|
"conditions": "Partly cloudy",
|
|
"humidity": "60%",
|
|
})
|
|
else:
|
|
return json.dumps({
|
|
"location": location,
|
|
"temperature": "20°C",
|
|
"conditions": "Clear",
|
|
"humidity": "50%",
|
|
})
|
|
|
|
elif name == "get_forecast":
|
|
location = input_data.get("location", "Unknown")
|
|
days = input_data.get("days", 3)
|
|
forecast = []
|
|
for i in range(days):
|
|
forecast.append({
|
|
"day": i + 1,
|
|
"high": f"{20 + i * 2}°C",
|
|
"low": f"{12 + i}°C",
|
|
"conditions": ["Sunny", "Cloudy", "Rainy"][i % 3],
|
|
})
|
|
return json.dumps({
|
|
"location": location,
|
|
"forecast": forecast,
|
|
})
|
|
|
|
return json.dumps({"error": f"Unknown tool: {name}"})
|
|
|
|
|
|
def main():
|
|
"""Run a task with tools and observe interleaved thinking."""
|
|
|
|
capture = TraceCapture(
|
|
api_key=os.getenv("ANTHROPIC_API_KEY"),
|
|
base_url="https://api.minimax.io/anthropic",
|
|
model="MiniMax-M2.1",
|
|
)
|
|
|
|
task = """Compare the current weather in San Francisco and New York City.
|
|
Then tell me which city would be better for outdoor activities this weekend."""
|
|
|
|
system_prompt = """You are a helpful weather assistant.
|
|
Use the available tools to get accurate weather information.
|
|
Always provide specific data to support your recommendations."""
|
|
|
|
print("=" * 60)
|
|
print("TOOL USAGE WITH INTERLEAVED THINKING")
|
|
print("=" * 60)
|
|
print(f"\nTask: {task}")
|
|
print(f"\nTools available: {', '.join(t['name'] for t in TOOLS)}")
|
|
print("\nCapturing trace with tool usage...\n")
|
|
|
|
# Capture the trace (using non-streaming for reliability)
|
|
trace = capture.run(
|
|
task=task,
|
|
system_prompt=system_prompt,
|
|
tools=TOOLS,
|
|
tool_executor=execute_tool,
|
|
max_turns=10,
|
|
)
|
|
|
|
print("\n\n" + "=" * 60)
|
|
print("TRACE ANALYSIS")
|
|
print("=" * 60)
|
|
|
|
print(f"\nSuccess: {trace.success}")
|
|
print(f"Total Turns: {trace.total_turns}")
|
|
print(f"Thinking Blocks: {len(trace.thinking_blocks)}")
|
|
print(f"Tool Calls: {len(trace.tool_calls)}")
|
|
|
|
# Show how thinking evolved between tool calls
|
|
print("\n" + "=" * 60)
|
|
print("THINKING EVOLUTION ACROSS TOOL CALLS")
|
|
print("=" * 60)
|
|
|
|
for i, thinking in enumerate(trace.thinking_blocks):
|
|
print(f"\n[Turn {thinking.turn_index}] Thinking Block {i + 1}")
|
|
print("-" * 40)
|
|
|
|
# Show what tool was called after this thinking
|
|
turn_tools = trace.get_tool_calls_at_turn(thinking.turn_index)
|
|
if turn_tools:
|
|
print(f"Following action: Called {', '.join(t.name for t in turn_tools)}")
|
|
else:
|
|
print("Following action: Generated response")
|
|
|
|
# Show key reasoning points (first 300 chars)
|
|
print(f"\nReasoning preview:\n{thinking.content[:300]}...")
|
|
|
|
# Show tool call results
|
|
print("\n" + "=" * 60)
|
|
print("TOOL CALL SUMMARY")
|
|
print("=" * 60)
|
|
|
|
for tc in trace.tool_calls:
|
|
status = "✅" if tc.success else "❌"
|
|
print(f"\n{status} {tc.name}")
|
|
print(f" Input: {json.dumps(tc.input)}")
|
|
print(f" Result: {tc.result[:100]}..." if tc.result and len(tc.result) > 100 else f" Result: {tc.result}")
|
|
|
|
# Final response
|
|
if trace.final_response:
|
|
print("\n" + "=" * 60)
|
|
print("FINAL RESPONSE")
|
|
print("=" * 60)
|
|
print(trace.final_response)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|