diff --git a/runbook/02_agent_class.py b/runbook/02_agent_class.py index c4e9122..9fc13d7 100644 --- a/runbook/02_agent_class.py +++ b/runbook/02_agent_class.py @@ -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 @@ -21,16 +27,16 @@ 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) @@ -38,4 +44,5 @@ def __init__(self, api_key: str): # export ANTHROPIC_API_KEY="your-api-key-here" # uv run runbook/02_agent_class.py # ``` -# Should print: Agent initialized +# Should print: Agent initializedcls + diff --git a/runbook/03_define_tools.py b/runbook/03_define_tools.py index cf8cdb0..58790d9 100644 --- a/runbook/03_define_tools.py +++ b/runbook/03_define_tools.py @@ -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 @@ -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() @@ -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) diff --git a/runbook/04_implement_tool_execution.py b/runbook/04_implement_tool_execution.py index 6e2c5c3..b534b44 100644 --- a/runbook/04_implement_tool_execution.py +++ b/runbook/04_implement_tool_execution.py @@ -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 @@ -19,9 +25,9 @@ class Tool(BaseModel): 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() @@ -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: diff --git a/runbook/05_add_chat_method.py b/runbook/05_add_chat_method.py index 4e602e7..818f68d 100644 --- a/runbook/05_add_chat_method.py +++ b/runbook/05_add_chat_method.py @@ -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 @@ -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() @@ -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 diff --git a/runbook/06_create_interactive_cli.py b/runbook/06_create_interactive_cli.py index 3bb4d56..5594975 100644 --- a/runbook/06_create_interactive_cli.py +++ b/runbook/06_create_interactive_cli.py @@ -2,18 +2,24 @@ # 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 -import argparse # NEW +import argparse import logging 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() + # Set up logging logging.basicConfig( level=logging.INFO, @@ -34,7 +40,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() @@ -169,79 +175,115 @@ 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) def main(): parser = argparse.ArgumentParser( description="AI Code Assistant - A conversational AI agent with file editing capabilities" ) parser.add_argument( - "--api-key", help="Anthropic API key (or set ANTHROPIC_API_KEY env var)" + "--api-key", help="Anthropic API key (or set GEMINI_API_KEY env var)" ) args = parser.parse_args() - api_key = args.api_key or os.environ.get("ANTHROPIC_API_KEY") + api_key = args.api_key or os.environ.get("GEMINI_API_KEY") if not api_key: print( - "Error: Please provide an API key via --api-key or ANTHROPIC_API_KEY environment variable" + "Error: Please provide an API key via --api-key or GEMINI_API_KEY environment variable" ) sys.exit(1) @@ -282,7 +324,7 @@ def main(): # ```bash -# export ANTHROPIC_API_KEY="your-api-key-here" +# export GEMINI_API_KEY="your-api-key-here" # uv run runbook/06_create_interactive_cli.py # ``` # Should print: diff --git a/runbook/07_add_personality.py b/runbook/07_add_personality.py index df505eb..59df9ee 100644 --- a/runbook/07_add_personality.py +++ b/runbook/07_add_personality.py @@ -2,18 +2,23 @@ # 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 import argparse import logging 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() # Set up logging logging.basicConfig( level=logging.INFO, @@ -34,7 +39,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() @@ -235,14 +240,14 @@ def main(): description="AI Code Assistant - A conversational AI agent with file editing capabilities" ) parser.add_argument( - "--api-key", help="Anthropic API key (or set ANTHROPIC_API_KEY env var)" + "--api-key", help="Anthropic API key (or set GEMINI_API_KEY env var)" ) args = parser.parse_args() - api_key = args.api_key or os.environ.get("ANTHROPIC_API_KEY") + api_key = args.api_key or os.environ.get("GEMINI_API_KEY") if not api_key: print( - "Error: Please provide an API key via --api-key or ANTHROPIC_API_KEY environment variable" + "Error: Please provide an API key via --api-key or GEMINI_API_KEY environment variable" ) sys.exit(1) @@ -283,7 +288,7 @@ def main(): # ```bash -# export ANTHROPIC_API_KEY="your-api +# export GEMINI_API_KEY="your-api # uv run runbook/07_add_personality.py # ``` # AI Code Assistant