chore: import upstream snapshot with attribution
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
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
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) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:24:08 +08:00
commit 0d3cb498a3
5438 changed files with 1316560 additions and 0 deletions
@@ -0,0 +1,108 @@
# integration-strands-agents (Strands Agents SDK example)
This example demonstrates how to evaluate [Strands Agents SDK](https://github.com/strands-agents/sdk-python) with [promptfoo](https://promptfoo.dev).
[Strands Agents](https://strandsagents.com/) is an open-source AI agent framework developed by [AWS](https://github.com/strands-agents) that provides a model-driven approach to building AI agents.
You can run this example with:
```bash
npx promptfoo@latest init --example integration-strands-agents
cd integration-strands-agents
```
## Overview
This example showcases:
- Creating a [Strands agent](https://strandsagents.com/latest/user-guide/concepts/agents/) with custom tools
- Using the [`@tool` decorator](https://strandsagents.com/latest/user-guide/concepts/tools/python-tools/) to define agent capabilities
- Evaluating agent responses with various [promptfoo assertions](https://promptfoo.dev/docs/configuration/expected-outputs/)
- Testing tool usage with mock weather and temperature conversion tools
## Prerequisites
- Python 3.9+
- [OpenAI API key](https://platform.openai.com/api-keys) (default) or other supported provider
## Setup
### 1. Install Python dependencies
```bash
pip install -r requirements.txt
```
This installs:
- [`strands-agents[openai]`](https://pypi.org/project/strands-agents/) - The Strands Agents SDK with OpenAI support
- [`pydantic`](https://docs.pydantic.dev/) - Data validation library required by Strands
### 2. Set environment variables
```bash
export OPENAI_API_KEY=your-api-key-here
```
### Alternative: use Anthropic or Bedrock
[Strands supports multiple model providers](https://strandsagents.com/latest/user-guide/concepts/model-providers/). To use [Anthropic](https://www.anthropic.com/):
```bash
pip install 'strands-agents[anthropic]'
export ANTHROPIC_API_KEY=your-key
```
Then modify `agent.py` to use [`AnthropicModel`](https://strandsagents.com/latest/user-guide/concepts/model-providers/anthropic/) instead of [`OpenAIModel`](https://strandsagents.com/latest/user-guide/concepts/model-providers/openai/).
To use [Amazon Bedrock](https://strandsagents.com/latest/user-guide/concepts/model-providers/amazon-bedrock/):
```bash
pip install 'strands-agents[bedrock]'
```
## Running the example
```bash
# Run evaluation
npx promptfoo eval
# View results in the web UI
npx promptfoo view
```
## How it works
### Agent structure
The agent is defined in `agent.py` using the [Strands Agent class](https://strandsagents.com/latest/user-guide/concepts/agents/) with two tools:
- `get_weather`: Returns mock weather data for cities (New York, London, Tokyo, Paris, Seattle, San Francisco)
- `convert_temperature`: Converts temperatures between Fahrenheit and Celsius
Tools are defined using the [`@tool` decorator](https://strandsagents.com/latest/user-guide/concepts/tools/python-tools/) which automatically exposes them to the LLM based on their docstrings.
### Provider integration
`agent_provider.py` exposes a `call_api` function that [promptfoo's Python provider](https://promptfoo.dev/docs/providers/python/) calls to interact with the Strands agent.
### Test cases and assertion types
The [promptfoo config](https://promptfoo.dev/docs/configuration/guide/) includes 5 test cases that demonstrate different [assertion types](https://promptfoo.dev/docs/configuration/expected-outputs/):
| Test | Description | Assertion types used |
| ----------------------------------- | -------------------------- | --------------------------------------- |
| Weather query for New York | Basic tool usage | `contains-any`, `llm-rubric`, `latency` |
| Weather query for London | Verify temperature format | `contains-any`, `javascript`, `latency` |
| Weather query for Tokyo | Case-insensitive matching | `icontains`, `javascript`, `latency` |
| Weather with temperature conversion | Multi-tool chaining | `llm-rubric`, `javascript`, `latency` |
| Weather for unknown city | Graceful fallback handling | `icontains`, `not-contains`, `latency` |
#### Assertion types explained
- **[`latency`](https://promptfoo.dev/docs/configuration/expected-outputs/#latency)** - Ensures responses complete within 30 seconds (applied to all tests via `defaultTest`)
- **[`contains-any`](https://promptfoo.dev/docs/configuration/expected-outputs/#contains)** - Verifies the agent returns expected city names and weather data from the mock tool
- **[`icontains`](https://promptfoo.dev/docs/configuration/expected-outputs/#contains)** - Case-insensitive matching to verify city names appear regardless of formatting
- **[`not-contains`](https://promptfoo.dev/docs/configuration/expected-outputs/#not-contains)** - Ensures the agent handles unknown cities gracefully without error messages
- **[`javascript`](https://promptfoo.dev/docs/configuration/expected-outputs/#javascript)** - Validates temperature format (°F/°C symbols) and response length requirements
- **[`llm-rubric`](https://promptfoo.dev/docs/configuration/expected-outputs/model-graded/)** - Semantically evaluates whether the agent correctly chains weather lookup with temperature conversion
@@ -0,0 +1,122 @@
"""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.")
@@ -0,0 +1,66 @@
"""Promptfoo provider for Strands Agents SDK.
This module exposes a call_api function that promptfoo uses to interact
with the Strands agent for evaluation.
The provider follows promptfoo's Python provider interface:
https://promptfoo.dev/docs/providers/python/
Usage in promptfooconfig.yaml:
providers:
- id: 'file://agent_provider.py:call_api'
config:
model_id: 'gpt-4o-mini'
For more information:
- Strands Agents SDK: https://github.com/strands-agents/sdk-python
- Strands Documentation: https://strandsagents.com/
- promptfoo Python Providers: https://promptfoo.dev/docs/providers/python/
"""
from typing import Any, Dict
from agent import run_agent
def call_api(
prompt: str, options: Dict[str, Any], context: Dict[str, Any]
) -> Dict[str, Any]:
"""Main entry point for promptfoo to call the Strands agent.
This function is called by promptfoo for each test case. It receives the
rendered prompt and returns the agent's response.
Args:
prompt: The user's input message (rendered from the prompt template)
options: Configuration options from the provider config in YAML
context: Context information from promptfoo (vars, test metadata, etc.)
Returns:
Dictionary with 'output' key containing the agent's response.
On error, includes both 'error' and 'output' keys.
"""
try:
# Extract model configuration from provider options
config = options.get("config", {})
model_id = config.get("model_id", "gpt-4o-mini")
# Run the Strands agent and get the response
result = run_agent(prompt, model_id)
# Return in promptfoo's expected format
return {"output": result}
except Exception as e:
# Return error in a format promptfoo can display
return {"error": str(e), "output": f"Error: {str(e)}"}
if __name__ == "__main__":
import os
print("Testing Strands agent provider...")
if os.getenv("OPENAI_API_KEY"):
result = call_api("What's the weather in New York?", {}, {})
print(f"Result: {result}")
else:
print("Set OPENAI_API_KEY to test.")
@@ -0,0 +1,104 @@
# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
# Strands Agents SDK Example - Weather Assistant Evaluation
#
# This configuration evaluates a Strands agent that provides weather information
# and temperature conversions. It demonstrates:
# - Using a Python provider (file://) to integrate custom agents
# https://promptfoo.dev/docs/providers/python/
# - Testing tool usage with contains-any assertions
# https://promptfoo.dev/docs/configuration/expected-outputs/#contains
# - Using llm-rubric for semantic evaluation of responses
# https://promptfoo.dev/docs/configuration/expected-outputs/model-graded/
# - Performance assertions (latency, cost)
# https://promptfoo.dev/docs/configuration/expected-outputs/#latency
#
# Strands Agents SDK by AWS: https://github.com/strands-agents/sdk-python
# Strands Documentation: https://strandsagents.com/
description: Strands Agents SDK evaluation
# The prompt template - {{query}} is replaced with test case variables
prompts:
- '{{query}}'
# Provider configuration pointing to our Python agent
# The file:// prefix tells promptfoo to load a Python module
# Format: file://<script>:<function>
providers:
- id: 'file://agent_provider.py:call_api'
label: 'Strands Weather Agent'
config:
# Model ID passed to the Strands agent (can be changed to gpt-4o, etc.)
model_id: 'gpt-4o-mini'
# Default assertions applied to all test cases
# https://promptfoo.dev/docs/configuration/guide/#default-test-cases
defaultTest:
assert:
# Performance: Response should complete within 30 seconds
- type: latency
threshold: 30000
# Test cases to evaluate the agent's responses
tests:
# Test 1: Basic weather query - verifies the get_weather tool works
- description: 'Weather query for New York'
vars:
query: "What's the weather like in New York?"
assert:
# Check that response contains expected weather data
- type: contains-any
value: ['New York', 'Sunny', '72']
# Use LLM to verify response quality
- type: llm-rubric
value: 'Response should include weather information for New York City'
# Test 2: Another city to verify tool handles different inputs
- description: 'Weather query for London'
vars:
query: 'Tell me the current weather in London'
assert:
- type: contains-any
value: ['London', 'Cloudy', '58']
# Verify response mentions temperature (custom JavaScript assertion)
# https://promptfoo.dev/docs/configuration/expected-outputs/#javascript
- type: javascript
value: 'output.includes("°F") || output.includes("degrees")'
# Test 3: Test with icontains (case-insensitive)
- description: 'Weather query for Tokyo'
vars:
query: "What's the weather in Tokyo?"
assert:
# Case-insensitive check
- type: icontains
value: 'tokyo'
- type: icontains
value: 'clear'
# Verify response is not too short (at least 20 chars)
- type: javascript
value: 'output.length >= 20'
# Test 4: Multi-tool usage - tests both get_weather AND convert_temperature
# This verifies the agent can chain multiple tools together
- description: 'Weather with temperature conversion'
vars:
query: "What's the weather in New York? Also tell me the temperature in Celsius."
assert:
- type: llm-rubric
value: 'Response should include New York weather (72°F) AND convert it to Celsius (approximately 22°C)'
# Verify both temperature units appear in response
- type: javascript
value: '(output.includes("°F") || output.includes("Fahrenheit")) && (output.includes("°C") || output.includes("Celsius"))'
# Test 5: Unknown city - verify graceful handling
- description: 'Weather for unknown city'
vars:
query: "What's the weather in Atlantis?"
assert:
# Should still return a response (default weather)
- type: icontains
value: 'atlantis'
# Should not error out
- type: not-contains
value: 'Error'
@@ -0,0 +1,14 @@
# Strands Agents SDK Example - Python Dependencies
#
# Strands Agents SDK by AWS:
# - GitHub: https://github.com/strands-agents/sdk-python
# - Documentation: https://strandsagents.com/
# - PyPI: https://pypi.org/project/strands-agents/
# Strands Agents SDK with OpenAI model support
# https://strandsagents.com/latest/user-guide/concepts/model-providers/openai/
strands-agents[openai]>=1.0.0,<2.0.0
# Pydantic for data validation (required by Strands)
# https://docs.pydantic.dev/
pydantic>=2.0.0