"""agent_manual.py, M9a: tool calling from FIRST PRINCIPLES (the manual loop).

An agent is a model that can *take actions* by calling tools you give it. The loop:
  1. You describe your tools (name + description + input schema) and send the question.
  2. The model either answers, OR asks to use a tool (a "tool_use" block).
  3. YOU run the real function and send the result back.
  4. Repeat until the model stops asking for tools.
No framework here, just the Messages API, so you can see exactly how it works.

Run (venv active, key in .env, from this folder):
    python agent_manual.py
"""

import os
from dotenv import load_dotenv
import anthropic
from tools import calculate, count_characters

load_dotenv()
client = anthropic.Anthropic()
MODEL = "claude-opus-4-8"

# 1. Tell the model what tools exist (this is the "schema" the model reads).
TOOLS = [
    {"name": "calculate",
     "description": "Evaluate a basic arithmetic expression like '3 * (4 + 5)'.",
     "input_schema": {"type": "object",
                      "properties": {"expression": {"type": "string"}},
                      "required": ["expression"]}},
    {"name": "count_characters",
     "description": "Count the number of characters in a piece of text.",
     "input_schema": {"type": "object",
                      "properties": {"text": {"type": "string"}},
                      "required": ["text"]}},
]

# Map each tool name to the REAL Python function that does the work.
TOOL_FUNCS = {"calculate": calculate, "count_characters": count_characters}


def agent(user_message):
    """Run the tool-use loop until the model gives a final answer."""
    messages = [{"role": "user", "content": user_message}]
    while True:
        response = client.messages.create(
            model=MODEL, max_tokens=1000, tools=TOOLS, messages=messages,
        )

        # The model is done, return its text answer.
        if response.stop_reason != "tool_use":
            return next((b.text for b in response.content if b.type == "text"), "")

        # The model asked to use one or more tools. Keep its turn (incl. tool_use blocks).
        messages.append({"role": "assistant", "content": response.content})

        # Run each requested tool and collect the results.
        results = []
        for block in response.content:
            if block.type == "tool_use":
                output = TOOL_FUNCS[block.name](**block.input)   # YOU run the real function
                print(f"  [tool] {block.name}({block.input}) -> {output}")
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,        # must match the tool_use block's id
                    "content": str(output),
                })

        # Send the tool results back so the model can continue.
        messages.append({"role": "user", "content": results})


if __name__ == "__main__":
    print("I'm an agent with two tools: calculate and count_characters.")
    print("Try: 'What is 18% of 245, then how many characters in the answer?'  (type 'quit')\n")
    while True:
        q = input("You: ")
        if q.strip().lower() in {"quit", "exit"}:
            break
        print("Agent:", agent(q), "\n")
