OdinHoang03 commited on
Commit
9548d3d
·
0 Parent(s):

Initial setup: starter code app with AI logging hooks

Browse files
Files changed (4) hide show
  1. __init__.py +0 -0
  2. agent.py +112 -0
  3. config.py +9 -0
  4. tools.py +76 -0
__init__.py ADDED
File without changes
agent.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Basic agent loop using the Anthropic Claude API.
3
+ Receives user input, calls tools as needed, and returns results.
4
+ """
5
+
6
+ import logging
7
+ from anthropic import Anthropic
8
+ from .config import ANTHROPIC_API_KEY, DEFAULT_MODEL, LOG_LEVEL
9
+ from .tools import get_tool_schemas, execute_tool
10
+
11
+ logging.basicConfig(level=LOG_LEVEL, format="%(asctime)s [%(levelname)s] %(message)s")
12
+ logger = logging.getLogger(__name__)
13
+
14
+ SYSTEM_PROMPT = """You are an intelligent AI assistant.
15
+ You can use the provided tools to complete tasks.
16
+ Think step by step and use tools when necessary."""
17
+
18
+
19
+ def create_agent():
20
+ """Create an agent with the Anthropic client."""
21
+ if not ANTHROPIC_API_KEY:
22
+ raise ValueError("ANTHROPIC_API_KEY is not configured. Check your .env file")
23
+ return Anthropic(api_key=ANTHROPIC_API_KEY)
24
+
25
+
26
+ def run_agent_loop(client: Anthropic, user_input: str, max_turns: int = 10) -> str:
27
+ """
28
+ Run the agent loop: send message -> receive response -> call tool -> repeat.
29
+
30
+ Args:
31
+ client: Anthropic client
32
+ user_input: User's question or request
33
+ max_turns: Maximum number of tool-calling turns
34
+
35
+ Returns:
36
+ The agent's final response
37
+ """
38
+ messages = [{"role": "user", "content": user_input}]
39
+ tools = get_tool_schemas()
40
+
41
+ for turn in range(max_turns):
42
+ logger.info(f"Turn {turn + 1}/{max_turns}")
43
+
44
+ response = client.messages.create(
45
+ model=DEFAULT_MODEL,
46
+ max_tokens=4096,
47
+ system=SYSTEM_PROMPT,
48
+ tools=tools,
49
+ messages=messages,
50
+ )
51
+
52
+ # If agent stops (no more tool calls)
53
+ if response.stop_reason == "end_turn":
54
+ final_text = ""
55
+ for block in response.content:
56
+ if hasattr(block, "text"):
57
+ final_text += block.text
58
+ return final_text
59
+
60
+ # Handle tool calls
61
+ tool_results = []
62
+ has_tool_use = False
63
+
64
+ for block in response.content:
65
+ if block.type == "tool_use":
66
+ has_tool_use = True
67
+ logger.info(f"Calling tool: {block.name}({block.input})")
68
+ result = execute_tool(block.name, block.input)
69
+ logger.info(f"Result: {result[:200]}")
70
+ tool_results.append({
71
+ "type": "tool_result",
72
+ "tool_use_id": block.id,
73
+ "content": result,
74
+ })
75
+
76
+ if not has_tool_use:
77
+ # No tool calls, return text
78
+ final_text = ""
79
+ for block in response.content:
80
+ if hasattr(block, "text"):
81
+ final_text += block.text
82
+ return final_text
83
+
84
+ # Add assistant response and tool results to messages
85
+ messages.append({"role": "assistant", "content": response.content})
86
+ messages.append({"role": "user", "content": tool_results})
87
+
88
+ return "Agent reached the maximum number of processing turns."
89
+
90
+
91
+ def main():
92
+ """Interactive loop - enter a prompt and receive results."""
93
+ client = create_agent()
94
+ print("Agentic App (type 'quit' to exit)")
95
+ print("-" * 50)
96
+
97
+ while True:
98
+ user_input = input("\nYou: ").strip()
99
+ if not user_input or user_input.lower() in ("quit", "exit", "q"):
100
+ print("Bye!")
101
+ break
102
+
103
+ try:
104
+ response = run_agent_loop(client, user_input)
105
+ print(f"\nAgent: {response}")
106
+ except Exception as e:
107
+ logger.error(f"Error: {e}")
108
+ print(f"\nError: {e}")
109
+
110
+
111
+ if __name__ == "__main__":
112
+ main()
config.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ load_dotenv()
5
+
6
+ ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "")
7
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
8
+ DEFAULT_MODEL = os.getenv("DEFAULT_MODEL", "claude-sonnet-4-20250514")
9
+ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
tools.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tool definitions for the agent.
3
+ Add new tools by creating a function and registering it in the TOOLS dict.
4
+ """
5
+
6
+ import httpx
7
+
8
+
9
+ def search_web(query: str) -> str:
10
+ """Search for information on the web (placeholder)."""
11
+ return f"Search results for: {query}"
12
+
13
+
14
+ def calculate(expression: str) -> str:
15
+ """Evaluate a math expression."""
16
+ try:
17
+ result = eval(expression, {"__builtins__": {}})
18
+ return str(result)
19
+ except Exception as e:
20
+ return f"Error: {e}"
21
+
22
+
23
+ def fetch_url(url: str) -> str:
24
+ """Fetch content from a URL."""
25
+ try:
26
+ resp = httpx.get(url, timeout=10, follow_redirects=True)
27
+ return resp.text[:2000]
28
+ except Exception as e:
29
+ return f"Error: {e}"
30
+
31
+
32
+ # Tool registry - the agent uses this dict
33
+ TOOLS = {
34
+ "search_web": {
35
+ "fn": search_web,
36
+ "description": "Search for information on the web",
37
+ "parameters": {"query": "string"},
38
+ },
39
+ "calculate": {
40
+ "fn": calculate,
41
+ "description": "Evaluate a math expression",
42
+ "parameters": {"expression": "string"},
43
+ },
44
+ "fetch_url": {
45
+ "fn": fetch_url,
46
+ "description": "Fetch content from a URL",
47
+ "parameters": {"url": "string"},
48
+ },
49
+ }
50
+
51
+
52
+ def get_tool_schemas() -> list[dict]:
53
+ """Return tool schemas in Anthropic API format."""
54
+ schemas = []
55
+ for name, tool in TOOLS.items():
56
+ schemas.append({
57
+ "name": name,
58
+ "description": tool["description"],
59
+ "input_schema": {
60
+ "type": "object",
61
+ "properties": {
62
+ k: {"type": v, "description": k}
63
+ for k, v in tool["parameters"].items()
64
+ },
65
+ "required": list(tool["parameters"].keys()),
66
+ },
67
+ })
68
+ return schemas
69
+
70
+
71
+ def execute_tool(name: str, args: dict) -> str:
72
+ """Execute a tool by name."""
73
+ tool = TOOLS.get(name)
74
+ if not tool:
75
+ return f"Tool '{name}' does not exist"
76
+ return tool["fn"](**args)