Files
wehub-resource-sync 0d3cb498a3
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Waiting to run
Test and Publish Multi-arch Docker Image / test (push) Waiting to run
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Blocked by required conditions
Validate Renovate Config / Validate Renovate Configuration (push) Waiting to run
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

123 lines
3.8 KiB
Python

"""Strands agent implementation with weather tools.
This module demonstrates how to build a Strands agent with custom tools
that can be evaluated by promptfoo. It showcases:
- The @tool decorator for defining agent capabilities
https://strandsagents.com/latest/user-guide/concepts/tools/python-tools/
- Multiple tools working together (weather lookup + temperature conversion)
- Integration with OpenAI models via Strands SDK
https://strandsagents.com/latest/user-guide/concepts/model-providers/openai/
Strands Agents SDK is an open-source AI agent framework by AWS:
- Documentation: https://strandsagents.com/
- GitHub: https://github.com/strands-agents/sdk-python
- PyPI: https://pypi.org/project/strands-agents/
"""
from strands import Agent, tool
from strands.models.openai import OpenAIModel
# Define tools using the @tool decorator. The docstring becomes the tool's
# description that the LLM uses to decide when to call it.
@tool
def get_weather(city: str) -> str:
"""Get current weather for a city.
Args:
city: The name of the city to get weather for
"""
weather_data = {
"new york": "72°F, Sunny",
"london": "58°F, Cloudy",
"tokyo": "68°F, Clear",
"paris": "64°F, Partly Cloudy",
"seattle": "55°F, Rainy",
"san francisco": "62°F, Foggy",
}
city_lower = city.lower()
if city_lower in weather_data:
return f"Weather in {city}: {weather_data[city_lower]}"
return f"Weather in {city}: 70°F, Clear (default)"
@tool
def convert_temperature(value: float, from_unit: str) -> str:
"""Convert temperature between Fahrenheit and Celsius.
Args:
value: The temperature value to convert
from_unit: The unit to convert from ('F' for Fahrenheit, 'C' for Celsius)
"""
from_unit = from_unit.upper()
if from_unit == "F":
celsius = (value - 32) * 5 / 9
return f"{value}°F = {celsius:.1f}°C"
elif from_unit == "C":
fahrenheit = (value * 9 / 5) + 32
return f"{value}°C = {fahrenheit:.1f}°F"
return f"Unknown unit '{from_unit}'. Use 'F' for Fahrenheit or 'C' for Celsius."
def create_agent(model_id: str = "gpt-4o-mini") -> Agent:
"""Create a Strands agent with weather tools.
Args:
model_id: The OpenAI model ID to use
Returns:
A configured Strands Agent instance
"""
model = OpenAIModel(model_id=model_id, params={"temperature": 0.7})
return Agent(
model=model,
tools=[get_weather, convert_temperature],
system_prompt=(
"You are a helpful weather assistant. "
"Use the weather tool to get weather for cities and the temperature "
"conversion tool to convert between Fahrenheit and Celsius."
),
)
def run_agent(prompt: str, model_id: str = "gpt-4o-mini") -> str:
"""Run the agent with a prompt and return the response.
Args:
prompt: The user's input message
model_id: The OpenAI model ID to use
Returns:
The agent's response as a string
"""
import io
import sys
agent = create_agent(model_id)
# Suppress stdout during agent execution. The Strands SDK prints tool call
# information to stdout (e.g., "Tool #1: get_weather"), which interferes with
# promptfoo's Python worker protocol that uses stdout for control messages.
# Without this suppression, the worker times out waiting for protocol signals.
old_stdout = sys.stdout
sys.stdout = io.StringIO()
try:
result = agent(prompt)
finally:
sys.stdout = old_stdout
return str(result)
if __name__ == "__main__":
import os
if os.getenv("OPENAI_API_KEY"):
print("Testing Strands agent...")
response = run_agent("What's the weather in New York?")
print(f"Response: {response}")
else:
print("Set OPENAI_API_KEY to test.")