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,50 @@
# eval-image-classification (Image Classification Example with Promptfoo)
You can run this example with:
```bash
npx promptfoo@latest init --example eval-image-classification
cd eval-image-classification
```
This example demonstrates how to use Promptfoo for image classification tasks using the Fashion MNIST dataset. The example uses GPT-4o and GPT-4o-mini with a structured json schema to analyze images, including classification, color analysis, and additional attributes.
## Getting Started
1. Set up your OpenAI API key:
```sh
export OPENAI_API_KEY='your-api-key'
```
2. Run the evaluation:
```sh
npx promptfoo@latest eval
```
3. View the results:
```sh
npx promptfoo@latest view
```
4. Optionally, re-generate or update the dataset:
```sh
python dataset_gen.py
```
Note: You may need to install dependencies with:
```sh
pip install -r requirements.txt
```
This script creates a CSV file with 100 random images from the Fashion MNIST dataset and their labels. A CSV with 10 sample images is included so you can skip this step if preferred.
5. Experiment with the configuration:
- Modify the JSON schema in `promptfooconfig.yaml` to add or adjust required fields
- Try different models such as llama3.2 or Claude Sonnet 4.6 by changing the provider in the config
- Adjust the system prompt to improve classification accuracy
- Add additional assertions to validate model outputs
@@ -0,0 +1,117 @@
"""
This script downloads the Fashion MNIST dataset, processes a specified number of samples,
and saves them to a CSV file. Each row in the CSV file contains the original dataset index,
the class label name, and the image encoded as a base64 string.
The Fashion MNIST dataset is a collection of 70,000 grayscale images of 28x28 pixels,
each depicting one of 10 types of clothing. For more information on the dataset, see:
https://www.tensorflow.org/api_docs/python/tf/keras/datasets/fashion_mnist/load_data
Usage:
python save_fashion_mnist_to_csv.py --num_samples <num_samples> --filename <filename>
Arguments:
--num_samples: Number of samples to save (default: 100)
--filename: Output CSV file name (default: fashion_mnist_sample_base64.csv)
"""
import base64
import io
from typing import List
import numpy as np
import pandas as pd
import tensorflow as tf
from PIL import Image
# set np seed for reproducibility
np.random.seed(0)
def get_class_names() -> dict[int, str]:
"""Retrieves the class names for the Fashion MNIST dataset.
Returns:
A dictionary mapping class indices to class names.
"""
return {
0: "T-shirt/top",
1: "Trouser",
2: "Pullover",
3: "Dress",
4: "Coat",
5: "Sandal",
6: "Shirt",
7: "Sneaker",
8: "Bag",
9: "Ankle boot",
}
def image_to_base64(image: np.ndarray) -> str:
"""Converts an image to a base64 encoded string.
Args:
image: A numpy array representing the image.
Returns:
A base64 encoded string of the image.
"""
buffered = io.BytesIO()
pil_image = Image.fromarray(image)
# NOTE: For a dataset with large images, you can resize it here to save
# costs on the inference side.
# pil_image = pil_image.resize((32, 32))
pil_image.save(buffered, format="jpeg")
return base64.b64encode(buffered.getvalue()).decode("utf-8")
def save_fashion_mnist_sample_to_csv(num_samples: int, filename: str) -> None:
"""Saves a sample of the Fashion MNIST dataset to a CSV file.
Args:
num_samples: The number of samples to save.
filename: The name of the output CSV file.
"""
# Load the Fashion MNIST dataset
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_images, train_labels), _ = fashion_mnist.load_data()
class_names = get_class_names()
# Randomly sample indices without replacement
sample_indices = np.random.choice(len(train_images), num_samples, replace=False)
# Convert images to base64 and combine with labels and indices
data: List[List] = []
for sample_index in sample_indices:
base64_image = image_to_base64(train_images[sample_index])
label_index = train_labels[sample_index]
label_name = class_names[label_index]
data.append([sample_index, label_name, base64_image])
pd.DataFrame(data, columns=["index", "label", "image_base64"]).sort_values(
by=["label", "index"]
).to_csv(filename, index=False)
print(f"CSV file '{filename}' created successfully.")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description="Save Fashion MNIST samples to a CSV file."
)
parser.add_argument(
"--num_samples", type=int, default=100, help="Number of samples to save"
)
parser.add_argument(
"--filename",
type=str,
default="fashion_mnist_sample_base64.csv",
help="Output CSV file name",
)
args = parser.parse_args()
save_fashion_mnist_sample_to_csv(args.num_samples, args.filename)
@@ -0,0 +1,11 @@
index,label,image_base64
56864,Ankle boot,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+iunuvh54ptrG1vU0me6t7mNJEa0XzSNy7sMq/Mpx6jHoTXP3dldWEwhvLeSCXaG8uVSrAHpkHkVBiuy8GfDrWPFV5byvaz2+ksw8y7ZdoZe4jz949uMgd6+oreyRHUlESJIxGkZH3QOpz17Ba+dPjTp0qfEGSeK3kK3FtFJvCk7sZTP/AI7j8K6/wz44+HUNjaBkh02WBVVY7mxMhUjHzBkVucjOeCTzXcR/ETwcuc+IdNOe++QHH/fNK3xF8JspA8S6cM453N0Hb7tch4m8deB7jUYmlubW/ZYtvmCKRsDc3Gdv4/jXz7RRRX//2Q==
19563,Pullover,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/AOE+Fnhaw1qW+1DUbYXMdptEUTn5C3JJYd8ADjpzzXsDWOnS26W7aRYywrjbEbVdqgHIxjBAz6YpdStrTWYRb6lY2t2pG1VlgU7B/snGU/4Divm/xPpiaN4n1LTo/wDVQXDLGM5wucj9CK9a+Dwjg8JXEsxCxy3UwLH08tB/M13NvKsO5JoVkX6cg4I6/wBKS3YzXykhVBbO3G0Y9BXgvxPj8v4iarxgMYn+uYkNdx4Nf7L8K1dfvS3QUfV544/5V6FKh82TA43tz+JqhqDPbrZSqxUrqNlnnsbhFI/I1498WVx46mf+/bxH8lx/Ssm18YalZ+G4dIgW3WCC5W4WTaS5YNuAOTjGQOMdq6AfGLxD1NlpbHOSTFIM/k9Rz/FHXNX8qylttPhiknhLGGJg3ySK4wSxxyoqn8T7p7rxrMXVV2RIgC56cnufev/Z
21860,Pullover,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APENItY555HljEiRoW2EkZOCe3sDXWWeualpkRg0zULmwiZtzx2kpiVjjGSBxnHfrVS/8vVBJJdxie5fLPcNnzWb1LZ5P1zXGkYOD1roPDEcJu43uP8Aj3MoWbv8m1t36HP4VM0bB9pOSMAn3A5q5aqfs7kdTGw/Jgf5Vyl8oTULlB0WVh+prd0ALFp1xckA7UuAc/8AXAgfqRWi9qRcYb7ytyffNWbaFkQpgciRcf8AbJj/AOy1xepf8hS7/wCuz/8AoRrQ0didM1RD90Qlh9cH/AV0sV615KJpIowzDeducZ6+tTpcGXUbK3McYV53YkA54hcY69OTXBaic6ndk95n/ma//9k=
31488,Pullover,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APL/AIZ+GrbxT4yhsr1PMtIonnljyRvC4AXI5AyRX0jb6bYWVqlnb6bZR20f3YRbIVU+oBHX1PU1jeLfBml+K9KuI57GEXyQN9nukjCuhAO0ZGMrn+E8c8Yr5Xr1n9n6383xpqEuOE08qP8AeaWPH8jXvMrK0shQ5QuxU+ozxVmxVXkcNjOU/LPP9K+J5omhnkib7yMVP1BxXrfwIm+yXPiO9xzb2av/AN87n/8AZa9r04SS6VZSbSd9tE2cHnKKabLcSWev6UjJhbhbiLJyPmCBxx6YjNfI2tp5evain926lH/jxruvhNqc+nR+JVhWM+Zp7E7wTghWAI5/2jXBDVNQVFRb65CqoVQJmwABgAc12Hww1i9i+IOnyvK058uZcTszAAxnPf2FcjrEzXGuX87gB5LmR2x0yWJr/9k=
40228,Pullover,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APBrKzuNQvoLO0iaa4nkWOKNerMTgD86+t/BHhe20Hwlp9pcaVp8V6LdftZESMzSd9zD73pnJHpXjXxx8Ky6d4gi1mz02GDS54kjeSBFRfPy5IKjuQAc45/CvJq774MWpufilpTBQwhWaUgjPSNgP1Ir6itriK5t4ri3ffDMgkjb1UjIP5Vw/wAXFjuvhrrtsrebcWwt5m3DlQZVIPH+zur5Xr0v4Fv5XxAkmxkxafO4+oxX0NoVm1j4e0mzYYa3soIm+qxqD+ua5f4g27LoHjCVjmOfR7Yr7MjzA/oUr5WrtPhffy6d4uaaEKWNnOpDZxjb7EelaEXxr8YQwJEJbE7VC7jark4/Sr0XxA13xL4Q8Vf2k9u+LWKJfLi2bQXJPTr+Oa8tr//Z
845,Sandal,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+iiitjSdDF5NO2oSy2VrbwmZ28ks8nIVUjUkbmJYd+BkngGtzTvBml63ZEafr6warj5LDUIRAZD6K5bB/DPviuYv9J1DTL6SzvLSWK4jOGQrn+XBHuKrta3CY3QSrkZGUIq5Fr+swKFi1a+RV4AW4YAfTmpT4o15lKtq96ynqGmJB/OlTxVr8aBI9XvEQdFSUgfpVWfV9SuWDTahdSMBtBaVjgdcdfc1Sooor/9k=
8870,Sandal,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+iiirml6dJquoJZxSwxMyu2+ZtqKFUscnB7A10R8GiwngfUNQto4CUd0aWNZHTqcRlvMGRnGUGePWumv/AA94cuo4r2CSbUVh84y3CwNaxyIF3DzMgMGVTncF+cgj6eY3cKW15NBHcRXCRuUWaLOyQA43LkA4PXkCu20z4pX1qsK3+kabfmFdqyPGUk6YySOM49q05PjLNFJLPp/hrTLa6mcvNO5ZmkY9SSu0k/Umra/HbUBCFbRoS+3DYuZAp/4D6e2a5nVPifrup3Qn8uzgwu0LHESOpOTknnmuLooor//Z
25770,Sandal,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+pIYJbh9kMTyOAW2opJwBknjsBzUdPlhlt5TFNG8ci9UdSCPwNXtI0LVNeuvs2l2M91KOojXhfdj0Ue5Iru/+Faa7odsb/R9YSTW7NRLNa2hIZUbONjfx5APGAGwQCSMHqPDNlcan4ftvEeoaFaXmriN3tvs0KRPKucAuT8oYkE7sfKORya8+8Q+HPFGqavJeahbWEdxIBlIryABQOAPvknp3JPvU+i/EuXQ4Zo7PQdLiE2zzRGsihyoIBOH7ZP5mtKf4xXc0kcq6Hp0c0asqS/vWZA2NwUl+AcA4Oeeap3HxTvbuQyXFjbSSN1eQOzfmXrntZ8V3urXaTMI02R+WAqY4yT6n1rAooor/2Q==
58303,Shirt,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APOvhT4TtvFPid1v4jJZWkQlZCcK7lgqqfzJx/smvVdX+GvgzWrpJngksFQeUp0+RIUbHI3Ao2WwTkjn5e/UyXnwv8OXXhmTTbLSY0mjhbybksfOWXHys8mBuBJHBGMHgDINfNde2/BDytK8NeI9dlZHSL5niJOQkMbOxwOud/Hutek6fcO2k2JvZlNy9pCZJJHBJkMQLHaVJ4JHRSB7c4RbqSz8Uadavjyr61nIjRSuZIGikBHc/u2cAdwOc5JPzF4rsl07xbq9ohQpFdyBNhyNu4kc8dsVQ0+CS71C3tYmKyXEiwgj/aO3+tfXMrwxs0bSRIBlFdmQA4GM7dvUYTop7DIywHnPxmspf+EJs7tQqGC+UExoR8rxsDhiMkfKo9wQTnOa8DqxYXsunaja30IQy20yTIJF3KWUgjI7jI6V7X4d+JmrazbTNcafpqMkRlBiSRQWyRyN+O3pXG+OPiHqurQXegvaafBaGVdzQxN5jBT8oLMx9ByME45JHFefV//Z
3048,T-shirt/top,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/AOM+EngCw8WyXt/qqSS2tqyxxwKxUSuwJOSOcAAcDqSPofXWbQ7O3S0h0bRYLRFEqQ3UCKzj5l3rFsJ+8ACzc43egpW0rw54tto7S+0XTriPBWGe1A2HbjKxyIFZe5x0xjucV87eOfDS+EvF97pEcrSwR7XhkbqyMoYZ9xnB9waPD3jnxH4Vtnt9G1E20LyGRkMMbgtgDPzKewFfT95DcahJK8UsZtLhQwWRdzFSAQMnJA6HjFY3jTXdY8N+DNV1hLuNr5DCISUBVcyAEbTwcqW7D1r5s8QeI9U8UaiL/V7kT3CxiJWEaphQSQMKB6n3rKr7Pt1AtoVxjEacf8BFcX8YIy/w01JgfuSQE/8AfwD+tfMlFfSEXxE1Ihs2Gn8DjiXjj/frlfHPjfUNa8EX1pPa2cUcrRhjEJM8OD3c+leM1//Z
1 index label image_base64
2 56864 Ankle boot /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+iunuvh54ptrG1vU0me6t7mNJEa0XzSNy7sMq/Mpx6jHoTXP3dldWEwhvLeSCXaG8uVSrAHpkHkVBiuy8GfDrWPFV5byvaz2+ksw8y7ZdoZe4jz949uMgd6+oreyRHUlESJIxGkZH3QOpz17Ba+dPjTp0qfEGSeK3kK3FtFJvCk7sZTP/AI7j8K6/wz44+HUNjaBkh02WBVVY7mxMhUjHzBkVucjOeCTzXcR/ETwcuc+IdNOe++QHH/fNK3xF8JspA8S6cM453N0Hb7tch4m8deB7jUYmlubW/ZYtvmCKRsDc3Gdv4/jXz7RRRX//2Q==
3 19563 Pullover /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/AOE+Fnhaw1qW+1DUbYXMdptEUTn5C3JJYd8ADjpzzXsDWOnS26W7aRYywrjbEbVdqgHIxjBAz6YpdStrTWYRb6lY2t2pG1VlgU7B/snGU/4Divm/xPpiaN4n1LTo/wDVQXDLGM5wucj9CK9a+Dwjg8JXEsxCxy3UwLH08tB/M13NvKsO5JoVkX6cg4I6/wBKS3YzXykhVBbO3G0Y9BXgvxPj8v4iarxgMYn+uYkNdx4Nf7L8K1dfvS3QUfV544/5V6FKh82TA43tz+JqhqDPbrZSqxUrqNlnnsbhFI/I1498WVx46mf+/bxH8lx/Ssm18YalZ+G4dIgW3WCC5W4WTaS5YNuAOTjGQOMdq6AfGLxD1NlpbHOSTFIM/k9Rz/FHXNX8qylttPhiknhLGGJg3ySK4wSxxyoqn8T7p7rxrMXVV2RIgC56cnufev/Z
4 21860 Pullover /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APENItY555HljEiRoW2EkZOCe3sDXWWeualpkRg0zULmwiZtzx2kpiVjjGSBxnHfrVS/8vVBJJdxie5fLPcNnzWb1LZ5P1zXGkYOD1roPDEcJu43uP8Aj3MoWbv8m1t36HP4VM0bB9pOSMAn3A5q5aqfs7kdTGw/Jgf5Vyl8oTULlB0WVh+prd0ALFp1xckA7UuAc/8AXAgfqRWi9qRcYb7ytyffNWbaFkQpgciRcf8AbJj/AOy1xepf8hS7/wCuz/8AoRrQ0didM1RD90Qlh9cH/AV0sV615KJpIowzDeducZ6+tTpcGXUbK3McYV53YkA54hcY69OTXBaic6ndk95n/ma//9k=
5 31488 Pullover /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APL/AIZ+GrbxT4yhsr1PMtIonnljyRvC4AXI5AyRX0jb6bYWVqlnb6bZR20f3YRbIVU+oBHX1PU1jeLfBml+K9KuI57GEXyQN9nukjCuhAO0ZGMrn+E8c8Yr5Xr1n9n6383xpqEuOE08qP8AeaWPH8jXvMrK0shQ5QuxU+ozxVmxVXkcNjOU/LPP9K+J5omhnkib7yMVP1BxXrfwIm+yXPiO9xzb2av/AN87n/8AZa9r04SS6VZSbSd9tE2cHnKKabLcSWev6UjJhbhbiLJyPmCBxx6YjNfI2tp5evain926lH/jxruvhNqc+nR+JVhWM+Zp7E7wTghWAI5/2jXBDVNQVFRb65CqoVQJmwABgAc12Hww1i9i+IOnyvK058uZcTszAAxnPf2FcjrEzXGuX87gB5LmR2x0yWJr/9k=
6 40228 Pullover /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APBrKzuNQvoLO0iaa4nkWOKNerMTgD86+t/BHhe20Hwlp9pcaVp8V6LdftZESMzSd9zD73pnJHpXjXxx8Ky6d4gi1mz02GDS54kjeSBFRfPy5IKjuQAc45/CvJq774MWpufilpTBQwhWaUgjPSNgP1Ir6itriK5t4ri3ffDMgkjb1UjIP5Vw/wAXFjuvhrrtsrebcWwt5m3DlQZVIPH+zur5Xr0v4Fv5XxAkmxkxafO4+oxX0NoVm1j4e0mzYYa3soIm+qxqD+ua5f4g27LoHjCVjmOfR7Yr7MjzA/oUr5WrtPhffy6d4uaaEKWNnOpDZxjb7EelaEXxr8YQwJEJbE7VC7jark4/Sr0XxA13xL4Q8Vf2k9u+LWKJfLi2bQXJPTr+Oa8tr//Z
7 845 Sandal /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+iiitjSdDF5NO2oSy2VrbwmZ28ks8nIVUjUkbmJYd+BkngGtzTvBml63ZEafr6warj5LDUIRAZD6K5bB/DPviuYv9J1DTL6SzvLSWK4jOGQrn+XBHuKrta3CY3QSrkZGUIq5Fr+swKFi1a+RV4AW4YAfTmpT4o15lKtq96ynqGmJB/OlTxVr8aBI9XvEQdFSUgfpVWfV9SuWDTahdSMBtBaVjgdcdfc1Sooor/9k=
8 8870 Sandal /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+iiirml6dJquoJZxSwxMyu2+ZtqKFUscnB7A10R8GiwngfUNQto4CUd0aWNZHTqcRlvMGRnGUGePWumv/AA94cuo4r2CSbUVh84y3CwNaxyIF3DzMgMGVTncF+cgj6eY3cKW15NBHcRXCRuUWaLOyQA43LkA4PXkCu20z4pX1qsK3+kabfmFdqyPGUk6YySOM49q05PjLNFJLPp/hrTLa6mcvNO5ZmkY9SSu0k/Umra/HbUBCFbRoS+3DYuZAp/4D6e2a5nVPifrup3Qn8uzgwu0LHESOpOTknnmuLooor//Z
9 25770 Sandal /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+pIYJbh9kMTyOAW2opJwBknjsBzUdPlhlt5TFNG8ci9UdSCPwNXtI0LVNeuvs2l2M91KOojXhfdj0Ue5Iru/+Faa7odsb/R9YSTW7NRLNa2hIZUbONjfx5APGAGwQCSMHqPDNlcan4ftvEeoaFaXmriN3tvs0KRPKucAuT8oYkE7sfKORya8+8Q+HPFGqavJeahbWEdxIBlIryABQOAPvknp3JPvU+i/EuXQ4Zo7PQdLiE2zzRGsihyoIBOH7ZP5mtKf4xXc0kcq6Hp0c0asqS/vWZA2NwUl+AcA4Oeeap3HxTvbuQyXFjbSSN1eQOzfmXrntZ8V3urXaTMI02R+WAqY4yT6n1rAooor/2Q==
10 58303 Shirt /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APOvhT4TtvFPid1v4jJZWkQlZCcK7lgqqfzJx/smvVdX+GvgzWrpJngksFQeUp0+RIUbHI3Ao2WwTkjn5e/UyXnwv8OXXhmTTbLSY0mjhbybksfOWXHys8mBuBJHBGMHgDINfNde2/BDytK8NeI9dlZHSL5niJOQkMbOxwOud/Hutek6fcO2k2JvZlNy9pCZJJHBJkMQLHaVJ4JHRSB7c4RbqSz8Uadavjyr61nIjRSuZIGikBHc/u2cAdwOc5JPzF4rsl07xbq9ohQpFdyBNhyNu4kc8dsVQ0+CS71C3tYmKyXEiwgj/aO3+tfXMrwxs0bSRIBlFdmQA4GM7dvUYTop7DIywHnPxmspf+EJs7tQqGC+UExoR8rxsDhiMkfKo9wQTnOa8DqxYXsunaja30IQy20yTIJF3KWUgjI7jI6V7X4d+JmrazbTNcafpqMkRlBiSRQWyRyN+O3pXG+OPiHqurQXegvaafBaGVdzQxN5jBT8oLMx9ByME45JHFefV//Z
11 3048 T-shirt/top /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/AOM+EngCw8WyXt/qqSS2tqyxxwKxUSuwJOSOcAAcDqSPofXWbQ7O3S0h0bRYLRFEqQ3UCKzj5l3rFsJ+8ACzc43egpW0rw54tto7S+0XTriPBWGe1A2HbjKxyIFZe5x0xjucV87eOfDS+EvF97pEcrSwR7XhkbqyMoYZ9xnB9waPD3jnxH4Vtnt9G1E20LyGRkMMbgtgDPzKewFfT95DcahJK8UsZtLhQwWRdzFSAQMnJA6HjFY3jTXdY8N+DNV1hLuNr5DCISUBVcyAEbTwcqW7D1r5s8QeI9U8UaiL/V7kT3CxiJWEaphQSQMKB6n3rKr7Pt1AtoVxjEacf8BFcX8YIy/w01JgfuSQE/8AfwD+tfMlFfSEXxE1Ihs2Gn8DjiXjj/frlfHPjfUNa8EX1pPa2cUcrRhjEJM8OD3c+leM1//Z
@@ -0,0 +1,78 @@
const dedent = require('dedent');
/**
* Detects MIME type from base64 magic numbers
*
* Note: This is duplicated from src/evaluatorHelpers.ts to keep the example
* self-contained and runnable without dependencies on core promptfoo internals.
*
* Magic numbers (base64-encoded file signatures):
* - JPEG: /9j/
* - PNG: iVBORw0KGgo
* - GIF: R0lGODlh or R0lGODdh
* - WebP: UklGR (RIFF)
* - BMP: Qk0 or Qk1
* - TIFF: SUkq or TU0A
* - ICO: AAABAA
*/
function getMimeTypeFromBase64(base64Data) {
if (base64Data.startsWith('/9j/')) {
return 'image/jpeg';
} else if (base64Data.startsWith('iVBORw0KGgo')) {
return 'image/png';
} else if (base64Data.startsWith('R0lGODlh') || base64Data.startsWith('R0lGODdh')) {
return 'image/gif';
} else if (base64Data.startsWith('UklGR')) {
return 'image/webp';
} else if (base64Data.startsWith('Qk0') || base64Data.startsWith('Qk1')) {
return 'image/bmp';
} else if (base64Data.startsWith('SUkq') || base64Data.startsWith('TU0A')) {
return 'image/tiff';
} else if (base64Data.startsWith('AAABAA')) {
return 'image/x-icon';
}
// Fallback to JPEG if no magic number is matched
return 'image/jpeg';
}
module.exports = (context) => {
return [
{
role: 'system',
content: dedent`
You are an AI assistant tasked with analyzing and classifying images. Your goal is to determine the type of clothing item depicted in the image and provide additional relevant information.
Please analyze the image and provide:
1. Classification (must be one of: T-shirt/top, Trouser, Pullover, Dress, Coat, Sandal, Shirt, Sneaker, Bag, Ankle boot)
2. Primary color or color scheme
3. Notable features or patterns
4. Approximate style or era (e.g., modern, vintage, classic)
5. Confidence level (1-10, where 1 is least confident and 10 is most confident)
6. Brief reasoning for the classification
Provide your response as a JSON object with the following structure:
{
"classification": string (one of the allowed categories),
"color": string,
"features": string,
"style": string,
"confidence": number (1-10),
"reasoning": string
}
`,
},
{
role: 'user',
content: [
{
type: 'image_url',
image_url: {
url: `data:${getMimeTypeFromBase64(context.vars.image_base64)};base64,${
context.vars.image_base64
}`,
},
},
],
},
];
};
@@ -0,0 +1,72 @@
# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
description: Image Classification Example of Fashion MNIST dataset
providers:
- openai:chat:gpt-4o
- openai:chat:gpt-4.1-mini
prompts:
- label: Image Classification
raw: file://prompt.js
config:
response_format:
type: json_schema
json_schema:
name: image_classification
schema:
type: object
properties:
classification:
type: string
enum:
[
'T-shirt/top',
'Trouser',
'Pullover',
'Dress',
'Coat',
'Sandal',
'Shirt',
'Sneaker',
'Bag',
'Ankle boot',
]
color:
type: string
features:
type: string
style:
type: string
confidence:
type: integer
reasoning:
type: string
required:
- classification
- color
- features
- style
- confidence
- reasoning
additionalProperties: false
defaultTest:
assert:
- type: is-json
value:
type: object
properties:
classification:
type: string
color:
type: string
features:
type: string
style:
type: string
confidence:
type: integer
reasoning:
type: string
- type: javascript
value: 'output.classification === context.vars.label'
metric: accuracy
tests: file://fashion_mnist_sample_base64.csv
@@ -0,0 +1,4 @@
numpy==1.26.4
pandas==2.3.3
pillow==12.2.0
tensorflow==2.20.0