Python: consolidate lab packages into a single one; update contribution guidelines (#940)

* consolidate lab packages into a single one; update contribution guidelines

* update dep list

* add poe tasks; fix tests and lint erros

* add lab tests for CI

* fix test

* update root pyproject.toml
This commit is contained in:
Eric Zhu
2025-09-26 20:28:05 -07:00
committed by GitHub
Unverified
parent 9fec0f5ef4
commit 514d0209a8
55 changed files with 615 additions and 1087 deletions
+1
View File
@@ -0,0 +1 @@
test-results.xml
+70 -116
View File
@@ -1,145 +1,99 @@
# Agent Framework Lab
This directory contains experimental packages for Microsoft Agent Framework that are distributed as separate installable packages under the `agent_framework.lab` namespace.
Lab packages are not part of the core framework and may experience breaking changes or be deprecated in the future.
This is the experimental package for Microsoft Agent Framework, `agent-framework-lab`, which contains
various lab modules built on top of the core framework.
Lab modules are not part of the core framework and may experience breaking changes or be deprecated in the future.
## What are Lab Packages?
## What are Lab Modules?
Lab packages are extensions to the core Agent Framework that falls into
Lab modules are extensions to the core Agent Framework that fall into
one of the following categories:
1. Incubation of new features that may get incorporated by the core framework.
1. Incubation of new features that may get incorporated by the core framework.
2. Research prototypes built on the core framework.
3. Benchmarks and experimentation tools.
## Lab Packages
## Lab Modules
- [**gaia**](./gaia/): GAIA benchmark implementation (`agent-framework-lab-gaia`)
- [**lightning**](./lightning/): Reinforcement learning for agents (`agent-framework-lab-lightning`)
- [**tau2**](./tau2/): Customer service agent simulation framework (`agent-framework-lab-tau2`)
- [**gaia**](./gaia/): Evaluate your agents using the GAIA benchmark for general assistant tasks
- [**tau2**](./tau2/): Evaluate your agents using the TAU2 benchmark for customer support tasks
- [**lightning**](./lightning/): RL training for agents (in development)
## How do I contribute?
This repo only contains lab packages maintained by Microsoft.
If you want to contribute, please take the following steps:
1. Follow the [Create a New Lab Package](#create-new-lab-package) guide
below to create your own lab package.
2. Create a new repo on GitHub and check in your package there.
3. Tag your repo with `agent-framework-lab` for better discovery.
4. Submit a PR to this repo (github.com/microsoft/agent-framework)
to add a link to your repo in the [list](#lab-packages) above.
**The PR title must contain "[New Lab Package]"**.
5. We will review your repo and decide whether to approve it.
Follow the [guidelines](#guidelines) when you create your package, our decision
to accept your PR will be based on your idea as well as the quality of your
code.
We may decide to maintain your package in this repo. In that case, we will
contact you directly.
## Package Structure
Each lab package follows this structure:
## Repository Structure
```
packages/lab/{lab_name}/
├── agent_framework/
│ └── lab/
│ └── {lab_name}/
│ └── __init__.py # Imports from agent_framework_lab_{lab_name}
── agent_framework_lab_{lab_name}/ # Actual implementation package
├── __init__.py # Main exports and __version__
├── {module_files}.py # Implementation modules
└── py.typed # Type hints marker
├── tests/
│ ├── __init__.py
│ └── test_{lab_name}.py # Package tests
├── pyproject.toml # Package configuration
├── README.md # Package-specific documentation
└── LICENSE # MIT License
agent-framework-lab/
├── pyproject.toml # Single package configuration for agent-framework-lab
├── README.md # This file
├── LICENSE # License file
├── namespace/ # Centralized namespace package files
│ └── agent_framework/
└── lab/
├── gaia/ # Re-exports from agent_framework_lab_gaia
├── lightning/ # Re-exports from agent_framework_lab_lightning
│ └── tau2/ # Re-exports from agent_framework_lab_tau2
├── gaia/ # GAIA module implementation
│ └── agent_framework_lab_gaia/
├── lightning/ # Lightning module implementation
│ └── agent_framework_lab_lightning/
└── tau2/ # TAU2 module implementation
└── agent_framework_lab_tau2/
```
## Creating a New Lab Package
This structure maintains a single PyPI package `agent-framework-lab` while supporting modular imports through the namespace package mechanism.
### Create The Package
## Installation
First ensure `cookiecutter` is installed.
Install the base lab package:
```bash
pip install cookiecutter
pip install agent-framework-lab
```
Then go to the directory where you want to create the package:
For details on installing individual modules, see their respective README files listed above.
```bash
cookiecutter /path/to/agent-framework/python/packages/lab/cookiecutter-agent-framework-lab
```
## Usage
You will be prompted for:
- **package_name**: The name of your lab package (e.g., "lightning", "vision")
- **package_display_name**: Human-readable name (e.g., "Lighting Tools", "Computer Vision")
- **package_description**: Brief description (auto-generated from display name)
- **include_cli_script**: Whether to include a CLI script (y/n)
### After Package Creation
1. **Implement your functionality** in `agent_framework_lab_your_package_name/`
2. **Update exports** in `__init__.py` `__all__` list
3. **Add dependencies** to `pyproject.toml`
4. **Write tests** in the `tests/` directory
5. **Update README** with usage examples and API documentation
### Add to Workspace (only for packages maintained in this repo)
After creating your package, add it to the workspace configuration:
```
# Edit python/pyproject.toml
# Add to dependencies section:
dependencies = [
# ... existing packages ...
"agent-framework-lab-your-package-name",
]
# Add to [tool.uv.sources] section:
agent-framework-lab-your-package-name = { workspace = true }
```
### Usage
Once created, users can install your lab package
1. directly from your repo:
```bash
pip install git+https://github.com/your-username/your-lab-package-repo.git
```
2. or from PyPI if you have uploaded your lab package there:
```bash
pip install "agent-framework-lab-your-package-name"
```
Then, they can use your lab package:
Import and use lab modules from the `agent_framework.lab` namespace.
For example, to use the GAIA module:
```python
from agent_framework.lab.your_package_name import YourClass, your_function
# Use the functionality
instance = YourClass()
result = your_function()
# Using GAIA module
from agent_framework.lab.gaia import GAIA
```
## Guidelines
## Should I consume Lab Modules?
1. **Naming**: Use lowercase with hyphens for package names (`agent-framework-lab-your-package-name`)
2. **Namespace**: Always use `agent_framework.lab.your_package_name` for imports
3. **Versioning**: Start with `0.1.0b1` for beta releases
4. **Dependencies**: Minimize external dependencies, always include `agent-framework`
5. **Documentation**: Include comprehensive README with usage examples
6. **Tests**: Write comprehensive tests with good coverage
7. **Type hints**: Always include type hints and `py.typed` file
If you are looking for stable and production-ready features, you should not use lab modules. Stick to the core framework.
If you are looking for experimentation, research, or want to
benchmark different approaches -- most importantly, if you don't mind breaking changes and potential deprecations --
then lab modules are for you.
## Contributing to Lab Modules
### Microsoft-maintained modules
For Microsoft-maintained modules in this repository, please follow standard contribution guidelines and submit pull requests directly to this repository.
### Community modules
If you want to contribute a community-maintained lab module:
1. Create a new repository on GitHub for your module
2. Tag your repository with `agent-framework-lab` for discoverability
3. Submit a PR to add a link to your repository in the [Lab Modules](#lab-modules) section above
4. Use the PR title format: `[New Lab Module] Your Module Name`
We will review your submission based on the guidelines below.
### Guidelines
1. **Purpose**: Community modules should fit into one of the three categories of lab modules (incubation, research, benchmarks)
2. **Namespace**: Community modules should avoid the `agent_framework.lab` namespace (reserved for modules maintained in this repository)
3. **Dependencies**: Minimize external dependencies, always include `agent-framework` as a base dependency
4. **Documentation**: Include comprehensive README with installation instructions and usage examples
5. **Tests**: Write comprehensive tests with good coverage
6. **Type hints**: Always include type hints and a `py.typed` file
7. **Versioning**: Use semantic versioning, start with `0.1.0` for initial releases
@@ -1,58 +0,0 @@
# Cookiecutter Template for Agent Framework Lab Packages
This is a cookiecutter template for creating new lab packages in the Microsoft Agent Framework.
## Usage
```bash
cd /path/to/agent-framework/python/packages/lab
cookiecutter ./cookiecutter-agent-framework-lab
```
You will be prompted for the following information:
- **package_name**: The name of your lab package (e.g., "lightning", "vision")
- **package_display_name**: Human-readable name (e.g., "Lighting Tools", "Computer Vision")
- **package_description**: Brief description of the package (auto-generated from display name)
- **version**: Starting version (default: 0.1.0b1)
- **author_name**: Author name (default: Microsoft)
- **author_email**: Author email (default: af-support@microsoft.com)
- **include_cli_script**: Whether to include a CLI script (y/n)
- **cli_script_name**: Name of CLI script if included
## What Gets Generated
The template creates a complete lab package structure:
```
{package_name}/
├── agent_framework/
│ └── lab/
│ └── {package_name}/
│ └── __init__.py
├── agent_framework_lab_{package_name}/
│ ├── __init__.py
│ └── py.typed
├── tests/
│ ├── __init__.py
│ └── test_{package_name}.py
├── pyproject.toml
├── README.md
└── LICENSE
```
## After Generation
1. Implement your functionality in `agent_framework_lab_{package_name}/`
2. Update the `__all__` exports in `__init__.py`
3. Add your dependencies to `pyproject.toml`
4. Write comprehensive tests
5. Update the README with usage examples
## Integration
Don't forget to add your new package to the workspace:
1. Add to `python/pyproject.toml` dependencies
2. Add to `[tool.uv.sources]` section
3. Test installation with `uv run python -c "from agent_framework.lab.{name} import *"`
@@ -1,20 +0,0 @@
{
"package_name": "",
"package_display_name": "",
"package_description": "{{ cookiecutter.package_display_name }} module for Microsoft Agent Framework.",
"version": "0.1.0b1",
"author_name": "Microsoft",
"author_email": "af-support@microsoft.com",
"license": "MIT",
"include_cli_script": ["y", "n"],
"cli_script_name": "{{ cookiecutter.package_name }}_cli",
"python_requires": ">=3.10",
"within_microsoft_agent_framework_repo": ["y", "n"],
"__prompts__": {
"within_microsoft_agent_framework_repo": "Are you creating this package within the github.com/microsoft/agent-framework repo or a fork of it? (If yes, ensure you create it in python/packages/lab/ directory)"
},
"_templates_suffix": "",
"_copy_without_render": [
"*.py.typed"
]
}
@@ -1,56 +0,0 @@
# Agent Framework Lab - {{ cookiecutter.package_display_name }}
{{ cookiecutter.package_description }}
## Installation
```bash
pip install agent-framework-lab-{{ cookiecutter.package_name }}
```
## Usage
```python
from agent_framework.lab.{{ cookiecutter.package_name }} import YourClass
# Your usage example here
instance = YourClass()
```
## Overview
Brief description of what this lab package provides and its main features.
## Features
- Feature 1: Description
- Feature 2: Description
- Feature 3: Description
## Examples
### Basic Usage
```python
from agent_framework.lab.{{ cookiecutter.package_name }} import YourClass
# Example usage
```
### Advanced Usage
```python
# More advanced examples
```
## API Reference
Document your main classes and functions here.
## Contributing
This package is part of the Microsoft Agent Framework Lab. Please see the main repository for contribution guidelines.
## License
This project is licensed under the {{ cookiecutter.license }} License - see the LICENSE file for details.
@@ -1,95 +0,0 @@
[project]
name = "agent-framework-lab-{{ cookiecutter.package_name }}"
description = "{{ cookiecutter.package_description }}"
authors = [{ name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}"}]
readme = "README.md"
requires-python = "{{ cookiecutter.python_requires }}"
version = "{{ cookiecutter.version }}"
license-files = ["LICENSE"]
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
urls.issues = "https://github.com/microsoft/agent-framework/issues"
classifiers = [
"License :: OSI Approved :: {{ cookiecutter.license }} License",
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"agent-framework",
"pydantic>=2.0.0",
# Add your specific dependencies here
]
{% if cookiecutter.include_cli_script == "y" %}
[project.scripts]
{{ cookiecutter.cli_script_name }} = "agent_framework_lab_{{ cookiecutter.package_name }}:main"
{% endif %}
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
package-dir = {"" = "src"}
packages = ["agent_framework_lab_{{ cookiecutter.package_name }}", "agent_framework.lab.{{ cookiecutter.package_name }}"]
[tool.setuptools.package-data]
agent_framework_lab_{{ cookiecutter.package_name }} = ["py.typed"]
[tool.ruff]
line-length = 120
target-version = "py310"
extend-exclude = ["tests", "__pycache__"]
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP", "C4", "N"]
ignore = ["N803", "N806", "N999", "UP007"]
[tool.ruff.format]
quote-style = "double"
[tool.mypy]
python_version = "3.10"
strict = true
check_untyped_defs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true
show_error_codes = true
implicit_reexport = true
packages = ["src.agent_framework_lab_{{ cookiecutter.package_name }}"]
{% if cookiecutter.within_microsoft_agent_framework_repo == "y" %}
[tool.poe]
executor.type = "uv"
include = "../../../shared_tasks.toml"
[tool.poe.tasks]
test = "pytest --cov=agent_framework_lab_{{ cookiecutter.package_name }} --cov-report=term-missing:skip-covered tests"
mypy = "mypy agent_framework_lab_{{ cookiecutter.package_name }}"
{% else %}
[tool.poe.tasks]
fmt = "ruff format"
format = "ruff format"
lint = "ruff check"
test = "pytest --cov=agent_framework_lab_{{ cookiecutter.package_name }} --cov-report=term-missing:skip-covered tests"
mypy = "mypy agent_framework_lab_{{ cookiecutter.package_name }}"
{% endif %}
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
@@ -1,4 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# Import and re-export from the actual implementation
from agent_framework_lab_{{cookiecutter.package_name}} import * # noqa: F403, F401
@@ -1,21 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
"""
{{ cookiecutter.package_description }}
"""
import importlib.metadata
# Import your main exports here
# from .main_module import MainClass, main_function
try:
__version__ = importlib.metadata.version(__name__)
except importlib.metadata.PackageNotFoundError:
__version__ = "0.0.0" # Fallback for development mode
__all__ = [
# List your exports here
# "MainClass",
# "main_function",
]
@@ -1 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
@@ -1,15 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
"""Tests for {{ cookiecutter.package_name }} module."""
import pytest
from agent_framework_lab_{{cookiecutter.package_name}} import __version__
class Test{{cookiecutter.package_name | title}}:
"""Test the {{ cookiecutter.package_name }} module."""
def test_version(self):
"""Test package version is defined."""
assert __version__ is not None
assert __version__ == "{{ cookiecutter.version }}"
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+4 -2
View File
@@ -3,12 +3,14 @@
The GAIA benchmark can be used for evaluating agents and workflows built using the Agent Framework.
It includes built-in benchmarks as well as utilities for running custom evaluations.
> **Note**: This module is part of the consolidated `agent-framework-lab` package. Install the package with the `gaia` extra to use this module.
## Setup
Use `uv` to install the package with GAIA dependencies:
Install the agent-framework-lab package with GAIA dependencies:
```bash
uv pip install "agent-framework-lab-gaia"
pip install "agent-framework-lab[gaia]"
```
Set up Hugging Face token:
@@ -1,4 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# This makes agent_framework a namespace package
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -1,4 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# This makes agent_framework.lab a namespace package
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -1,8 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GAIA benchmark module for Agent Framework.
"""
"""GAIA benchmark module for Agent Framework."""
import importlib.metadata
@@ -16,13 +14,13 @@ except importlib.metadata.PackageNotFoundError:
__all__ = [
"GAIA",
"GAIATelemetryConfig",
"gaia_scorer",
"viewer_main",
"Task",
"Prediction",
"Evaluation",
"Evaluator",
"GAIATelemetryConfig",
"Prediction",
"Task",
"TaskResult",
"TaskRunner",
"Evaluator",
"gaia_scorer",
"viewer_main",
]
@@ -6,12 +6,12 @@ from dataclasses import dataclass
from typing import Any, Protocol, runtime_checkable
__all__ = [
"Task",
"Prediction",
"Evaluation",
"Evaluator",
"Prediction",
"Task",
"TaskResult",
"TaskRunner",
"Evaluator",
]
@@ -1,8 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GAIA benchmark implementation for Agent Framework.
"""
"""GAIA benchmark implementation for Agent Framework."""
import asyncio
import json
@@ -22,7 +20,7 @@ from tqdm import tqdm
from ._types import Evaluation, Evaluator, Prediction, Task, TaskResult, TaskRunner
__all__ = ["GAIA", "gaia_scorer", "GAIATelemetryConfig"]
__all__ = ["GAIA", "GAIATelemetryConfig", "gaia_scorer"]
class GAIATelemetryConfig:
@@ -36,8 +34,7 @@ class GAIATelemetryConfig:
trace_to_file: bool = False,
file_path: str | None = None,
):
"""
Initialize telemetry configuration.
"""Initialize telemetry configuration.
Args:
enable_tracing: Whether to enable OpenTelemetry tracing
@@ -74,8 +71,9 @@ class GAIATelemetryConfig:
try:
import json
import os
from collections.abc import Sequence
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace import ReadableSpan, TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter, SpanExportResult
from opentelemetry.trace import get_tracer_provider
@@ -85,7 +83,7 @@ class GAIATelemetryConfig:
# Ensure directory exists
os.makedirs(os.path.dirname(os.path.abspath(file_path)), exist_ok=True)
def export(self, spans) -> SpanExportResult:
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
try:
with open(self.file_path, "a", encoding="utf-8") as f:
for span in spans:
@@ -131,8 +129,10 @@ def _normalize_number_str(number_str: str) -> float:
return float("inf")
def _split_string(s: str, chars: list[str] = [",", ";"]) -> list[str]:
def _split_string(s: str, chars: list[str] | None = None) -> list[str]:
"""Split string by multiple delimiters."""
if chars is None:
chars = [",", ";"]
return re.split(f"[{''.join(chars)}]", s)
@@ -146,8 +146,7 @@ def _normalize_str(s: str, remove_punct: bool = True) -> str:
def gaia_scorer(model_answer: str, ground_truth: str) -> bool:
"""
Official GAIA scoring function.
"""Official GAIA scoring function.
Args:
model_answer: The model's answer
@@ -170,22 +169,21 @@ def gaia_scorer(model_answer: str, ground_truth: str) -> bool:
if is_float(ground_truth):
# numeric exact match after normalization
return _normalize_number_str(model_answer) == float(ground_truth)
elif any(ch in ground_truth for ch in [",", ";"]):
if any(ch in ground_truth for ch in [",", ";"]):
# list with per-element compare (number or string)
gt_elems = _split_string(ground_truth)
ma_elems = _split_string(model_answer)
if len(gt_elems) != len(ma_elems):
return False
comparisons = []
for ma, gt in zip(ma_elems, gt_elems):
for ma, gt in zip(ma_elems, gt_elems, strict=False):
if is_float(gt):
comparisons.append(_normalize_number_str(ma) == float(gt))
else:
comparisons.append(_normalize_str(ma, remove_punct=False) == _normalize_str(gt, remove_punct=False))
return all(comparisons)
else:
# string normalize + exact
return _normalize_str(model_answer) == _normalize_str(ground_truth)
# string normalize + exact
return _normalize_str(model_answer) == _normalize_str(ground_truth)
def _read_jsonl(path: Path) -> Iterable[dict[str, Any]]:
@@ -238,8 +236,7 @@ def _load_gaia_local(repo_dir: Path, wanted_levels: list[int] | None = None, max
class GAIA:
"""
GAIA benchmark runner for Agent Framework.
"""GAIA benchmark runner for Agent Framework.
GAIA (General AI Assistant) is a benchmark for general-purpose AI assistants.
This class provides utilities to run the benchmark with custom agents.
@@ -252,8 +249,7 @@ class GAIA:
hf_token: str | None = None,
telemetry_config: GAIATelemetryConfig | None = None,
):
"""
Initialize GAIA benchmark runner.
"""Initialize GAIA benchmark runner.
Args:
evaluator: Custom evaluator function. If None, uses default GAIA scorer.
@@ -282,7 +278,6 @@ class GAIA:
def _ensure_data(self) -> Path:
"""Ensure GAIA data is available locally."""
if self.data_dir.exists() and any(self.data_dir.rglob("metadata.jsonl")):
return self.data_dir
@@ -347,14 +342,12 @@ class GAIA:
# Add results to span
if span:
span.set_attributes(
{
"gaia.task.runtime_seconds": runtime_seconds,
"gaia.task.is_correct": evaluation.is_correct,
"gaia.task.score": evaluation.score,
"gaia.task.prediction_length": len(prediction.prediction or ""),
}
)
span.set_attributes({
"gaia.task.runtime_seconds": runtime_seconds,
"gaia.task.is_correct": evaluation.is_correct,
"gaia.task.score": evaluation.score,
"gaia.task.prediction_length": len(prediction.prediction or ""),
})
return TaskResult(
task_id=task.task_id,
@@ -368,14 +361,12 @@ class GAIA:
# Record error in span
if span:
span.set_attributes(
{
"gaia.task.runtime_seconds": runtime_seconds,
"gaia.task.error": str(e),
"gaia.task.is_correct": False,
"gaia.task.score": 0.0,
}
)
span.set_attributes({
"gaia.task.runtime_seconds": runtime_seconds,
"gaia.task.error": str(e),
"gaia.task.is_correct": False,
"gaia.task.score": 0.0,
})
span.record_exception(e)
return TaskResult(
@@ -396,8 +387,7 @@ class GAIA:
timeout: int | None = None,
out: str | None = None,
) -> list[TaskResult]:
"""
Run the GAIA benchmark.
"""Run the GAIA benchmark.
Args:
task_runner: Function that takes a Task and returns a Prediction
@@ -425,10 +415,7 @@ class GAIA:
data_path = self._ensure_data()
# Parse level parameter
if isinstance(level, int):
levels = [level]
else:
levels = level
levels = [level] if isinstance(level, int) else level
# Load tasks
with self.tracer.start_as_current_span(
@@ -442,11 +429,9 @@ class GAIA:
tasks = _load_gaia_local(data_path, wanted_levels=levels, max_n=max_n)
if load_span:
load_span.set_attributes(
{
"gaia.tasks.loaded_count": len(tasks),
}
)
load_span.set_attributes({
"gaia.tasks.loaded_count": len(tasks),
})
if not tasks:
raise RuntimeError(
@@ -458,11 +443,9 @@ class GAIA:
# Update benchmark span with task info
if benchmark_span:
benchmark_span.set_attributes(
{
"gaia.benchmark.total_tasks": len(tasks),
}
)
benchmark_span.set_attributes({
"gaia.benchmark.total_tasks": len(tasks),
})
# Run tasks
semaphore = asyncio.Semaphore(parallel)
@@ -484,14 +467,12 @@ class GAIA:
# Update benchmark span with final results
if benchmark_span:
benchmark_span.set_attributes(
{
"gaia.benchmark.accuracy": accuracy,
"gaia.benchmark.correct_count": correct,
"gaia.benchmark.total_count": len(results),
"gaia.benchmark.avg_runtime_seconds": avg_runtime,
}
)
benchmark_span.set_attributes({
"gaia.benchmark.accuracy": accuracy,
"gaia.benchmark.correct_count": correct,
"gaia.benchmark.total_count": len(results),
"gaia.benchmark.avg_runtime_seconds": avg_runtime,
})
print("\nGAIA Benchmark Results:")
print(f"Accuracy: {accuracy:.3f} ({correct}/{len(results)})")
-86
View File
@@ -1,86 +0,0 @@
[project]
name = "agent-framework-lab-gaia"
description = "GAIA benchmark module for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
license-files = ["LICENSE"]
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
urls.issues = "https://github.com/microsoft/agent-framework/issues"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"agent-framework",
"pydantic>=2.0.0",
"opentelemetry-api>=1.24.0",
"tqdm>=4.60.0",
"huggingface-hub>=0.20.0",
"orjson>=3.8.0",
]
[project.scripts]
gaia_viewer = "agent_framework_lab_gaia:viewer_main"
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["agent_framework_lab_gaia", "agent_framework.lab.gaia"]
[tool.setuptools.package-data]
agent_framework_lab_gaia = ["py.typed"]
[tool.ruff]
line-length = 120
target-version = "py310"
extend-exclude = ["tests", "__pycache__"]
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP", "C4", "N"]
ignore = ["N803", "N806", "N999", "UP007"]
[tool.ruff.format]
quote-style = "double"
[tool.mypy]
python_version = "3.10"
strict = true
check_untyped_defs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true
show_error_codes = true
implicit_reexport = true
packages = ["agent_framework_lab_gaia"]
[tool.poe]
executor.type = "uv"
include = "../../../shared_tasks.toml"
[tool.poe.tasks]
test = "pytest --cov=agent_framework_lab_gaia --cov-report=term-missing:skip-covered tests"
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]
addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
@@ -1,7 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GAIA Benchmark Sample
"""GAIA Benchmark Sample.
To run this sample, execute it from the root directory of the agent-framework repository:
cd /path/to/agent-framework
@@ -11,12 +10,11 @@ This avoids namespace package conflicts that occur when running from within the
"""
from agent_framework.azure import AzureAIAgentClient
from agent_framework.lab.gaia import GAIA, Evaluation, GAIATelemetryConfig, Prediction, Task
from azure.identity.aio import AzureCliCredential
from agent_framework.lab.gaia import GAIA, Evaluation, GAIATelemetryConfig, Prediction, Task
async def evaluate_task(task: Task, prediction: Prediction) -> Evaluation:
def evaluate_task(task: Task, prediction: Prediction) -> Evaluation:
"""Evaluate the prediction for a given task."""
# Simple evaluation: check if the prediction contains the answer
is_correct = (task.answer or "").lower() in prediction.prediction.lower()
@@ -24,12 +22,10 @@ async def evaluate_task(task: Task, prediction: Prediction) -> Evaluation:
async def main() -> None:
"""Run GAIA benchmark with telemetry configuration."""
# Configure telemetry for tracing
telemetry_config = GAIATelemetryConfig(
enable_tracing=True, # Enable OpenTelemetry tracing
# Optional: Configure external endpoints
# otlp_endpoint="http://localhost:4317", # For Aspire Dashboard or other OTLP endpoints
# applicationinsights_connection_string="your_connection_string", # For Azure Monitor
# Configure local file tracing
trace_to_file=True, # Export traces to local file
file_path="gaia_benchmark_traces.jsonl", # Custom file path for traces
@@ -1 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
+4 -5
View File
@@ -2,27 +2,26 @@
"""Tests for GAIA benchmark implementation."""
import pytest
from agent_framework_lab_gaia import gaia_scorer
class TestGAIAScorer:
"""Test the GAIA scoring function."""
def test_numeric_exact_match(self):
"""Test numeric exact matching."""
assert gaia_scorer("42", "42") is True
assert gaia_scorer("42.0", "42") is True
assert gaia_scorer("42", "42.0") is True
assert gaia_scorer("42", "43") is False
def test_string_normalization(self):
"""Test string normalization and matching."""
assert gaia_scorer("Hello World", "hello world") is True
assert gaia_scorer("Hello, World!", "helloworld") is True
assert gaia_scorer("test", "TEST") is True
assert gaia_scorer("test", "different") is False
def test_list_matching(self):
"""Test list matching with comma/semicolon separation."""
assert gaia_scorer("1,2,3", "1,2,3") is True
@@ -30,7 +29,7 @@ class TestGAIAScorer:
assert gaia_scorer("apple,banana", "apple,banana") is True
assert gaia_scorer("1,2,3", "1,2,4") is False
assert gaia_scorer("1,2", "1,2,3") is False
def test_none_handling(self):
"""Test handling of None values."""
assert gaia_scorer("None", "test") is False
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+6 -2
View File
@@ -1,11 +1,15 @@
# Agent Framework Lab - Agent Framework x Agent Lightning
# Agent Framework Lab - Lightning
RL Module for Microsoft Agent Framework
> **Note**: This module is part of the consolidated `agent-framework-lab` package. Install the package with the `lightning` extra to use this module.
## Installation
Install the agent-framework-lab package with Lightning dependencies:
```bash
pip install agent-framework-lab-lightning
pip install "agent-framework-lab[lightning]"
```
## Usage
@@ -1,4 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# This makes agent_framework a namespace package
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -1,4 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# This makes agent_framework.lab a namespace package
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -1,8 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
"""
RL Module for Microsoft Agent Framework
"""
"""RL Module for Microsoft Agent Framework."""
import importlib.metadata
@@ -11,11 +9,4 @@ try:
except importlib.metadata.PackageNotFoundError:
__version__ = "0.0.0" # Fallback for development mode
# Import your main exports here
# from .main_module import MainClass, main_function
__all__ = [
# List your exports here
# "MainClass",
# "main_function",
]
__all__: list[str] = []
@@ -1,87 +0,0 @@
[project]
name = "agent-framework-lab-lightning"
description = "RL Module for Microsoft Agent Framework"
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
license-files = ["LICENSE"]
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
urls.issues = "https://github.com/microsoft/agent-framework/issues"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"agent-framework",
"pydantic>=2.0.0",
# Add your specific dependencies here
]
[project.scripts]
lightning = "agent_framework_lab_lightning:main"
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["agent_framework_lab_lightning", "agent_framework.lab.lightning"]
[tool.setuptools.package-data]
agent_framework_lab_lightning = ["py.typed"]
[tool.ruff]
line-length = 120
target-version = "py310"
extend-exclude = ["tests", "__pycache__"]
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP", "C4", "N"]
ignore = ["N803", "N806", "N999", "UP007"]
[tool.ruff.format]
quote-style = "double"
[tool.mypy]
python_version = "3.10"
strict = true
check_untyped_defs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true
show_error_codes = true
implicit_reexport = true
packages = ["agent_framework_lab_lightning"]
[tool.poe]
executor.type = "uv"
include = "../../../shared_tasks.toml"
[tool.poe.tasks]
test = "pytest --cov=agent_framework_lab_lightning --cov-report=term-missing:skip-covered tests"
mypy = "mypy agent_framework_lab_lightning"
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]
addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
@@ -1 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
@@ -2,14 +2,16 @@
"""Tests for lightning module."""
import pytest
from agent_framework_lab_lightning import __version__
class TestLightning:
"""Test the lightning module."""
def test_version(self):
"""Test package version is defined."""
assert __version__ is not None
assert __version__ == "0.1.0b1"
# In development mode, version falls back to "0.0.0"
# In installed mode, it would be the actual package version
assert isinstance(__version__, str)
assert len(__version__) > 0
@@ -1,4 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
# Import and re-export from the actual implementation
from agent_framework_lab_gaia import * # noqa: F403, F401
from agent_framework_lab_gaia import * # noqa: F403
@@ -1,4 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
# Import and re-export from the actual implementation
from agent_framework_lab_tau2 import * # noqa: F403, F401
from agent_framework_lab_lightning import * # noqa: F403
@@ -1,4 +1,4 @@
# Copyright (c) Microsoft. All rights reserved.
# Import and re-export from the actual implementation
from agent_framework_lab_lightning import * # noqa: F403, F401
from agent_framework_lab_tau2 import * # noqa: F403
+136
View File
@@ -0,0 +1,136 @@
[project]
name = "agent-framework-lab"
description = "Experimental modules for Microsoft Agent Framework"
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
license-files = ["LICENSE"]
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
urls.issues = "https://github.com/microsoft/agent-framework/issues"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"agent-framework",
]
[project.optional-dependencies]
# GAIA benchmark module dependencies
gaia = [
"pydantic>=2.0.0",
"opentelemetry-api>=1.24.0",
"tqdm>=4.60.0",
"huggingface-hub>=0.20.0",
"orjson>=3.8.0",
]
# Lightning RL training module dependencies
lightning = [
"pydantic>=2.0.0",
]
# TAU2 benchmark module dependencies
tau2 = [
"pydantic>=2.0.0",
"tiktoken>=0.11.0",
"loguru>=0.7.3",
"numpy",
"tau2@ git+https://github.com/sierra-research/tau2-bench@5ba9e3e56db57c5e4114bf7f901291f09b2c5619",
]
[project.scripts]
gaia_viewer = "agent_framework_lab_gaia:viewer_main"
lightning = "agent_framework_lab_lightning:main"
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = [
"agent_framework_lab_gaia",
"agent_framework_lab_lightning",
"agent_framework_lab_tau2",
"agent_framework.lab.gaia",
"agent_framework.lab.lightning",
"agent_framework.lab.tau2",
]
[tool.setuptools.package-dir]
"agent_framework_lab_gaia" = "gaia/agent_framework_lab_gaia"
"agent_framework_lab_lightning" = "lightning/agent_framework_lab_lightning"
"agent_framework_lab_tau2" = "tau2/agent_framework_lab_tau2"
"agent_framework.lab.gaia" = "namespace/agent_framework/lab/gaia"
"agent_framework.lab.lightning" = "namespace/agent_framework/lab/lightning"
"agent_framework.lab.tau2" = "namespace/agent_framework/lab/tau2"
[tool.setuptools.package-data]
agent_framework_lab_gaia = ["py.typed"]
agent_framework_lab_lightning = ["py.typed"]
agent_framework_lab_tau2 = ["py.typed"]
[tool.ruff]
extend = "../../pyproject.toml"
[tool.ruff.lint]
ignore = ["T201", "ASYNC230", "INP001"] # Allow print statements, blocking file operations, and implicit namespace packages in lab modules
[tool.coverage.run]
omit = [
"**/__init__.py"
]
[tool.pyright]
extend = "../../pyproject.toml"
exclude = ['gaia/tests', 'lightning/tests', 'tau2/tests', 'namespace', '**/samples']
[tool.mypy]
plugins = ['pydantic.mypy']
strict = true
python_version = "3.10"
ignore_missing_imports = true
disallow_untyped_defs = true
no_implicit_optional = true
check_untyped_defs = true
warn_return_any = true
show_error_codes = true
warn_unused_ignores = false
disallow_incomplete_defs = true
disallow_untyped_decorators = true
[tool.bandit]
targets = ["agent_framework_lab_gaia", "agent_framework_lab_lightning", "agent_framework_lab_tau2"]
exclude_dirs = ["gaia/tests", "lightning/tests", "tau2/tests"]
[tool.poe]
executor.type = "uv"
include = "../../shared_tasks.toml"
[tool.poe.tasks]
mypy-gaia = "mypy --config-file $POE_ROOT/pyproject.toml gaia/agent_framework_lab_gaia"
mypy-lightning = "mypy --config-file $POE_ROOT/pyproject.toml lightning/agent_framework_lab_lightning"
mypy-tau2 = "mypy --config-file $POE_ROOT/pyproject.toml tau2/agent_framework_lab_tau2"
mypy = ["mypy-gaia", "mypy-lightning", "mypy-tau2"]
test = "pytest --cov-report=term-missing:skip-covered --junitxml=test-results.xml"
test-gaia = "pytest gaia/tests --cov=agent_framework_lab_gaia --cov-report=term-missing:skip-covered"
test-lightning = "pytest lightning/tests --cov=agent_framework_lab_lightning --cov-report=term-missing:skip-covered"
test-tau2 = "pytest tau2/tests --cov=agent_framework_lab_tau2 --cov-report=term-missing:skip-covered"
[tool.pytest.ini_options]
pythonpath = ["."]
addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+12 -7
View File
@@ -2,7 +2,10 @@
τ²-bench implements a simulation framework for evaluating customer service agents across various domains.
> **Note**: This module is part of the consolidated `agent-framework-lab` package. Install the package with the `tau2` extra to use this module.
The framework orchestrates conversations between two AI agents:
- **Customer Service Agent**: Follows domain-specific policies and has access to tools (e.g., booking systems, databases)
- **User Simulator**: Simulates realistic customer behavior with specific goals and scenarios
@@ -20,8 +23,10 @@ Each evaluation runs a multi-turn conversation where the user simulator presents
## Installation
Install the agent-framework-lab package with TAU2 dependencies:
```bash
pip install agent-framework-lab-tau2
pip install "agent-framework-lab[tau2]"
```
Download data from [Tau2-Bench](https://github.com/sierra-research/tau2-bench):
@@ -45,7 +50,7 @@ export TAU2_DATA_DIR="data"
```python
import asyncio
from agent_framework.openai import OpenAIChatClient
from agent_framework_lab_tau2 import TaskRunner
from agent_framework.lab.tau2 import TaskRunner
from tau2.domains.airline.environment import get_tasks
async def run_single_task():
@@ -126,7 +131,7 @@ export OPENAI_BASE_URL="https://your-custom-endpoint.com/v1"
### Custom Agent Implementation
```python
from agent_framework_lab_tau2 import TaskRunner
from agent_framework.lab.tau2 import TaskRunner
from agent_framework import ChatAgent
class CustomTaskRunner(TaskRunner):
@@ -149,8 +154,8 @@ class CustomTaskRunner(TaskRunner):
### Custom Workflow Integration
```python
from agent_framework._workflow import WorkflowBuilder, AgentExecutor
from agent_framework_lab_tau2 import TaskRunner
from agent_framework import WorkflowBuilder, AgentExecutor
from agent_framework.lab.tau2 import TaskRunner
class WorkflowTaskRunner(TaskRunner):
def build_conversation_workflow(self, assistant_agent, user_simulator_agent):
@@ -172,7 +177,7 @@ class WorkflowTaskRunner(TaskRunner):
### Utility Functions
```python
from agent_framework_lab_tau2 import patch_env_set_state, unpatch_env_set_state
from agent_framework.lab.tau2 import patch_env_set_state, unpatch_env_set_state
# Enable compatibility patches for τ²-bench integration
patch_env_set_state()
@@ -187,4 +192,4 @@ This package is part of the Microsoft Agent Framework Lab. Please see the main r
## License
This project is licensed under the MIT License - see the LICENSE file for details.
This project is licensed under the MIT License - see the LICENSE file for details.
@@ -1,8 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
"""
Tau2 Benchmark for Agent Framework.
"""
"""Tau2 Benchmark for Agent Framework."""
import importlib.metadata
@@ -51,7 +51,7 @@ def log_messages(messages: list[ChatMessage]) -> None:
Provides visual debugging by color-coding different message roles and
content types. Escapes HTML-like characters to prevent log formatting issues.
"""
_logger = logger.opt(colors=True)
logger_ = logger.opt(colors=True)
for msg in messages:
# Handle different content types
if hasattr(msg, "contents") and msg.contents:
@@ -60,53 +60,53 @@ def log_messages(messages: list[ChatMessage]) -> None:
if content.type == "text":
escape_text = content.text.replace("<", r"\<")
if msg.role == Role.SYSTEM:
_logger.info(f"<cyan>[SYSTEM]</cyan> {escape_text}")
logger_.info(f"<cyan>[SYSTEM]</cyan> {escape_text}")
elif msg.role == Role.USER:
_logger.info(f"<green>[USER]</green> {escape_text}")
logger_.info(f"<green>[USER]</green> {escape_text}")
elif msg.role == Role.ASSISTANT:
_logger.info(f"<blue>[ASSISTANT]</blue> {escape_text}")
logger_.info(f"<blue>[ASSISTANT]</blue> {escape_text}")
elif msg.role == Role.TOOL:
_logger.info(f"<yellow>[TOOL]</yellow> {escape_text}")
logger_.info(f"<yellow>[TOOL]</yellow> {escape_text}")
else:
_logger.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {escape_text}")
logger_.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {escape_text}")
elif content.type == "function_call":
function_call_text = f"{content.name}({content.arguments})"
function_call_text = function_call_text.replace("<", r"\<")
_logger.info(f"<yellow>[TOOL_CALL]</yellow> 🔧 {function_call_text}")
logger_.info(f"<yellow>[TOOL_CALL]</yellow> 🔧 {function_call_text}")
elif content.type == "function_result":
function_result_text = f"ID:{content.call_id} -> {content.result}"
function_result_text = function_result_text.replace("<", r"\<")
_logger.info(f"<yellow>[TOOL_RESULT]</yellow> 🔨 {function_result_text}")
logger_.info(f"<yellow>[TOOL_RESULT]</yellow> 🔨 {function_result_text}")
else:
content_text = str(content).replace("<", r"\<")
_logger.info(f"<magenta>[{msg.role.value.upper()}] ({content.type})</magenta> {content_text}")
logger_.info(f"<magenta>[{msg.role.value.upper()}] ({content.type})</magenta> {content_text}")
else:
# Fallback for content without type
text_content = str(content).replace("<", r"\<")
if msg.role == Role.SYSTEM:
_logger.info(f"<cyan>[SYSTEM]</cyan> {text_content}")
logger_.info(f"<cyan>[SYSTEM]</cyan> {text_content}")
elif msg.role == Role.USER:
_logger.info(f"<green>[USER]</green> {text_content}")
logger_.info(f"<green>[USER]</green> {text_content}")
elif msg.role == Role.ASSISTANT:
_logger.info(f"<blue>[ASSISTANT]</blue> {text_content}")
logger_.info(f"<blue>[ASSISTANT]</blue> {text_content}")
elif msg.role == Role.TOOL:
_logger.info(f"<yellow>[TOOL]</yellow> {text_content}")
logger_.info(f"<yellow>[TOOL]</yellow> {text_content}")
else:
_logger.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {text_content}")
logger_.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {text_content}")
elif hasattr(msg, "text") and msg.text:
# Handle simple text messages
text_content = msg.text.replace("<", r"\<")
if msg.role == Role.SYSTEM:
_logger.info(f"<cyan>[SYSTEM]</cyan> {text_content}")
logger_.info(f"<cyan>[SYSTEM]</cyan> {text_content}")
elif msg.role == Role.USER:
_logger.info(f"<green>[USER]</green> {text_content}")
logger_.info(f"<green>[USER]</green> {text_content}")
elif msg.role == Role.ASSISTANT:
_logger.info(f"<blue>[ASSISTANT]</blue> {text_content}")
logger_.info(f"<blue>[ASSISTANT]</blue> {text_content}")
elif msg.role == Role.TOOL:
_logger.info(f"<yellow>[TOOL]</yellow> {text_content}")
logger_.info(f"<yellow>[TOOL]</yellow> {text_content}")
else:
_logger.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {text_content}")
logger_.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {text_content}")
else:
# Fallback for other message formats
text_content = str(msg).replace("<", r"\<")
_logger.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {text_content}")
logger_.info(f"<magenta>[{msg.role.value.upper()}]</magenta> {text_content}")
@@ -58,6 +58,7 @@ class SlidingWindowChatMessageList(ChatMessageList):
def get_token_count(self) -> int:
"""Estimate token count for a list of messages using tiktoken.
Args:
messages: List of ChatMessage objects
system_message: Optional system message to include in count
@@ -35,11 +35,7 @@ def convert_tau2_tool_to_ai_function(tau2_tool: Tool) -> AIFunction[Any, Any]:
def wrapped_func(**kwargs: Any) -> Any:
result = tau2_tool(**kwargs)
# Deep copy to prevent mutations of returned data
if isinstance(result, BaseModel):
result = result.model_copy(deep=True)
else:
result = deepcopy(result)
return result
return result.model_copy(deep=True) if isinstance(result, BaseModel) else deepcopy(result)
return AIFunction(
name=tau2_tool.name,
@@ -55,7 +51,6 @@ def convert_agent_framework_messages_to_tau2_messages(messages: list[ChatMessage
Handles role mapping, text extraction, function calls, and function results.
Function results are converted to separate ToolMessage instances.
"""
tau2_messages = []
for msg in messages:
@@ -126,16 +121,13 @@ def patch_env_set_state() -> None:
initialization_actions: list[EnvFunctionCall] | None,
message_history: list[Message],
) -> None:
if self.solo_mode:
if any(isinstance(message, UserMessage) for message in message_history):
raise ValueError("User messages are not allowed in solo mode")
if self.solo_mode and any(isinstance(message, UserMessage) for message in message_history):
raise ValueError("User messages are not allowed in solo mode")
def get_actions_from_messages(
messages: list[Message],
) -> list[tuple[ToolCall, ToolMessage]]:
"""
Get the actions from the messages.
"""
"""Get the actions from the messages."""
messages = deepcopy(messages)[::-1]
actions = []
while messages:
@@ -194,14 +186,13 @@ def unpatch_env_set_state() -> None:
def _dump_function_result(result: Any) -> Any:
if isinstance(result, BaseModel):
return result.model_dump_json()
elif isinstance(result, list):
if isinstance(result, list):
return [_dump_function_result(item) for item in result]
elif isinstance(result, dict):
if isinstance(result, dict):
return {k: _dump_function_result(v) for k, v in result.items()}
elif result is None:
if result is None:
return None
else:
return result
return result
def _to_native(obj: Any) -> Any:
@@ -227,9 +218,7 @@ def _to_native(obj: Any) -> Any:
def _recursive_json_deserialize(obj: Any) -> Any:
"""
Recursively deserialize a JSON object.
"""
"""Recursively deserialize a JSON object."""
if isinstance(obj, str):
try:
deserialized = json.loads(obj)
@@ -100,6 +100,7 @@ class TaskRunner:
return self
def __repr__(self) -> str:
"""Return string representation of TaskRunner."""
return (
f"TaskRunner(max_steps={self.max_steps}, step_count={self.step_count}, "
f"full_conversation_length={len(self.full_conversation)}, "
@@ -108,7 +109,6 @@ class TaskRunner:
def should_not_stop(self, response: AgentExecutorResponse) -> bool:
"""Based on the response, check whether we should or not stop the conversation."""
# Determine who sent this based on executor_id
is_from_agent = response.executor_id == ASSISTANT_AGENT_ID
is_from_user = response.executor_id == USER_SIMULATOR_ID
@@ -165,7 +165,6 @@ class TaskRunner:
Returns:
The assistant agent.
"""
# Initialize tau2 environment and extract tools/policy
# This provides the domain-specific context (airline customer service in this case)
env = get_environment()
@@ -216,7 +215,6 @@ class TaskRunner:
Returns:
The user simulator agent.
"""
# User simulator follows tau2's guidelines for realistic customer behavior
# No tools available - users typically don't have direct system access
user_sim_guidelines = get_global_user_sim_guidelines(use_tools=False)
@@ -277,7 +275,6 @@ class TaskRunner:
Returns:
The conversation workflow.
"""
# STEP 1: Create workflow executors
# Each executor wraps an agent or function for workflow orchestration
self._assistant_executor = AgentExecutor(assistant_agent, id=ASSISTANT_AGENT_ID)
@@ -287,7 +284,7 @@ class TaskRunner:
# STEP 2: Build the conversation workflow
# Creates a cyclic workflow: Orchestrator -> Assistant -> Orchestrator -> User -> Orchestrator...
# The orchestrator acts as a message router that flips roles and routes to appropriate agent
workflow = (
return (
WorkflowBuilder(max_iterations=10000) # Unlimited - we control termination via should_not_stop
.set_start_executor(orchestrator) # Orchestrator manages the conversation flow
.add_edge(orchestrator, self._assistant_executor) # Route messages to assistant
@@ -299,8 +296,6 @@ class TaskRunner:
.build()
)
return workflow
async def run(
self,
task: Task,
@@ -325,7 +320,6 @@ class TaskRunner:
Returns:
Complete conversation history as ChatMessage list for evaluation
"""
logger.info(f"Starting workflow agent for task {task.id}: {task.description.purpose}") # type: ignore[unused-ignore]
logger.info(f"Assistant chat client: {assistant_chat_client}")
logger.info(f"User simulator chat client: {user_simuator_chat_client}")
@@ -390,7 +384,6 @@ class TaskRunner:
Side Effects:
Stores detailed evaluation results in self.full_reward_info
"""
# Handle missing termination reason (can happen with unexpected workflow endings)
if termination_reason is None:
termination_reason = TerminationReason.TOO_MANY_ERRORS
@@ -1,4 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# This makes agent_framework a namespace package
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -1,4 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# This makes agent_framework.lab a namespace package
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
-99
View File
@@ -1,99 +0,0 @@
[project]
name = "agent-framework-lab-tau2"
description = "Tau2 Benchmark for Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
license-files = ["LICENSE"]
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
urls.issues = "https://github.com/microsoft/agent-framework/issues"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"agent-framework",
"pydantic>=2.0.0",
"tiktoken>=0.11.0",
"loguru>=0.7.3",
"tau2@git+https://github.com/sierra-research/tau2-bench@5ba9e3e56db57c5e4114bf7f901291f09b2c5619",
]
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["agent_framework_lab_tau2", "agent_framework.lab.tau2"]
[tool.setuptools.package-dir]
"agent_framework.lab.tau2" = "namespace/agent_framework/lab/tau2"
"agent_framework_lab_tau2" = "agent_framework_lab_tau2"
[tool.setuptools.package-data]
agent_framework_lab_tau2 = ["py.typed"]
[tool.ruff]
line-length = 120
target-version = "py310"
extend-exclude = ["tests", "__pycache__"]
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP", "C4", "N"]
ignore = ["N803", "N806", "N999", "UP007"]
[tool.ruff.format]
quote-style = "double"
[tool.mypy]
python_version = "3.10"
strict = true
check_untyped_defs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true
show_error_codes = true
implicit_reexport = true
packages = ["agent_framework_lab_tau2"]
exclude = [
"data",
]
[tool.pyright]
exclude = ["**/data"]
[tool.poe]
executor.type = "uv"
include = "../../../shared_tasks.toml"
[tool.poe.tasks]
test = "pytest --cov=agent_framework_lab_tau2 --cov-report=term-missing:skip-covered tests"
mypy = "mypy agent_framework_lab_tau2"
setup-data = "python tests/setup_data.py"
purge-data = "python tests/purge_data.py"
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]
addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
env = [
"TAU2_DATA_DIR=data",
]
+107 -68
View File
@@ -8,12 +8,11 @@ import traceback
from datetime import datetime
from typing import Any
from agent_framework.lab.tau2 import TaskRunner, patch_env_set_state
from agent_framework.openai import OpenAIChatClient
from loguru import logger
from tau2.domains.airline.environment import get_tasks
from agent_framework_lab_tau2 import TaskRunner, patch_env_set_state
def to_dumpable(result: dict[str, Any]) -> dict[str, Any]:
"""Convert benchmark result to JSONL-serializable format.
@@ -33,16 +32,15 @@ def to_dumpable(result: dict[str, Any]) -> dict[str, Any]:
"config": result["config"],
"task": result["task"].model_dump(),
}
else:
# Success case: full result structure
return {
"id": result["task"].id,
"evaluation": result["evaluation"].model_dump(), # Detailed evaluation metrics
"config": result["config"], # Model configuration used
"termination_reason": result["termination_reason"].value, # Enum to string
"messages": [m.model_dump() for m in result["messages"]], # Full conversation
"task": result["task"].model_dump(), # Task specification
}
# Success case: full result structure
return {
"id": result["task"].id,
"evaluation": result["evaluation"].model_dump(), # Detailed evaluation metrics
"config": result["config"], # Model configuration used
"termination_reason": result["termination_reason"].value, # Enum to string
"messages": [m.model_dump() for m in result["messages"]], # Full conversation
"task": result["task"].model_dump(), # Task specification
}
async def run_benchmark(assistant_model: str, user_model: str, debug_task_id: str | None, max_steps: int):
@@ -66,15 +64,13 @@ async def run_benchmark(assistant_model: str, user_model: str, debug_task_id: st
Creates timestamped JSONL file with detailed results for analysis
Prints summary statistics to console with colored logging
"""
# STEP 1: Configure output handling based on execution mode
result_fp = None
result_filename = None
if debug_task_id is None:
# Full benchmark mode: create timestamped results file
timestamp = datetime.now().strftime("%m%d%H%M") # Format: MMDDHHMM
result_filename = f"results/{assistant_model}_user-{user_model}_{timestamp}.jsonl"
os.makedirs("results", exist_ok=True)
result_fp = open(result_filename, "a") # Append mode for resumability
logger.info(f"Results will be saved to: {result_filename}")
else:
# Debug mode: single task, no file output, verbose logging
@@ -84,7 +80,7 @@ async def run_benchmark(assistant_model: str, user_model: str, debug_task_id: st
tasks = get_tasks() # Loads all tau2 airline customer service tasks
logger.info(f"Found {len(tasks)} tasks in the dataset")
_logger = logger.opt(colors=True) # Enable colored console output
logger_ = logger.opt(colors=True) # Enable colored console output
# Validate required OpenAI configuration
# Both models use the same endpoint but can be different model types
@@ -121,67 +117,110 @@ async def run_benchmark(assistant_model: str, user_model: str, debug_task_id: st
all_rewards: list[float] = [] # Stores reward scores for final statistics
task_runner = TaskRunner(max_steps=max_steps) # Reusable workflow orchestrator
# STEP 6: Execute benchmark across all tasks
for task in tasks:
_logger.info(f"<red>Testing task #{task.id}</red>")
_logger.info(f"<cyan>Purpose:</cyan> {task.description.purpose}") # type: ignore
# Initialize result structure for this task
result: dict[str, Any] = {
"config": {
"assistant": assistant_chat_client.ai_model_id,
"user": user_simulator_chat_client.ai_model_id,
},
"task": task,
}
# Log user scenario context for transparency
if task.user_scenario and task.user_scenario.instructions:
_logger.info(f"<cyan>User scenario:</cyan> {task.user_scenario.instructions.reason_for_call}") # type: ignore
try:
# Execute the workflow: agent + user simulator conversation
conversation = await task_runner.run(task, assistant_chat_client, user_simulator_chat_client)
# Evaluate performance using tau2's comprehensive metrics
reward_value = task_runner.evaluate(task, conversation, task_runner.termination_reason)
# Store detailed results for analysis
result["evaluation"] = task_runner.full_reward_info # Full evaluation breakdown
result["messages"] = conversation # Complete conversation history
result["termination_reason"] = task_runner.termination_reason # How conversation ended
# Log evaluation results (escape HTML for colored output)
reward_str = str(task_runner.full_reward_info).replace("<", r"\<")
_logger.info(f"<cyan>Final evaluation:</cyan> {reward_str}")
except Exception as e:
# Robust error handling: capture all failures for analysis
_logger.error(f"<red>Error testing task #{task.id}:</red> {e}")
result["error"] = traceback.format_exc() # Full stack trace for debugging
traceback.print_exc() # Console output for immediate debugging
reward_value = 0.0 # Zero score for failed runs
# STEP 7: Persist results incrementally (enables partial analysis)
# STEP 6: Execute benchmark across all tasks with proper file handling
def write_result(result_fp, result):
"""Write result to file if file pointer is provided."""
if result_fp is not None:
result_fp.write(json.dumps(to_dumpable(result), default=str) + "\n")
all_rewards.append(reward_value) # Track for final statistics
# Use context manager for file handling
if result_filename:
with open(result_filename, "a") as result_fp:
for task in tasks:
logger_.info(f"<red>Testing task #{task.id}</red>")
logger_.info(f"<cyan>Purpose:</cyan> {task.description.purpose}") # type: ignore
# Reset runner state for next task
task_runner.reinit()
# Initialize result structure for this task
result: dict[str, Any] = {
"config": {
"assistant": assistant_chat_client.ai_model_id,
"user": user_simulator_chat_client.ai_model_id,
},
"task": task,
}
# STEP 8: Finalize and report aggregate results
if result_fp is not None:
result_fp.close()
# Log user scenario context for transparency
if task.user_scenario and task.user_scenario.instructions:
logger_.info(f"<cyan>User scenario:</cyan> {task.user_scenario.instructions.reason_for_call}") # type: ignore
# Calculate overall benchmark performance
try:
# Execute the workflow: agent + user simulator conversation
conversation = await task_runner.run(task, assistant_chat_client, user_simulator_chat_client)
# Evaluate performance using tau2's comprehensive metrics
reward_value = task_runner.evaluate(task, conversation, task_runner.termination_reason)
# Store detailed results for analysis
result["evaluation"] = task_runner.full_reward_info # Full evaluation breakdown
result["messages"] = conversation # Complete conversation history
result["termination_reason"] = task_runner.termination_reason # How conversation ended
# Log evaluation results (escape HTML for colored output)
reward_str = str(task_runner.full_reward_info).replace("<", r"\<")
logger_.info(f"<cyan>Final evaluation:</cyan> {reward_str}")
except Exception as e:
# Robust error handling: capture all failures for analysis
logger_.error(f"<red>Error testing task #{task.id}:</red> {e}")
result["error"] = traceback.format_exc() # Full stack trace for debugging
traceback.print_exc() # Console output for immediate debugging
reward_value = 0.0 # Zero score for failed runs
# STEP 7: Persist results incrementally (enables partial analysis)
write_result(result_fp, result)
all_rewards.append(reward_value) # Track for final statistics
# Reset runner state for next task
task_runner.reinit()
else:
# Debug mode without file output
for task in tasks:
logger_.info(f"<red>Testing task #{task.id}</red>")
logger_.info(f"<cyan>Purpose:</cyan> {task.description.purpose}") # type: ignore
# Initialize result structure for this task
result: dict[str, Any] = {
"config": {
"assistant": assistant_chat_client.ai_model_id,
"user": user_simulator_chat_client.ai_model_id,
},
"task": task,
}
# Log user scenario context for transparency
if task.user_scenario and task.user_scenario.instructions:
logger_.info(f"<cyan>User scenario:</cyan> {task.user_scenario.instructions.reason_for_call}") # type: ignore
try:
# Execute the workflow: agent + user simulator conversation
conversation = await task_runner.run(task, assistant_chat_client, user_simulator_chat_client)
# Evaluate performance using tau2's comprehensive metrics
reward_value = task_runner.evaluate(task, conversation, task_runner.termination_reason)
# Log evaluation results (escape HTML for colored output)
reward_str = str(task_runner.full_reward_info).replace("<", r"\<")
logger_.info(f"<cyan>Final evaluation:</cyan> {reward_str}")
except Exception as e:
# Robust error handling: capture all failures for analysis
logger_.error(f"<red>Error testing task #{task.id}:</red> {e}")
traceback.print_exc() # Console output for immediate debugging
reward_value = 0.0 # Zero score for failed runs
all_rewards.append(reward_value) # Track for final statistics
# Reset runner state for next task
task_runner.reinit()
# STEP 8: Calculate overall benchmark performance and report final statistics
all_accuracy = sum(all_rewards) / len(all_rewards) if all_rewards else 0.0
# Report final statistics with colored formatting
_logger.info("<green>Final Results:</green>")
_logger.info(f"<cyan>All tasks accuracy:</cyan> {all_accuracy:.2f} ({int(sum(all_rewards))}/{len(tasks)})")
logger_.info("<green>Final Results:</green>")
logger_.info(f"<cyan>All tasks accuracy:</cyan> {all_accuracy:.2f} ({int(sum(all_rewards))}/{len(tasks)})")
if __name__ == "__main__":
@@ -1 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
@@ -2,7 +2,7 @@
from unittest.mock import patch
from agent_framework._types import ChatMessage, Role, TextContent, FunctionCallContent, FunctionResultContent
from agent_framework._types import ChatMessage, FunctionCallContent, FunctionResultContent, Role, TextContent
from agent_framework_lab_tau2._message_utils import flip_messages, log_messages
@@ -120,7 +120,8 @@ def test_flip_messages_mixed_conversation():
flipped = flip_messages(messages)
# Should have: system (unchanged), assistant (from user), user (from assistant, filtered), assistant (from final assistant)
# Should have: system (unchanged), assistant (from user), user (from assistant, filtered),
# assistant (from final assistant)
assert len(flipped) == 4
# Check each flipped message
@@ -2,10 +2,10 @@
"""Tests for sliding window message list."""
import pytest
from unittest.mock import patch
from agent_framework._types import ChatMessage, Role, TextContent, FunctionCallContent, FunctionResultContent
import pytest
from agent_framework._types import ChatMessage, FunctionCallContent, FunctionResultContent, Role, TextContent
from agent_framework_lab_tau2._sliding_window import SlidingWindowChatMessageList
@@ -225,7 +225,8 @@ def test_estimate_any_object_token_count_non_serializable():
async def test_real_world_scenario():
"""Test a realistic conversation scenario."""
sliding_window = SlidingWindowChatMessageList(
max_tokens=30, system_message="You are a helpful assistant" # Moderate limit
max_tokens=30,
system_message="You are a helpful assistant", # Moderate limit
)
# Simulate a conversation
@@ -239,7 +240,8 @@ async def test_real_world_scenario():
role=Role.ASSISTANT,
contents=[
TextContent(
text="I'd be happy to help with weather information, but I don't have access to current weather data."
text="I'd be happy to help with weather information, "
"but I don't have access to current weather data."
)
],
),
@@ -2,19 +2,30 @@
"""Tests for tau2 utils module."""
from typing import Any, cast
from pydantic import BaseModel
import pytest
from agent_framework._tools import AIFunction
from agent_framework._types import ChatMessage, Role, TextContent, FunctionCallContent, FunctionResultContent
from agent_framework._types import ChatMessage, FunctionCallContent, FunctionResultContent, Role, TextContent
from agent_framework_lab_tau2._tau2_utils import (
convert_tau2_tool_to_ai_function,
convert_agent_framework_messages_to_tau2_messages,
convert_tau2_tool_to_ai_function,
)
from tau2.data_model.message import SystemMessage, UserMessage, AssistantMessage, ToolMessage, ToolCall
from tau2.domains.airline.environment import get_environment
from tau2.data_model.message import AssistantMessage, SystemMessage, ToolCall, ToolMessage, UserMessage
# Try to import get_environment and handle missing data files
try:
from tau2.domains.airline.environment import get_environment
# Try to initialize the environment to check if data files are available
try:
get_environment()
TAU2_DATA_AVAILABLE = True
except FileNotFoundError:
TAU2_DATA_AVAILABLE = False
except ImportError:
TAU2_DATA_AVAILABLE = False
@pytest.mark.skipif(not TAU2_DATA_AVAILABLE, reason="tau2 data files not available")
def test_convert_tau2_tool_to_ai_function_basic():
"""Test basic conversion from tau2 tool to AIFunction."""
# Get real tools from tau2 environment
@@ -38,6 +49,7 @@ def test_convert_tau2_tool_to_ai_function_basic():
assert callable(ai_function.func)
@pytest.mark.skipif(not TAU2_DATA_AVAILABLE, reason="tau2 data files not available")
def test_convert_tau2_tool_to_ai_function_multiple_tools():
"""Test conversion with multiple tau2 tools."""
# Get real tools from tau2 environment
@@ -48,7 +60,7 @@ def test_convert_tau2_tool_to_ai_function_multiple_tools():
ai_functions = [convert_tau2_tool_to_ai_function(tool) for tool in tools[:3]] # Test first 3 tools
# Verify all conversions
for ai_function, tau2_tool in zip(ai_functions, tools[:3]):
for ai_function, tau2_tool in zip(ai_functions, tools[:3], strict=False):
assert isinstance(ai_function, AIFunction)
assert ai_function.name == tau2_tool.name
assert ai_function.description == tau2_tool._get_description()
+4 -4
View File
@@ -10,7 +10,7 @@ dependencies = [
"agent-framework-mem0",
"agent-framework-redis",
"agent-framework-devui",
"agent-framework-lab-gaia",
"agent-framework-lab",
]
[dependency-groups]
@@ -49,14 +49,14 @@ environments = [
]
[tool.uv.workspace]
members = [ "packages/*", "packages/lab/*" ]
exclude = [ "packages/agent_framework_project.egg-info", "packages/lab", "packages/lab/cookiecutter-agent-framework-lab", "packages/lab/README.md" ]
members = [ "packages/*" ]
exclude = [ "packages/agent_framework_project.egg-info" ]
[tool.uv.sources]
agent-framework = { workspace = true }
agent-framework-azure-ai = { workspace = true }
agent-framework-copilotstudio = { workspace = true }
agent-framework-lab-gaia = { workspace = true }
agent-framework-lab = { workspace = true }
agent-framework-mem0 = { workspace = true }
agent-framework-redis = { workspace = true }
agent-framework-runtime = { workspace = true }
+72 -87
View File
@@ -27,9 +27,7 @@ members = [
"agent-framework-azure-ai",
"agent-framework-copilotstudio",
"agent-framework-devui",
"agent-framework-lab-gaia",
"agent-framework-lab-lightning",
"agent-framework-lab-tau2",
"agent-framework-lab",
"agent-framework-mem0",
"agent-framework-project",
"agent-framework-redis",
@@ -206,50 +204,28 @@ requires-dist = [
provides-extras = ["dev", "all"]
[[package]]
name = "agent-framework-lab-gaia"
name = "agent-framework-lab"
version = "0.1.0b1"
source = { editable = "packages/lab/gaia" }
source = { editable = "packages/lab" }
dependencies = [
{ name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
]
[package.optional-dependencies]
gaia = [
{ name = "huggingface-hub", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "orjson", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
]
[package.metadata]
requires-dist = [
{ name = "agent-framework", editable = "packages/main" },
{ name = "huggingface-hub", specifier = ">=0.20.0" },
{ name = "opentelemetry-api", specifier = ">=1.24.0" },
{ name = "orjson", specifier = ">=3.8.0" },
{ name = "pydantic", specifier = ">=2.0.0" },
{ name = "tqdm", specifier = ">=4.60.0" },
]
[[package]]
name = "agent-framework-lab-lightning"
version = "0.1.0b1"
source = { editable = "packages/lab/lightning" }
dependencies = [
{ name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
lightning = [
{ name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
]
[package.metadata]
requires-dist = [
{ name = "agent-framework", editable = "packages/main" },
{ name = "pydantic", specifier = ">=2.0.0" },
]
[[package]]
name = "agent-framework-lab-tau2"
version = "0.1.0b1"
source = { editable = "packages/lab/tau2" }
dependencies = [
{ name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
tau2 = [
{ name = "loguru", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
{ name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
{ name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "tau2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "tiktoken", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
@@ -258,11 +234,19 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "agent-framework", editable = "packages/main" },
{ name = "loguru", specifier = ">=0.7.3" },
{ name = "pydantic", specifier = ">=2.0.0" },
{ name = "tau2", git = "https://github.com/sierra-research/tau2-bench?rev=5ba9e3e56db57c5e4114bf7f901291f09b2c5619" },
{ name = "tiktoken", specifier = ">=0.11.0" },
{ name = "huggingface-hub", marker = "extra == 'gaia'", specifier = ">=0.20.0" },
{ name = "loguru", marker = "extra == 'tau2'", specifier = ">=0.7.3" },
{ name = "numpy", marker = "extra == 'tau2'" },
{ name = "opentelemetry-api", marker = "extra == 'gaia'", specifier = ">=1.24.0" },
{ name = "orjson", marker = "extra == 'gaia'", specifier = ">=3.8.0" },
{ name = "pydantic", marker = "extra == 'gaia'", specifier = ">=2.0.0" },
{ name = "pydantic", marker = "extra == 'lightning'", specifier = ">=2.0.0" },
{ name = "pydantic", marker = "extra == 'tau2'", specifier = ">=2.0.0" },
{ name = "tau2", marker = "extra == 'tau2'", git = "https://github.com/sierra-research/tau2-bench?rev=5ba9e3e56db57c5e4114bf7f901291f09b2c5619" },
{ name = "tiktoken", marker = "extra == 'tau2'", specifier = ">=0.11.0" },
{ name = "tqdm", marker = "extra == 'gaia'", specifier = ">=4.60.0" },
]
provides-extras = ["gaia", "lightning", "tau2"]
[[package]]
name = "agent-framework-mem0"
@@ -288,7 +272,7 @@ dependencies = [
{ name = "agent-framework-azure-ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "agent-framework-copilotstudio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "agent-framework-devui", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "agent-framework-lab-gaia", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "agent-framework-lab", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "agent-framework-mem0", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "agent-framework-redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
]
@@ -323,7 +307,7 @@ requires-dist = [
{ name = "agent-framework-azure-ai", editable = "packages/azure-ai" },
{ name = "agent-framework-copilotstudio", editable = "packages/copilotstudio" },
{ name = "agent-framework-devui", editable = "packages/devui" },
{ name = "agent-framework-lab-gaia", editable = "packages/lab/gaia" },
{ name = "agent-framework-lab", editable = "packages/lab" },
{ name = "agent-framework-mem0", editable = "packages/mem0" },
{ name = "agent-framework-redis", editable = "packages/redis" },
]
@@ -1338,7 +1322,7 @@ name = "exceptiongroup"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" },
{ name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
wheels = [
@@ -1370,53 +1354,54 @@ wheels = [
[[package]]
name = "fastuuid"
version = "0.13.3"
version = "0.13.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/15/80/3c16a1edad2e6cd82fbd15ac998cc1b881f478bf1f80ca717d941c441874/fastuuid-0.13.5.tar.gz", hash = "sha256:d4976821ab424d41542e1ea39bc828a9d454c3f8a04067c06fca123c5b95a1a1", size = 18255, upload-time = "2025-09-26T09:05:38.281Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/85/08371c62cd86a4826f4bf90b885784475b232ac2512d45f0193592fbae46/fastuuid-0.13.3-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a445d61106c0bbca80630d00de3d17025da6542da7f95adfa90fd5ac7641d1f8", size = 494073, upload-time = "2025-09-25T17:13:53.822Z" },
{ url = "https://files.pythonhosted.org/packages/e1/b0/0be84b80bd1e9febf4cd06358b46ce536ffc3e426ba2a4d7da04d6b66ca2/fastuuid-0.13.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ce1a5f3cfec829f326dd9278d90448e62fa531802349f64fe159ab17cedda1d", size = 252783, upload-time = "2025-09-25T17:12:30.108Z" },
{ url = "https://files.pythonhosted.org/packages/22/b1/f6ce4cf41effb67696c91e0e30aa7e3ef81a4f15ece23875ace121eabe9d/fastuuid-0.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2cec2d49df6c2f5ae4adc680dd674664d788d8d48247fb23118ef8775e020ed3", size = 244261, upload-time = "2025-09-25T17:13:13.445Z" },
{ url = "https://files.pythonhosted.org/packages/83/f7/f2617df3e48d3c5eabb1ee335169831d008790bae0e41bc44075a2c141c2/fastuuid-0.13.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a36eef2b7f63521b073475a1b84672b539204090b0f85ffce52a7fce648aad7", size = 271651, upload-time = "2025-09-25T17:13:49.759Z" },
{ url = "https://files.pythonhosted.org/packages/b4/cf/902cc47c8088830b37a2075898a71b94ddfd77735727e08693053cd3bfa2/fastuuid-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b27970d859e2f2bf04d17d505c3337dfe8e74b94ddf75b742a4e4ab8dbc5a0f", size = 272301, upload-time = "2025-09-25T17:10:47.738Z" },
{ url = "https://files.pythonhosted.org/packages/98/32/f5d7bc98baa2e5011301e86a481a392812b6b48724ca549a6479a30e79e7/fastuuid-0.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4e6ebe585f1fca4d2dd62e7fb6eaba9a657a554b5cde682213d93ba657478922", size = 291024, upload-time = "2025-09-25T17:11:08.746Z" },
{ url = "https://files.pythonhosted.org/packages/6b/bc/44973d159d70d38bf80e6506365dc7bc897ade431035b7162eeb04ffa79b/fastuuid-0.13.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:415696c6a268967a66e4dd0ef8db490b126f79001cde05dd87a62613e5ebc72b", size = 453067, upload-time = "2025-09-25T17:10:39.656Z" },
{ url = "https://files.pythonhosted.org/packages/db/66/7e561dc8ee1b1b76f360512bee3ed652d5c6fb0e5a662ebb4d66edc8988c/fastuuid-0.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:133a633bfa040f1b35b0d2470dc07c87d7e992857414fe455395040c5bd1d46f", size = 468462, upload-time = "2025-09-25T17:12:22.629Z" },
{ url = "https://files.pythonhosted.org/packages/2a/08/f5d63e07f53c612c78b39159928c7e8f2e616969f4e4f921b7c72eeb41b7/fastuuid-0.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6013aec0a317ccc299b826fa7c4a26938f1c663148d479f1adba546a62cdc320", size = 444980, upload-time = "2025-09-25T17:10:49.865Z" },
{ url = "https://files.pythonhosted.org/packages/e5/e2/31adfe18e9f889d81415aea428174bcea9da1addf565cddc094f30ccdfbd/fastuuid-0.13.3-cp310-cp310-win32.whl", hash = "sha256:4b09c5684904e222a6f15006862999e2a94a6e5c754e60f5dccf316aa758cbdf", size = 144941, upload-time = "2025-09-25T17:12:15.184Z" },
{ url = "https://files.pythonhosted.org/packages/7a/9a/9bc7ee4833031dd0a483fee85c5a3d0361986acb03c5d17f9ff0815c0da9/fastuuid-0.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:87377b41895ab467dfa78cc0044af21ee2f7f956e0697cfddd79edf2b06d21c4", size = 150708, upload-time = "2025-09-25T17:12:36.003Z" },
{ url = "https://files.pythonhosted.org/packages/c6/52/4fbba2812dd3601886dc7e3deb922b7c46807b9a820517bc5f459431e745/fastuuid-0.13.3-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9a9f50d397d59467448eac8949d2be500634f77b1b96f3cc07f7d1084e82e794", size = 493949, upload-time = "2025-09-25T17:15:08.369Z" },
{ url = "https://files.pythonhosted.org/packages/5d/46/e96705e86273ce9d0e45557ca2ab21190b86b01ff4ed3fdd5c6b97058c2f/fastuuid-0.13.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6de517642c15c67daf36bc20d0b48d9ef1495e6035a9e78ec83ea1ff603353c1", size = 252638, upload-time = "2025-09-25T17:17:14.938Z" },
{ url = "https://files.pythonhosted.org/packages/c5/6e/d473a7e44908addb5abbaa65161a0e43c7f0ca1ebec5ff5dcfc0096a0c14/fastuuid-0.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8dfc15c727ddbd170e7a11faa3668ca4cb5a61980c50cc2d7e6f49a2a61036bf", size = 244266, upload-time = "2025-09-25T17:12:05.832Z" },
{ url = "https://files.pythonhosted.org/packages/2f/fb/64396531447deca86989a5231013e8deed5449df57bd178140a6f390adfc/fastuuid-0.13.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6469267b7944d73581db1781385879d23457b706db4cdd13b90516bab906b2", size = 271490, upload-time = "2025-09-25T17:12:35.724Z" },
{ url = "https://files.pythonhosted.org/packages/09/1b/e3cdc12e7ba53827c4a9900ed6d55146687824b13d4977399de8bc1d37e2/fastuuid-0.13.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d22455bbd32de6f538e7d589e9ee2e85632dea474f3f4ece0af4142dbb8ef5", size = 272151, upload-time = "2025-09-25T17:13:37.296Z" },
{ url = "https://files.pythonhosted.org/packages/04/b2/0916a6f3651c7d4601114b2095d71ece51ae69dd71943d596957f4e84dc3/fastuuid-0.13.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7a87bca69ea721d87af5d3877553a8ea1da54ecc3a906d7fac2991e8781d80ae", size = 290967, upload-time = "2025-09-25T17:11:07.344Z" },
{ url = "https://files.pythonhosted.org/packages/39/68/c7a3701eb9876899dc8b9a435fb8c88bc6586b5492a3bbdf4ecdb5025b91/fastuuid-0.13.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ee41bd445c20771347daa230fc059557db25e0cf2b392c2f0f0767afa775b90e", size = 452924, upload-time = "2025-09-25T17:12:15.521Z" },
{ url = "https://files.pythonhosted.org/packages/8d/90/e3c04cfbb245b9c9997db4c526f794203784607222c3ff343b8d358178f6/fastuuid-0.13.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d357eae16f0aea47241cfe5d1a6d82426cd28d86fbbd86b86a36d84a8a75075", size = 468318, upload-time = "2025-09-25T17:13:51.847Z" },
{ url = "https://files.pythonhosted.org/packages/88/e3/35f225a1e239cb9e1017e3e0dedef8f6a7f65eb4d99ae2f10cbe5501177d/fastuuid-0.13.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:44e4d72b6cbafe11c0522815257ce1ea5f737a23d14991d5667a7f14854c6f6b", size = 444868, upload-time = "2025-09-25T17:12:42.825Z" },
{ url = "https://files.pythonhosted.org/packages/c0/46/68259e2a07fe353778826f1fc3f87e2c0550174861d9d963a5dbc3f67b4f/fastuuid-0.13.3-cp311-cp311-win32.whl", hash = "sha256:d96c35e388054adc71ecaf17b7041996fdd45ae5a9a5b40dbe0549ed0bd76e99", size = 144856, upload-time = "2025-09-25T17:15:37.739Z" },
{ url = "https://files.pythonhosted.org/packages/b2/c2/71c117f675c156d2cf87be6f2d6363bc117ac48cb59e38b9b791b997fbee/fastuuid-0.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:761eb49d46ac9556d8dcbed216d28ff19cd4b7ec6d9723735439d016e19977dc", size = 150527, upload-time = "2025-09-25T17:13:07.426Z" },
{ url = "https://files.pythonhosted.org/packages/75/b0/7e1a7f2410aafc381c5df31f402dbc5255bec079cec403de1d24539b27a6/fastuuid-0.13.3-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:acdb9e11324180b82732d1c683bf97d36da0ce0fc34582b0330022fba3120c06", size = 494620, upload-time = "2025-09-25T17:17:14.021Z" },
{ url = "https://files.pythonhosted.org/packages/8d/4d/ecb8e82dda6bb42f5a6fb06ebdeb10f02f7e8da6d70cc4c031fa149b703c/fastuuid-0.13.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7712996e7b01c5034487eebec9bbc1307c5936d414653ba5cb4a77a601dce3d8", size = 253086, upload-time = "2025-09-25T17:12:41.237Z" },
{ url = "https://files.pythonhosted.org/packages/2b/7e/9164f9a93b6518042805d2a8646d052420795c84210ac5647b060254be06/fastuuid-0.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41bc3b9e7fdf4c756f1fb29a5be12353630cfcef3919ee6483c573869cdb30fd", size = 244517, upload-time = "2025-09-25T17:10:27.669Z" },
{ url = "https://files.pythonhosted.org/packages/79/ed/d591956acb1132e9be464393e806b8714d96676f5d855b16681b917ee19d/fastuuid-0.13.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef61a6ba6b86de2ef03842817cc7bd9bdddd1ff7db60b6e2e47d6e45057e01c", size = 271530, upload-time = "2025-09-25T17:11:09.067Z" },
{ url = "https://files.pythonhosted.org/packages/dc/68/51682716872f4454ed6f24530ff4e9759cd15021b48cd513016725a16c4a/fastuuid-0.13.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43f782974fe3d0c6637d5bb312409e7f52b27ae54ef690238c796875718932ea", size = 272306, upload-time = "2025-09-25T17:10:45.482Z" },
{ url = "https://files.pythonhosted.org/packages/87/6a/4f45e5ef70f78b17761e22a2477d7525bb85f87c98f188bd755a74284c65/fastuuid-0.13.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc53993b41817c522a9b8700bcadfdd3db6c7858eaf51b2f9d5254bed0075d08", size = 290557, upload-time = "2025-09-25T17:19:39.373Z" },
{ url = "https://files.pythonhosted.org/packages/0a/e5/3f3def87fb7f7b93ba01107f3e5e0fae9a2b2aec5b3ee933e1fbb1d102e6/fastuuid-0.13.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4f364b9e39a3f056f2b70eb066f1066daf5e73fd6d88fd42d70bc113c76b969b", size = 452803, upload-time = "2025-09-25T17:12:12.13Z" },
{ url = "https://files.pythonhosted.org/packages/01/df/496d13bf593a06aab4efdab7064460e674979f1b5556389c65df2807dae1/fastuuid-0.13.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b7bb143762fe851c4065826e6e835e2461879eb030aacc4ee397bdcbadbfa73d", size = 468133, upload-time = "2025-09-25T17:10:47.457Z" },
{ url = "https://files.pythonhosted.org/packages/4c/8f/36f08d313201f4e8fc857e55a6ef5d5c81b2f2058380d91521fbf4803aaa/fastuuid-0.13.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff6ba513a728dc6ae80367acbcb92d4c388aee9db552d8c326df2e490ba0cabc", size = 444939, upload-time = "2025-09-25T17:18:21.415Z" },
{ url = "https://files.pythonhosted.org/packages/7c/21/7312862b8a4326e5e2b419a9c7e1a9e8712d4750ac9ca57c9d03879aaac5/fastuuid-0.13.3-cp312-cp312-win32.whl", hash = "sha256:48046ba0dbecc07a35c9f7ef5d78443d6eea39fcf9f6dc2957da4b4f753f6175", size = 145419, upload-time = "2025-09-25T17:17:14.105Z" },
{ url = "https://files.pythonhosted.org/packages/2a/dd/f60bee16a60a711659e27bcb5499b13601d57c779725e6168a9e0586eee2/fastuuid-0.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:fbb6f8273827b96d221c5973a7ef9d431f8e082554a16a17e51362fcf6f3c982", size = 150823, upload-time = "2025-09-25T17:19:43.188Z" },
{ url = "https://files.pythonhosted.org/packages/73/41/9482a375d3af33e2cdb99d3fa1bbbdb95f2e698ceb29e38880f81d919ebe/fastuuid-0.13.3-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e5be92899120006ed44b263c02588d38632b49aa1fb2a8fcd18bb5b93a1fa7f2", size = 494635, upload-time = "2025-09-25T17:18:44.961Z" },
{ url = "https://files.pythonhosted.org/packages/46/bf/50530bb9bcc505ea74c06ac376af44c2b4e085a897b2d4fb168729267418/fastuuid-0.13.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:217c8438e4b2d727c810ff4c49123e45f2109925b04463745e382c7d808472fa", size = 253079, upload-time = "2025-09-25T17:18:19.086Z" },
{ url = "https://files.pythonhosted.org/packages/0c/11/cb674d840ab86f3486cca060b93401b51a7b45eca81c269288d2efb98928/fastuuid-0.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1bffec9780ecab477f6d003dfae917720bb1485f44d15a469974e78be67d13d3", size = 244547, upload-time = "2025-09-25T17:15:05.277Z" },
{ url = "https://files.pythonhosted.org/packages/21/fd/9302cde221ec2e33f2284263e3f75758334f5cf6c93be2dcaee8ca5d717e/fastuuid-0.13.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa4744abf7203567ccc8e79c5c8585922c79a1b7d5a3bbbd7f3cb688e87192c9", size = 271469, upload-time = "2025-09-25T17:18:10.156Z" },
{ url = "https://files.pythonhosted.org/packages/dd/f8/de9a6bc73fbd43cdc7419e1b791f2c6a1300d3638db07ba2380bf6addaf4/fastuuid-0.13.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd73098a9057f109f59cf28752b18519dd2a8c708741e26154d4c208cef9ed01", size = 272278, upload-time = "2025-09-25T17:12:26.19Z" },
{ url = "https://files.pythonhosted.org/packages/05/99/93af7a69451918ecb434a90e8e373dccc1d6bdae030bd67c24bde87df463/fastuuid-0.13.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9927b96ff5982ef18a4dccbcd1fee677faaa486b86a9f48c235f2e969b0b3956", size = 290406, upload-time = "2025-09-25T17:18:12.611Z" },
{ url = "https://files.pythonhosted.org/packages/9f/4e/39b459085cd90cbb3c2a483fdbdeed0e4edd67c1ff036705a8486abfd29e/fastuuid-0.13.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2a64486c5e52f132fb225c248c4c9bd174946514a38ab1b293595ee9bd16bb6d", size = 452823, upload-time = "2025-09-25T17:10:37.088Z" },
{ url = "https://files.pythonhosted.org/packages/fa/b6/289aefee8cbfc8f0afaf2e9462be703a062f2686901cf25cc65ad71eba3b/fastuuid-0.13.3-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f4e9c4552e3ffaec33f8af09067dcafd435b1188a68d86cc26aef50a4b200de6", size = 468092, upload-time = "2025-09-25T17:19:11.065Z" },
{ url = "https://files.pythonhosted.org/packages/fe/1f/5994c4d3a298b2116417f49abb1893950600087dbbc3839016347679c9ba/fastuuid-0.13.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:4817671973467d8c32bdc1d9dae9ad2727664ac1363dbe94e42e0a890fbc521d", size = 444968, upload-time = "2025-09-25T17:19:24.896Z" },
{ url = "https://files.pythonhosted.org/packages/89/82/31cefe573bc19f2d1b07b86af9b9db8d0a74487685dfbaf16edf4ec91055/fastuuid-0.13.3-cp313-cp313-win32.whl", hash = "sha256:ad55c13069711c3fb30c7080d117516ee7a419f64f073212c2e9a9536051b743", size = 145463, upload-time = "2025-09-25T17:17:15.705Z" },
{ url = "https://files.pythonhosted.org/packages/f9/49/3d41776751d207526c8917d8e6ec35290f78be396f8c05102c32c0d63558/fastuuid-0.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:ffe6a759e15e0968d2022d6a07957bc1f9830127b2d1bef2ee56d4e21c5a90b6", size = 150918, upload-time = "2025-09-25T17:15:56.942Z" },
{ url = "https://files.pythonhosted.org/packages/f0/17/f8ed7f707c1bf994ff4e38f163b367cc2060f13a8aa60b03a3c821daaf0f/fastuuid-0.13.5-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b9edf8ee30718aee787cdd2e9e1ff3d4a3ec6ddb32fba0a23fa04956df69ab07", size = 494134, upload-time = "2025-09-26T09:14:35.852Z" },
{ url = "https://files.pythonhosted.org/packages/18/de/b03e4a083a307fb5a2c8afcfbcc6ab45578fba7996f69f329e35d18e0e67/fastuuid-0.13.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f67ea1e25c5e782f7fb5aaa5208f157d950401dd9321ce56bcc6d4dc3d72ed60", size = 252832, upload-time = "2025-09-26T09:10:21.321Z" },
{ url = "https://files.pythonhosted.org/packages/62/65/3a8be5ce86e2a1eb3947be32512b62fcb0a360a998ba2405cd3e54e54f04/fastuuid-0.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ff3fc87e1f19603dd53c38f42c2ea8d5d5462554deab69e9cf1800574e4756c", size = 244309, upload-time = "2025-09-26T09:09:08.333Z" },
{ url = "https://files.pythonhosted.org/packages/ab/eb/7b9c98d25a810fcc5f4a3e10e1e051c18e10cdad4527242e18c998fab4b1/fastuuid-0.13.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6e5337fa7698dc52bc724da7e9239e93c5b24a09f6904b8660dfb8c41ce3dee", size = 271629, upload-time = "2025-09-26T09:13:37.525Z" },
{ url = "https://files.pythonhosted.org/packages/c0/37/6331f626852c2aeea8d666af049b1337e273d11e700a26333c402d0e7a94/fastuuid-0.13.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9db596023c10dabb12489a88c51b75297c3a2478cb2be645e06905934e7b9fc", size = 272312, upload-time = "2025-09-26T09:13:05.252Z" },
{ url = "https://files.pythonhosted.org/packages/ad/d3/e4d3f3c2968689e17d5c73bd0da808d1673329d5ff3b4065db03d58f36e3/fastuuid-0.13.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:191ff6192fe53c5fc9d4d241ee1156b30a7ed6f1677b1cc2423e7ecdbc26222b", size = 291049, upload-time = "2025-09-26T09:13:31.817Z" },
{ url = "https://files.pythonhosted.org/packages/4f/4e/f27539c9b15b1947ba50907b1a83bbe905363770472c0a1c3175fb2a0ebf/fastuuid-0.13.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:348ce9f296dda701ba46d8dceeff309f90dbc75dd85080bbed2b299aa908890a", size = 453074, upload-time = "2025-09-26T09:11:42.674Z" },
{ url = "https://files.pythonhosted.org/packages/6b/5c/57cba66a8f04cd26d3118b21393a0dda221cb82ac992b9fe153b69a22a0a/fastuuid-0.13.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:46954fb644995d7fc8bbd710fbd4c65cedaa48c921c86fdbafef0229168a8c96", size = 468531, upload-time = "2025-09-26T09:10:30.626Z" },
{ url = "https://files.pythonhosted.org/packages/dc/90/dbc19dc18282b3c2264554c595901b520224efe65907c5ff5595e688ab28/fastuuid-0.13.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22da0f66041e1c10c7d465b495cc6cd8e17e080dda34b4bd5ff5240b860fbb82", size = 444933, upload-time = "2025-09-26T09:09:33.405Z" },
{ url = "https://files.pythonhosted.org/packages/5b/03/4652cc314fc5163db12bc451512b087e5b5e4f36ba513f111fd5a5ff1c07/fastuuid-0.13.5-cp310-cp310-win32.whl", hash = "sha256:3e6b548f06c1ed7bad951a17a09eef69d6f24eb2b874cb4833e26b886d82990f", size = 144981, upload-time = "2025-09-26T09:08:14.812Z" },
{ url = "https://files.pythonhosted.org/packages/8f/0b/85b3a68418911923acb8955219ab33ac728eaa9337ef0135b9e5c9d1ed9d/fastuuid-0.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:c82838e52189d16b1307631179cb2cd37778dd8f4ddc00e9ce3c26f920b3b2f7", size = 150741, upload-time = "2025-09-26T09:09:00.161Z" },
{ url = "https://files.pythonhosted.org/packages/04/ab/9351bfc04ff2144115758233130b5469993d3d379323903a4634cb9c78c1/fastuuid-0.13.5-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c122558ca4b5487e2bd0863467e4ccfe636afd1274803741487d48f2e32ea0e1", size = 493910, upload-time = "2025-09-26T09:12:36.995Z" },
{ url = "https://files.pythonhosted.org/packages/b7/ab/84fac529cc12a03d49595e70ac459380f7cb12c70f0fe401781b276f9e94/fastuuid-0.13.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d7abd42a03a17a681abddd19aa4d44ca2747138cf8a48373b395cf1341a10de2", size = 252621, upload-time = "2025-09-26T09:12:22.222Z" },
{ url = "https://files.pythonhosted.org/packages/7f/9d/f4c734d7b74a04ca695781c58a1376f07b206fe2849e58e7778d476a0e94/fastuuid-0.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2705cf7c2d6f7c03053404b75a4c44f872a73f6f9d5ea34f1dc6bba400c4a97c", size = 244269, upload-time = "2025-09-26T09:08:31.921Z" },
{ url = "https://files.pythonhosted.org/packages/5b/da/b42b7eb84523d69cfe9dac82950e105061c8d59f4d4d2cc3e170dbd20937/fastuuid-0.13.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d220a056fcbad25932c1f25304261198612f271f4d150b2a84e81adb877daf7", size = 271528, upload-time = "2025-09-26T09:12:42.718Z" },
{ url = "https://files.pythonhosted.org/packages/1b/45/6eee36929119e9544b0906fd6591e685d682e4b51cfad4c25d96ccf04009/fastuuid-0.13.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f29f93b5a0c5f5579f97f77d5319e9bfefd61d8678ec59d850201544faf33bf", size = 272168, upload-time = "2025-09-26T09:07:04.238Z" },
{ url = "https://files.pythonhosted.org/packages/ce/ac/75b70f13515e12194a25b0459dd8a8a33de4ab0a92142f0776d21e41ca84/fastuuid-0.13.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:399d86623fb806151b1feb9fdd818ebfc1d50387199a35f7264f98dfc1540af5", size = 290948, upload-time = "2025-09-26T09:07:53.433Z" },
{ url = "https://files.pythonhosted.org/packages/76/30/1801326a5b433aafc04eae906e6b005e8a3d1120fd996409fe88124edb06/fastuuid-0.13.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:689e8795a1edd573b2c9a455024e4edf605a9690339bba29709857f7180894ea", size = 452932, upload-time = "2025-09-26T09:09:28.017Z" },
{ url = "https://files.pythonhosted.org/packages/61/2a/080b6b2ac4ef2ead54a7463ae4162d66a52867bbd4447ad5354427b82ae2/fastuuid-0.13.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:25e82c4a1734da168b36f7308e397afbe9c9b353799a9c69563a605f11dd4641", size = 468384, upload-time = "2025-09-26T09:08:14.32Z" },
{ url = "https://files.pythonhosted.org/packages/b6/d3/4a3ffcaf8d874f7f208dad7e98ded7c5359b6599073960e3aa0530ca6139/fastuuid-0.13.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f62299e3cca69aad6a6fb37e26e45055587954d498ad98903fea24382377ea0e", size = 444815, upload-time = "2025-09-26T09:06:38.691Z" },
{ url = "https://files.pythonhosted.org/packages/9d/a0/08dd8663f7bff3e9c0b2416708b01d1fb65f52bcd4bce18760f77c4735fd/fastuuid-0.13.5-cp311-cp311-win32.whl", hash = "sha256:68227f2230381b89fb1ad362ca6e433de85c6c11c36312b41757cad47b8a8e32", size = 144897, upload-time = "2025-09-26T09:14:53.695Z" },
{ url = "https://files.pythonhosted.org/packages/9e/e2/2c2a37dcc56e2323c6214c38c8faac22f9d03d98c481f8a40843e0b9526a/fastuuid-0.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:4a32306982bd031cb20d5d1a726b7b958a55babebd2300ce6c8e352d3496e931", size = 150523, upload-time = "2025-09-26T09:12:24.031Z" },
{ url = "https://files.pythonhosted.org/packages/21/36/434f137c5970cac19e57834e1f7680e85301619d49891618c00666700c61/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35fe8045e866bc6846f8de6fa05acb1de0c32478048484a995e96d31e21dff2a", size = 494638, upload-time = "2025-09-26T09:14:58.695Z" },
{ url = "https://files.pythonhosted.org/packages/ca/3c/083de2ac007b2b305523b9c006dba5051e5afd87a626ef1a39f76e2c6b82/fastuuid-0.13.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:02a460333f52d731a006d18a52ef6fcb2d295a1f5b1a5938d30744191b2f77b7", size = 253138, upload-time = "2025-09-26T09:13:33.283Z" },
{ url = "https://files.pythonhosted.org/packages/73/5e/630cffa1c8775db526e39e9e4c5c7db0c27be0786bb21ba82c912ae19f63/fastuuid-0.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:74b0e4f8c307b9f477a5d7284db4431ce53a3c1e3f4173db7a97db18564a6202", size = 244521, upload-time = "2025-09-26T09:14:40.682Z" },
{ url = "https://files.pythonhosted.org/packages/4d/51/55d78705f4fbdadf88fb40f382f508d6c7a4941ceddd7825fafebb4cc778/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6955a99ef455c2986f3851f4e0ccc35dec56ac1a7720f2b92e88a75d6684512e", size = 271557, upload-time = "2025-09-26T09:15:09.75Z" },
{ url = "https://files.pythonhosted.org/packages/6a/2b/1b89e90a8635e5587ccdbbeb169c590672ce7637880f2c047482a0359950/fastuuid-0.13.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10c77b826738c1a27dcdaa92ea4dc1ec9d869748a99e1fde54f1379553d4854", size = 272334, upload-time = "2025-09-26T09:07:48.865Z" },
{ url = "https://files.pythonhosted.org/packages/0c/06/4c8207894eeb30414999e5c3f66ac039bc4003437eb4060d8a1bceb4cc6f/fastuuid-0.13.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb25dccbeb249d16d5e664f65f17ebec05136821d5ef462c4110e3f76b86fb86", size = 290594, upload-time = "2025-09-26T09:12:54.124Z" },
{ url = "https://files.pythonhosted.org/packages/50/69/96d221931a31d77a47cc2487bdfacfb3091edfc2e7a04b1795df1aec05df/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5becc646a3eeafb76ce0a6783ba190cd182e3790a8b2c78ca9db2b5e87af952", size = 452835, upload-time = "2025-09-26T09:14:00.994Z" },
{ url = "https://files.pythonhosted.org/packages/25/ef/bf045f0a47dcec96247497ef3f7a31d86ebc074330e2dccc34b8dbc0468a/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:69b34363752d06e9bb0dbdf02ae391ec56ac948c6f2eb00be90dad68e80774b9", size = 468225, upload-time = "2025-09-26T09:13:38.585Z" },
{ url = "https://files.pythonhosted.org/packages/30/46/4817ab5a3778927155a4bde92540d4c4fa996161ec8b8e080c8928b0984e/fastuuid-0.13.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57d0768afcad0eab8770c9b8cf904716bd3c547e8b9a4e755ee8a673b060a3a3", size = 444907, upload-time = "2025-09-26T09:14:30.163Z" },
{ url = "https://files.pythonhosted.org/packages/80/27/ab284117ce4dc9b356a7196bdbf220510285f201d27f1f078592cdc8187b/fastuuid-0.13.5-cp312-cp312-win32.whl", hash = "sha256:8ac6c6f5129d52eaa6ef9ea4b6e2f7c69468a053f3ab8e439661186b9c06bb85", size = 145415, upload-time = "2025-09-26T09:08:59.494Z" },
{ url = "https://files.pythonhosted.org/packages/f4/0c/f970a4222773b248931819f8940800b760283216ca3dda173ed027e94bdd/fastuuid-0.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:ad630e97715beefef07ec37c9c162336e500400774e2c1cbe1a0df6f80d15b9a", size = 150840, upload-time = "2025-09-26T09:13:46.115Z" },
{ url = "https://files.pythonhosted.org/packages/4f/62/74fc53f6e04a4dc5b36c34e4e679f85a4c14eec800dcdb0f2c14b5442217/fastuuid-0.13.5-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ea17dfd35e0e91920a35d91e65e5f9c9d1985db55ac4ff2f1667a0f61189cefa", size = 494678, upload-time = "2025-09-26T09:14:30.908Z" },
{ url = "https://files.pythonhosted.org/packages/09/ba/f28b9b7045738a8bfccfb9cd6aff4b91fce2669e6b383a48b0694ee9b3ff/fastuuid-0.13.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:be6ad91e5fefbcc2a4b478858a2715e386d405834ea3ae337c3b6b95cc0e47d6", size = 253162, upload-time = "2025-09-26T09:13:35.879Z" },
{ url = "https://files.pythonhosted.org/packages/b1/18/13fac89cb4c9f0cd7e81a9154a77ecebcc95d2b03477aa91d4d50f7227ee/fastuuid-0.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ea6df13a306aab3e0439d58c312ff1e6f4f07f09f667579679239b4a6121f64a", size = 244546, upload-time = "2025-09-26T09:14:58.13Z" },
{ url = "https://files.pythonhosted.org/packages/04/bf/9691167804d59411cc4269841df949f6dd5e76452ab10dcfcd1dbe04c5bc/fastuuid-0.13.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2354c1996d3cf12dc2ba3752e2c4d6edc46e1a38c63893146777b1939f3062d4", size = 271528, upload-time = "2025-09-26T09:14:48.996Z" },
{ url = "https://files.pythonhosted.org/packages/a9/b5/7a75a03d1c7aa0b6d573032fcca39391f0aef7f2caabeeb45a672bc0bd3c/fastuuid-0.13.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6cf9b7469fc26d1f9b1c43ac4b192e219e85b88fdf81d71aa755a6c08c8a817", size = 272292, upload-time = "2025-09-26T09:14:42.82Z" },
{ url = "https://files.pythonhosted.org/packages/c0/db/fa0f16cbf76e6880599533af4ef01bb586949c5320612e9d884eff13e603/fastuuid-0.13.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92ba539170097b9047551375f1ca09d8d2b4aefcc79eeae3e1c43fe49b42072e", size = 290466, upload-time = "2025-09-26T09:08:33.161Z" },
{ url = "https://files.pythonhosted.org/packages/1e/02/6b8c45bfbc8500994dd94edba7f59555f9683c4d8c9a164ae1d25d03c7c7/fastuuid-0.13.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:dbb81d05617bc2970765c1ad82db7e8716f6a2b7a361a14b83de5b9240ade448", size = 452838, upload-time = "2025-09-26T09:13:44.747Z" },
{ url = "https://files.pythonhosted.org/packages/27/12/85d95a84f265b888e8eb9f9e2b5aaf331e8be60c0a7060146364b3544b6a/fastuuid-0.13.5-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:d973bd6bf9d754d3cca874714ac0a6b22a47f239fb3d3c8687569db05aac3471", size = 468149, upload-time = "2025-09-26T09:13:18.712Z" },
{ url = "https://files.pythonhosted.org/packages/ad/da/dd9a137e9ea707e883c92470113a432233482ec9ad3e9b99c4defc4904e6/fastuuid-0.13.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e725ceef79486423f05ee657634d4b4c1ca5fb2c8a94e0708f5d6356a83f2a83", size = 444933, upload-time = "2025-09-26T09:14:09.494Z" },
{ url = "https://files.pythonhosted.org/packages/12/f4/ab363d7f4ac3989691e2dc5ae2d8391cfb0b4169e52ef7fa0ac363e936f0/fastuuid-0.13.5-cp313-cp313-win32.whl", hash = "sha256:a1c430a332ead0b2674f1ef71b17f43b8139ec5a4201182766a21f131a31e021", size = 145462, upload-time = "2025-09-26T09:14:15.105Z" },
{ url = "https://files.pythonhosted.org/packages/aa/8a/52eb77d9c294a54caa0d2d8cc9f906207aa6d916a22de963687ab6db8b86/fastuuid-0.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:241fdd362fd96e6b337db62a65dd7cb3dfac20adf854573247a47510e192db6f", size = 150923, upload-time = "2025-09-26T09:13:03.923Z" },
]
[[package]]