mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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:
committed by
GitHub
Unverified
parent
9fec0f5ef4
commit
514d0209a8
@@ -0,0 +1 @@
|
||||
test-results.xml
|
||||
+70
-116
@@ -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"
|
||||
]
|
||||
}
|
||||
-56
@@ -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.
|
||||
-95
@@ -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",
|
||||
]
|
||||
-4
@@ -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
|
||||
-21
@@ -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
@@ -1 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
-15
@@ -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 }}"
|
||||
@@ -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.
|
||||
@@ -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)})")
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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.
|
||||
@@ -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__)
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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()
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Generated
+72
-87
@@ -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]]
|
||||
|
||||
Reference in New Issue
Block a user