Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions runbook/02_agent_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@
# requires-python = ">=3.12"
# dependencies = [
# "anthropic", # type: ignore
# "dotenv>=0.9.9",
# "google-genai>=1.74.0",
# "pydantic",
# ]
# ///

from dotenv import load_dotenv
import os
import sys
from typing import List, Dict, Any
from anthropic import Anthropic
from google import genai
# from google.genai import types
from pydantic import BaseModel

load_dotenv()


class Tool(BaseModel):
name: str
Expand All @@ -21,21 +27,22 @@ class Tool(BaseModel):

class AIAgent:
def __init__(self, api_key: str):
self.client = Anthropic(api_key=api_key)
self.client = genai.Client(api_key=api_key)
self.messages: List[Dict[str, Any]] = []
self.tools: List[Tool] = []
print("Agent initialized")


if __name__ == "__main__":
api_key = os.environ.get("ANTHROPIC_API_KEY")
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
print("Error: ANTHROPIC_API_KEY not set")
print("Error: GEMINI_API_KEY not set")
sys.exit(1)
agent = AIAgent(api_key)

# ```bash
# export ANTHROPIC_API_KEY="your-api-key-here"
# uv run runbook/02_agent_class.py
# ```
# Should print: Agent initialized
# Should print: Agent initializedcls

14 changes: 10 additions & 4 deletions runbook/03_define_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@
# requires-python = ">=3.12"
# dependencies = [
# "anthropic", # type: ignore
# "dotenv>=0.9.9",
# "google-genai>=1.74.0",
# "pydantic",
# ]
# ///

from dotenv import load_dotenv
import os
import sys
from typing import List, Dict, Any
from anthropic import Anthropic
from google import genai
# from google.genai import types
from pydantic import BaseModel

load_dotenv()


class Tool(BaseModel):
name: str
Expand All @@ -21,7 +27,7 @@ class Tool(BaseModel):

class AIAgent:
def __init__(self, api_key: str):
self.client = Anthropic(api_key=api_key)
self.client = genai.Client(api_key=api_key)
self.messages: List[Dict[str, Any]] = []
self.tools: List[Tool] = []
self._setup_tools()
Expand Down Expand Up @@ -83,9 +89,9 @@ def _setup_tools(self):


if __name__ == "__main__":
api_key = os.environ.get("ANTHROPIC_API_KEY")
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
print("Error: ANTHROPIC_API_KEY not set")
print("Error: GEMINI_API_KEY not set")
sys.exit(1)
agent = AIAgent(api_key)

Expand Down
23 changes: 15 additions & 8 deletions runbook/04_implement_tool_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@
# requires-python = ">=3.12"
# dependencies = [
# "anthropic", # type: ignore
# "dotenv>=0.9.9",
# "google-genai>=1.74.0",
# "pydantic",
# ]
# ///

from dotenv import load_dotenv
import os
import sys
from typing import List, Dict, Any
from anthropic import Anthropic
from google import genai
# from google.genai import types
from pydantic import BaseModel

load_dotenv()


class Tool(BaseModel):
name: str
description: str
input_schema: Dict[str, Any]


class AIAgent:
class AiAgent:
def __init__(self, api_key: str):
self.client = Anthropic(api_key=api_key)
self.client = genai.Client(api_key=api_key)
self.messages: List[Dict[str, Any]] = []
self.tools: List[Tool] = []
self._setup_tools()
Expand Down Expand Up @@ -158,16 +164,17 @@ def _edit_file(self, path: str, old_text: str, new_text: str) -> str:


if __name__ == "__main__":
api_key = os.environ.get("ANTHROPIC_API_KEY")
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
print("Error: ANTHROPIC_API_KEY not set")
print("Error: GEMINI_API_KEY not set")
sys.exit(1)
agent = AIAgent(api_key)
agent = AiAgent(api_key)
# Test the tools
print(agent._list_files("."))
""" print(agent._list_files(".")) """
print(agent._read_file("./01_basic_script.py"))

# ```bash
# export ANTHROPIC_API_KEY="your-api
# export GEMINI_API_KEY="your-api
# uv run runbook/04_implement_tool_execution.py
# ```
# Should print:
Expand Down
147 changes: 90 additions & 57 deletions runbook/05_add_chat_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@
# requires-python = ">=3.12"
# dependencies = [
# "anthropic", # type: ignore
# "dotenv>=0.9.9",
# "google-genai>=1.74.0",
# "pydantic",
# ]
# ///

from dotenv import load_dotenv
import os
import sys
from typing import List, Dict, Any
from anthropic import Anthropic
from google import genai
from google.genai import types
from pydantic import BaseModel

load_dotenv()


class Tool(BaseModel):
name: str
Expand All @@ -21,7 +27,7 @@ class Tool(BaseModel):

