File size: 8,393 Bytes
45fe4f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
"""
Dog Weight Calculator Agent - Strands Agents Version

This is a rewrite of the original Hugging Face OpenAI-based ReAct agent
using Amazon's Strands Agents SDK. The original implementation used a
manual ReAct loop with regex parsing. Strands handles all of this
automatically through its model-driven approach.

Original: Manual ReAct loop with OpenAI GPT-4
New: Strands Agents with native tool calling
"""

import os
import gradio as gr
from strands import Agent, tool
from strands.models.openai import OpenAIModel

# =============================================================================
# TOOLS
# =============================================================================
# In Strands, tools are simply Python functions decorated with @tool.
# The framework automatically extracts the function signature, docstring,
# and type hints to create tool specifications for the LLM.

@tool
def calculate(expression: str) -> str:
    """
    Evaluate a mathematical expression and return the result.
    
    Args:
        expression: A mathematical expression to evaluate (e.g., "4 * 7 / 3", "37 + 20")
    
    Returns:
        The result of the calculation as a string
    """
    try:
        # Using eval for simple math - in production, consider using a safer parser
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"Error evaluating expression: {e}"


@tool
def average_dog_weight(breed: str) -> str:
    """
    Get the average weight of a dog breed.
    
    Args:
        breed: The name of the dog breed (e.g., "Border Collie", "Scottish Terrier", "Toy Poodle")
    
    Returns:
        A string describing the average weight of the specified breed
    """
    # Normalize the breed name for matching
    breed_lower = breed.lower()
    
    if "scottish terrier" in breed_lower:
        return "Scottish Terriers average 20 lbs"
    elif "border collie" in breed_lower:
        return "A Border Collie's average weight is 37 lbs"
    elif "toy poodle" in breed_lower:
        return "A Toy Poodle's average weight is 7 lbs"
    elif "bulldog" in breed_lower:
        return "A Bulldog weighs 51 lbs"
    elif "labrador" in breed_lower:
        return "A Labrador Retriever's average weight is 65 lbs"
    elif "german shepherd" in breed_lower:
        return "A German Shepherd's average weight is 75 lbs"
    elif "golden retriever" in breed_lower:
        return "A Golden Retriever's average weight is 65 lbs"
    elif "beagle" in breed_lower:
        return "A Beagle's average weight is 25 lbs"
    elif "chihuahua" in breed_lower:
        return "A Chihuahua's average weight is 5 lbs"
    elif "great dane" in breed_lower:
        return "A Great Dane's average weight is 140 lbs"
    else:
        return f"I don't have specific data for {breed}. An average dog weighs about 50 lbs"


# =============================================================================
# SYSTEM PROMPT
# =============================================================================
# With Strands, we don't need to specify the ReAct format in the prompt.
# The framework handles tool selection and execution automatically.
# We just describe the agent's purpose and behavior.

SYSTEM_PROMPT = """
You are a helpful assistant that specializes in answering questions about dog weights.
You have access to tools that can:
1. Look up the average weight of specific dog breeds
2. Perform mathematical calculations

When a user asks about dog weights:
- Use the average_dog_weight tool to look up breed-specific information
- If they ask about multiple dogs, look up each breed separately
- Use the calculate tool for any math (like adding weights together)

Always provide clear, helpful answers about dog weights.
""".strip()


# =============================================================================
# AGENT SETUP
# =============================================================================

def create_agent():
    """
    Create and configure the Strands agent.
    
    The agent can use either:
    - OpenAI models (requires OPENAI_API_KEY)
    - Amazon Bedrock models (requires AWS credentials, default)
    """
    # Check for OpenAI API key
    openai_api_key = os.environ.get('OPENAI_API_KEY')
    
    if openai_api_key:
        # Use OpenAI if API key is available
        model = OpenAIModel(
            client_args={"api_key": openai_api_key},
            model_id="gpt-4o",
            params={
                "temperature": 0,
                "max_tokens": 1024
            }
        )
        print("Using OpenAI GPT-4o model")
    else:
        # Fall back to Bedrock (default in Strands)
        # Requires AWS credentials to be configured
        model = None  # Strands uses Bedrock by default
        print("Using Amazon Bedrock (default)")
    
    # Create the agent with our tools
    if model:
        agent = Agent(
            model=model,
            system_prompt=SYSTEM_PROMPT,
            tools=[calculate, average_dog_weight]
        )
    else:
        agent = Agent(
            system_prompt=SYSTEM_PROMPT,
            tools=[calculate, average_dog_weight]
        )
    
    return agent


def query(question: str) -> str:
    """
    Process a question using the Strands agent.
    
    Unlike the original implementation that required manual loop management
    and regex parsing, Strands handles all of this automatically:
    - Tool selection based on the question
    - Tool execution
    - Multi-step reasoning
    - Response generation
    
    Args:
        question: The user's question about dog weights
        
    Returns:
        The agent's response
    """
    try:
        # Create a fresh agent for each query
        agent = create_agent()
        
        # Invoke the agent - Strands handles the entire agentic loop
        result = agent(question)
        
        # Extract the final response
        # The result object contains the full conversation and metrics
        return str(result)
        
    except Exception as e:
        return f"Error processing question: {str(e)}"


# =============================================================================
# GRADIO INTERFACE
# =============================================================================

def process_question(question: str) -> str:
    """Wrapper function for Gradio interface."""
    return query(question)


# Create the Gradio interface
iface = gr.Interface(
    fn=process_question,
    inputs=gr.Textbox(
        label="Enter your question",
        placeholder="e.g., I have 2 dogs, a border collie and a scottish terrier. What is their combined weight?",
        lines=3
    ),
    outputs=gr.Textbox(label="Answer", lines=5),
    title="🐕 Dog Weight Calculator (Strands Agents)",
    description="""
    Ask about dog weights or perform calculations!
    
    **Examples:**
    - How much does a toy poodle weigh?
    - I have 2 dogs, a border collie and a scottish terrier. What is their combined weight?
    - What's heavier, a Great Dane or a German Shepherd?
    
    *Powered by Amazon Strands Agents SDK*
    """,
    examples=[
        ["How much does a toy poodle weigh?"],
        ["I have 2 dogs, a border collie and a scottish terrier. What is their combined weight?"],
        ["What's the average weight of a Labrador Retriever?"],
        ["If I have a Chihuahua and a Great Dane, how much do they weigh together?"],
    ],
    theme=gr.themes.Soft()
)


# =============================================================================
# DEMO / TESTING
# =============================================================================

def run_demo():
    """Run some demo queries to test the agent."""
    print("\n" + "="*60)
    print("STRANDS AGENTS - DOG WEIGHT CALCULATOR DEMO")
    print("="*60 + "\n")
    
    test_questions = [
        "How much does a toy poodle weigh?",
        "I have 2 dogs, a border collie and a scottish terrier. What is their combined weight?",
    ]
    
    for question in test_questions:
        print(f"Question: {question}")
        print("-" * 40)
        answer = query(question)
        print(f"Answer: {answer}")
        print("\n")


if __name__ == "__main__":
    import sys
    
    if "--demo" in sys.argv:
        # Run demo mode
        run_demo()
    else:
        # Launch Gradio interface
        iface.launch()