class AIAgent:
def __init__(self, api_key: str):
self.client = Anthropic(api_key=api_key)
self.client = genai.Client(api_key=api_key)
self.messages: List[Dict[str, Any]] = []
self.tools: List[Tool] = []
self._setup_tools()
Expand Down Expand Up @@ -156,82 +162,109 @@ def _edit_file(self, path: str, old_text: str, new_text: str) -> str:
return f"Error editing file: {str(e)}"

def chat(self, user_input: str) -> str:
self.messages.append({"role": "user", "content": user_input})

tool_schemas = [
{
"name": tool.name,
"description": tool.description,
"input_schema": tool.input_schema,
}
for tool in self.tools
# 1. Agregar mensaje del usuario al estilo Google
self.messages.append(types.Content(role="user", parts=[types.Part.from_text(text=user_input)]))

# 2. Convertir tus herramientas al formato que Gemini entiende
gemini_tools = [
types.Tool(
function_declarations=[
types.FunctionDeclaration(
name=t.name,
description=t.description,
parameters=t.input_schema,
)
]
)
for t in self.tools
]

while True:
try:
response = self.client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
messages=self.messages,
tools=tool_schemas,
# 3. Llamada a la API
response = self.client.models.generate_content(
#model="gemini-2.0-flash", # Modelo actual recomendado
model="gemini-3-flash-preview",
contents=self.messages,
config=types.GenerateContentConfig(
tools=gemini_tools,
),
)

assistant_message = {"role": "assistant", "content": []}

for content in response.content:
if content.type == "text":
assistant_message["content"].append(
{
"type": "text",
"text": content.text,
}
)
elif content.type == "tool_use":
assistant_message["content"].append(
{
"type": "tool_use",
"id": content.id,
"name": content.name,
"input": content.input,
}
)

self.messages.append(assistant_message)

tool_results = []
for content in response.content:
if content.type == "tool_use":
result = self._execute_tool(content.name, content.input)
tool_results.append(
{
"type": "tool_result",
"tool_use_id": content.id,
"content": result,
}
# 4. Guardar la respuesta del modelo en el historial
# El SDK de Google permite añadir la respuesta directamente
self.messages.append(response.candidates[0].content)

# 5. Revisar si el modelo quiere usar una herramienta
# En Google, esto viene en 'parts' como 'function_call'
found_tool_use = False
for part in response.candidates[0].content.parts:
if part.function_call:
found_tool_use = True
tool_call = part.function_call

# Ejecutar la herramienta
print(f"RESPUESTA: Ejecutando herramienta 🔩{tool_call.name}🔩. \n")
result = self._execute_tool(tool_call.name, tool_call.args)

# 6. Añadir el RESULTADO al historial (role: tool)
self.messages.append(
types.Content(
role="tool",
parts=[
types.Part.from_function_response(
name=tool_call.name,
response={"result": result}
)
]
)
)

if tool_results:
self.messages.append({"role": "user", "content": tool_results})
else:
return response.content[0].text if response.content else ""
# Si no hubo llamadas a herramientas, retornamos el texto final
if not found_tool_use:
return response.text

except Exception as e:
return f"Error: {str(e)}"
return f"Error en el flujo de Gemini: {str(e)}"

# MÉTODOS DE AYUDA (Iguales a los tuyos)
def _execute_tool(self, tool_name: str, tool_input: Dict[str, Any]) -> str:
if tool_name == "read_file": return self._read_file(tool_input["path"])
if tool_name == "list_files": return self._list_files(tool_input.get("path", "."))
if tool_name == "edit_file":
return self._edit_file(tool_input["path"], tool_input.get("old_text", ""), tool_input["new_text"])
return f"Unknown tool: {tool_name}"

def _read_file(self, path: str) -> str:
try:
with open(path, "r", encoding="utf-8") as f: return f.read()
except Exception as e: return str(e)

def _list_files(self, path: str) -> str:
try: return str(os.listdir(path))
except Exception as e: return str(e)

def _edit_file(self, path: str, old_text: str, new_text: str) -> str:
try:
with open(path, "w", encoding="utf-8") as f: f.write(new_text)
return "Success"
except Exception as e: return str(e)


if __name__ == "__main__":
api_key = os.environ.get("ANTHROPIC_API_KEY")
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
print("Error: ANTHROPIC_API_KEY not set")
print("Error: GEMINI_API_KEY not set")
sys.exit(1)
agent = AIAgent(api_key)
# Test chat
response = agent.chat("What files are in the current directory?")
""" response = agent.chat("¿Cuales archivos hay en el directorio actual?") """
response = agent.chat("¿En qué puedes ayudarme?")
print(response)


# ```bash
# export ANTHROPIC_API_KEY="your-api
# export GEMINI_API_KEY="your-api
# uv run runbook/05_add_chat_method.py
# ```
# Should print a response from Claude listing the files in the directory
Loading