Python: api doc generation setup (#342)

* api doc generation setup

* remove old log file

* improved check md function

* update with sample code in docstring

* updated script

* docs update

* docs update and action

* removed all-extras

* fixed sync command

* moved install

* moved action

* renamed folder

* fixed syntax

* add python path

* fix mypy and reused steps

* updated merge test

* undo change

* slight update in poe commands

* dev setup update

* updated uvlock
This commit is contained in:
Eduard van Valkenburg
2025-09-16 12:02:53 +02:00
committed by GitHub
Unverified
parent 66fe1c957c
commit 65dd48aa1d
58 changed files with 676 additions and 2588 deletions
+25
View File
@@ -0,0 +1,25 @@
name: Reusable Setup UV
description: Reusable workflow to setup uv environment
inputs:
python-version:
description: The Python version to set up
required: true
os:
description: The operating system to set up
required: true
runs:
using: "composite"
steps:
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ inputs.os }}-${{ inputs.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
shell: bash
run: |
cd python && uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit
+8 -8
View File
@@ -28,15 +28,15 @@ jobs:
UV_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: uv sync --all-extras --dev
python-version: ${{ matrix.python-version }}
os: ${{ runner.os }}
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
+39
View File
@@ -0,0 +1,39 @@
name: Python - Create Docs
on:
workflow_dispatch:
release:
types: [published]
permissions:
contents: write
id-token: write
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
jobs:
python-build-docs:
if: github.event_name == 'release' && startsWith(github.event.release.tag_name, 'python-')
name: Python Build Docs
runs-on: ubuntu-latest
environment: "integration"
env:
UV_PYTHON: "3.11"
defaults:
run:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ env.UV_PYTHON }}
cache-dependency-glob: "**/uv.lock"
- name: Install dependencies
run: uv sync --all-packages --dev --docs
- name: Build the docs
run: uv run poe docs-full
# Upload docs to learn gh
+24 -27
View File
@@ -67,16 +67,15 @@ jobs:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: |
uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit
python-version: ${{ matrix.python-version }}
os: ${{ runner.os }}
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
- name: Test with pytest
timeout-minutes: 10
run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml
@@ -128,16 +127,15 @@ jobs:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: |
uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit
python-version: ${{ matrix.python-version }}
os: ${{ runner.os }}
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
- name: Azure CLI Login
if: github.event_name != 'pull_request'
uses: azure/login@v2
@@ -195,16 +193,15 @@ jobs:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: |
uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit
python-version: ${{ matrix.python-version }}
os: ${{ runner.os }}
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
- name: Azure CLI Login
if: github.event_name != 'pull_request'
uses: azure/login@v2
+8 -6
View File
@@ -24,13 +24,15 @@ jobs:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ env.UV_PYTHON }}
cache-dependency-glob: "**/uv.lock"
python-version: ${{ matrix.python-version }}
os: ${{ runner.os }}
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
- name: Set environment variables
run: |
# Extract package name from tag (format: python-<package>-<version>)
+8 -8
View File
@@ -26,15 +26,15 @@ jobs:
- name: Save PR number
run: |
echo ${{ github.event.number }} > ./pr_number
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ env.UV_PYTHON }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: uv sync --all-extras --dev
python-version: ${{ matrix.python-version }}
os: ${{ runner.os }}
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
- name: Run all tests with coverage report
run: uv run poe all-tests -n logical --dist loadfile --dist worksteal --cov-report=xml:python-coverage.xml -q --junitxml=pytest.xml
- name: Upload coverage report
+8 -10
View File
@@ -27,17 +27,15 @@ jobs:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
- name: Set up python and install the project
id: python-setup
uses: ./.github/actions/python-setup
with:
version-file: "python/pyproject.toml"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: |
uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit
python-version: ${{ matrix.python-version }}
os: ${{ runner.os }}
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
# Main package tests
- name: Set environment variables - main - win
if: ${{ matrix.os == 'windows-latest' }}
+1 -1
View File
@@ -70,7 +70,7 @@ instance/
.scrapy
# Sphinx documentation
docs/_build/
docs/build/
# PyBuilder
.pybuilder/
+9
View File
@@ -11,6 +11,15 @@
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Python Attach",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
}
}
]
}
+27 -9
View File
@@ -550,6 +550,12 @@ uv run poe test
### Documentation
#### `docs-install`
Install including the documentation tools:
```bash
uv run poe docs-install
```
#### `docs-clean`
Remove the docs build directory:
```bash
@@ -562,22 +568,34 @@ Build the documentation:
uv run poe docs-build
```
#### `docs-serve`
Serve documentation locally with auto-reload:
#### `docs-full`
Build the packages, clean and build the documentation:
```bash
uv run poe docs-serve
uv run poe docs-full
```
#### `docs-check`
Build documentation and fail on warnings:
#### `docs-rebuild`
Clean and build the documentation:
```bash
uv run poe docs-check
uv run poe docs-rebuild
```
#### `docs-check-examples`
Check documentation examples for code correctness:
#### `docs-full-install`
Install the docs dependencies, build the packages, clean and build the documentation:
```bash
uv run poe docs-check-examples
uv run poe docs-full-install
```
#### `docs-debug`
Build the documentation with debug information:
```bash
uv run poe docs-debug
```
#### `docs-rebuild-debug`
Clean and build the documentation with debug information:
```bash
uv run poe docs-rebuild-debug
```
### Code Validation
+32 -8
View File
@@ -3,19 +3,35 @@
"""Check code blocks in Markdown files for syntax errors."""
import argparse
from enum import Enum
import logging
import tempfile
import subprocess # nosec
from pygments import highlight # type: ignore
from pygments.formatters import TerminalFormatter
from pygments.lexers import PythonLexer
from sphinx.util.console import darkgreen, darkred, faint, red, teal # type: ignore[attr-defined]
logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.INFO)
class Colors(str, Enum):
CEND = "\33[0m"
CRED = "\33[31m"
CREDBG = "\33[41m"
CGREEN = "\33[32m"
CGREENBG = "\33[42m"
CVIOLET = "\33[35m"
CGREY = "\33[90m"
def with_color(text: str, color: Colors) -> str:
"""Prints a string with the specified color."""
return f"{color.value}{text}{Colors.CEND.value}"
def extract_python_code_blocks(markdown_file_path: str) -> list[tuple[str, int]]:
"""Extract Python code blocks from a Markdown file."""
with open(markdown_file_path, encoding="utf-8") as file:
@@ -40,7 +56,7 @@ def extract_python_code_blocks(markdown_file_path: str) -> list[tuple[str, int]]
def check_code_blocks(markdown_file_paths: list[str]) -> None:
"""Check Python code blocks in a Markdown file for syntax errors."""
files_with_errors = []
files_with_errors: list[str] = []
for markdown_file_path in markdown_file_paths:
code_blocks = extract_python_code_blocks(markdown_file_path)
@@ -54,7 +70,7 @@ def check_code_blocks(markdown_file_paths: list[str]) -> None:
all(import_code not in code_block for import_code in [f"import {module}", f"from {module}"])
for module in ["agent_framework"]
):
logger.info(" " + darkgreen("OK[ignored]"))
logger.info(f' {with_color("OK[ignored]", Colors.CGREENBG)}')
continue
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as temp_file:
@@ -62,17 +78,25 @@ def check_code_blocks(markdown_file_paths: list[str]) -> None:
temp_file.flush()
# Run pyright on the temporary file using subprocess.run
import subprocess # nosec
result = subprocess.run(["pyright", temp_file.name], capture_output=True, text=True) # nosec
if result.returncode != 0:
logger.info(" " + darkred("FAIL"))
highlighted_code = highlight(code_block, PythonLexer(), TerminalFormatter()) # type: ignore
output = f"{faint('========================================================')}\n{red('Error')}: Pyright found issues in {teal(markdown_file_path_with_line_no)}:\n{faint('--------------------------------------------------------')}\n{highlighted_code}\n{faint('--------------------------------------------------------')}\n\n{teal('pyright output:')}\n{red(result.stdout)}{faint('========================================================')}\n"
logger.info(output)
logger.info(
f" {with_color('FAIL', Colors.CREDBG)}\n"
f"{with_color('========================================================', Colors.CGREY)}\n"
f"{with_color('Error', Colors.CRED)}: Pyright found issues in {with_color(markdown_file_path_with_line_no, Colors.CVIOLET)}:\n"
f"{with_color('--------------------------------------------------------', Colors.CGREY)}\n"
f"{highlighted_code}\n"
f"{with_color('--------------------------------------------------------', Colors.CGREY)}\n"
"\n"
f"{with_color('pyright output:', Colors.CVIOLET)}\n"
f"{with_color(result.stdout, Colors.CRED)}"
f"{with_color('========================================================', Colors.CGREY)}\n"
)
had_errors = True
else:
logger.info(" " + darkgreen("OK"))
logger.info(f" {with_color('OK', Colors.CGREENBG)}")
if had_errors:
files_with_errors.append(markdown_file_path)
-37
View File
@@ -1,37 +0,0 @@
## Building the Agent Framework Documentation
Agent Framework documentation is based on the sphinx documentation system and uses the myst-parser to render markdown files. It uses the [pydata-sphinx-theme](https://pydata-sphinx-theme.readthedocs.io/en/latest/) to style the documentation.
### Prerequisites
Ensure you have all of the dev dependencies for the `agent-framework` package installed. You can install them by running the following command from the root of the `python` directory:
```bash
uv sync
source .venv/bin/activate
```
## Building Docs
To build the documentation, run the following command from the root of the python repository:
```bash
poe docs-build
```
To serve the documentation locally, run the following command from the root of the python repository:
```bash
poe --directory ./packages/autogen-core/ docs-serve
```
[!NOTE]
Sphinx will only rebuild files that have changed since the last build. If you want to force a full rebuild, you can run `poe docs-clean` before running the `docs-build` command.
## Versioning the Documentation
The current theme - [pydata-sphinx-theme](https://pydata-sphinx-theme.readthedocs.io/en/latest/) - supports [switching between versions](https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/version-dropdown.html) of the documentation.
To version the documentation, you need to create a new version of the documentation by copying the existing documentation to a new directory with the version number. For example, to create a new version of the documentation for version `0.1.0`, you would run the following command:
How are various versions built? - TBD.
@@ -1,8 +0,0 @@
{%- if show_headings %}
{{- basename | e | heading }}
{% endif -%}
.. automodule:: {{ qualname }}
{%- for option in automodule_options %}
:{{ option }}:
{%- endfor %}
@@ -1,53 +0,0 @@
{%- macro automodule(modname, options) -%}
.. automodule:: {{ modname }}
{%- for option in options %}
:{{ option }}:
{%- endfor %}
{%- endmacro %}
{%- macro toctree(docnames) -%}
.. toctree::
:maxdepth: {{ maxdepth }}
:hidden:
{% for docname in docnames %}
{{ docname }}
{%- endfor %}
{%- endmacro %}
{%- if is_namespace %}
{{- [pkgname, "namespace"] | join(" ") | e | heading }}
{% else %}
{{- pkgname | e | heading }}
{% endif %}
{%- if is_namespace %}
.. py:module:: {{ pkgname }}
{% endif %}
{%- if modulefirst and not is_namespace %}
{{ automodule(pkgname, automodule_options) }}
{% endif %}
{%- if subpackages %}
{{ toctree(subpackages) }}
{% endif %}
{%- if submodules %}
{% if separatemodules %}
{{ toctree(submodules) }}
{% else %}
{%- for submodule in submodules %}
{% if show_headings %}
{{- [submodule, "module"] | join(" ") | e | heading(2) }}
{% endif %}
{{ automodule(submodule, automodule_options) }}
{% endfor %}
{%- endif %}
{%- endif %}
{%- if not modulefirst and not is_namespace %}
{{ automodule(pkgname, automodule_options) }}
{% endif %}
@@ -1,98 +0,0 @@
# Modified from: https://github.com/kai687/sphinxawesome-codelinter
import tempfile
from collections.abc import Iterable
from typing import AbstractSet, Any
from docutils import nodes
from pygments import highlight # type: ignore
from pygments.formatters import TerminalFormatter
from pygments.lexers import PythonLexer
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.util import logging
from sphinx.util.console import darkgreen, darkred, faint, red, teal # type: ignore[attr-defined]
logger = logging.getLogger(__name__)
__version__ = "0.1.0"
class CodeLinter(Builder):
"""Iterate over all ``literal_block`` nodes.
pipe them into any command line tool that
can read from standard input.
"""
name = "code_lint"
allow_parallel = True
def init(self) -> None:
"""Initialize."""
self._had_errors = False
pass
def get_outdated_docs(self) -> str | Iterable[str]:
"""Check for outdated files.
Return an iterable of outdated output files, or a string describing what an
update will build.
"""
return self.env.found_docs
def get_target_uri(self, docname: str, typ: str | None = None) -> str:
"""Return Target URI for a document name."""
return ""
def prepare_writing(self, docnames: AbstractSet[str]) -> None:
"""Run these steps before documents are written."""
return
def write_doc(self, docname: str, doctree: nodes.Node) -> None:
path_prefix: str = self.app.config.code_lint_path_prefix
supported_languages = {"python", "default"}
if not docname.startswith(path_prefix):
return
for code in doctree.findall(nodes.literal_block):
if code["language"] in supported_languages:
logger.info("Checking a code block in %s...", docname, nonl=True)
if "ignore" in code["classes"]:
logger.info(" " + darkgreen("OK[ignored]"))
continue
# Create a temporary file to store the code block
with tempfile.NamedTemporaryFile(mode="wb", suffix=".py") as temp_file:
temp_file.write(code.astext().encode())
temp_file.flush()
# Run pyright on the temporary file using subprocess.run
import subprocess
result = subprocess.run(["pyright", temp_file.name], capture_output=True, text=True)
if result.returncode != 0:
logger.info(" " + darkred("FAIL"))
highlighted_code = highlight(code.astext(), PythonLexer(), TerminalFormatter()) # type: ignore
output = f"{faint('========================================================')}\n{red('Error')}: Pyright found issues in {teal(docname)}:\n{faint('--------------------------------------------------------')}\n{highlighted_code}\n{faint('--------------------------------------------------------')}\n\n{teal('pyright output:')}\n{red(result.stdout)}{faint('========================================================')}\n"
logger.info(output)
self._had_errors = True
else:
logger.info(" " + darkgreen("OK"))
def finish(self) -> None:
"""Finish the build process."""
if self._had_errors:
raise RuntimeError("Code linting failed - see earlier output")
def setup(app: Sphinx) -> dict[str, Any]:
app.add_builder(CodeLinter)
app.add_config_value("code_lint_path_prefix", "", "env")
return {
"version": __version__,
"parallel_read_safe": True,
"parallel_write_safe": True,
}
@@ -1,144 +0,0 @@
"""A directive to generate a gallery of images from structured data.
Generating a gallery of images that are all the same size is a common
pattern in documentation, and this can be cumbersome if the gallery is
generated programmatically. This directive wraps this particular use-case
in a helper-directive to generate it with a single YAML configuration file.
It currently exists for maintainers of the pydata-sphinx-theme,
but might be abstracted into a standalone package if it proves useful.
"""
from pathlib import Path
from typing import Any, ClassVar
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx.application import Sphinx
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective
from yaml import safe_load
logger = logging.getLogger(__name__)
TEMPLATE_GRID = """
`````{{grid}} {columns}
{options}
{content}
`````
"""
GRID_CARD = """
````{{grid-item-card}} {title}
{options}
{content}
````
"""
class GalleryGridDirective(SphinxDirective):
"""A directive to show a gallery of images and links in a Bootstrap grid.
The grid can be generated from a YAML file that contains a list of items, or
from the content of the directive (also formatted in YAML). Use the parameter
"class-card" to add an additional CSS class to all cards. When specifying the grid
items, you can use all parameters from "grid-item-card" directive to customize
individual cards + ["image", "header", "content", "title"].
Danger:
This directive can only be used in the context of a Myst documentation page as
the templates use Markdown flavored formatting.
"""
name = "gallery-grid"
has_content = True
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = True
option_spec: ClassVar[dict[str, Any]] = {
# A class to be added to the resulting container
"grid-columns": directives.unchanged,
"class-container": directives.unchanged,
"class-card": directives.unchanged,
}
def run(self) -> list[nodes.Node]:
"""Create the gallery grid."""
if self.arguments:
# If an argument is given, assume it's a path to a YAML file
# Parse it and load it into the directive content
path_data_rel = Path(self.arguments[0])
path_doc, _ = self.get_source_info()
path_doc = Path(path_doc).parent
path_data = (path_doc / path_data_rel).resolve()
if not path_data.exists():
logger.info(f"Could not find grid data at {path_data}.")
nodes.text("No grid data found at {path_data}.")
return None
yaml_string = path_data.read_text()
else:
yaml_string = "\n".join(self.content)
# Use all the element with an img-bottom key as sites to show
# and generate a card item for each of them
grid_items = []
for item in safe_load(yaml_string):
# remove parameters that are not needed for the card options
title = item.pop("title", "")
# build the content of the card using some extra parameters
header = f"{item.pop('header')} \n^^^ \n" if "header" in item else ""
image = f"![image]({item.pop('image')}) \n" if "image" in item else ""
content = f"{item.pop('content')} \n" if "content" in item else ""
# optional parameter that influence all cards
if "class-card" in self.options:
item["class-card"] = self.options["class-card"]
loc_options_str = "\n".join(f":{k}: {v}" for k, v in item.items()) + " \n"
card = GRID_CARD.format(
options=loc_options_str, content=header + image + content, title=title
)
grid_items.append(card)
# Parse the template with Sphinx Design to create an output container
# Prep the options for the template grid
class_ = "gallery-directive" + f' {self.options.get("class-container", "")}'
options = {"gutter": 2, "class-container": class_}
options_str = "\n".join(f":{k}: {v}" for k, v in options.items())
# Create the directive string for the grid
grid_directive = TEMPLATE_GRID.format(
columns=self.options.get("grid-columns", "1 2 3 4"),
options=options_str,
content="\n".join(grid_items),
)
# Parse content as a directive so Sphinx Design processes it
container = nodes.container()
self.state.nested_parse([grid_directive], 0, container)
# Sphinx Design outputs a container too, so just use that
return [container.children[0]]
def setup(app: Sphinx) -> dict[str, Any]:
"""Add custom configuration to sphinx app.
Args:
app: the Sphinx application
Returns:
the 2 parallel parameters set to ``True``.
"""
app.add_directive("gallery-grid", GalleryGridDirective)
return {
"parallel_read_safe": True,
"parallel_write_safe": True,
}
@@ -1,11 +0,0 @@
var version = DOCUMENTATION_OPTIONS.VERSION;
if (version === "stable") {
var styles = `
#bd-header-version-warning {
display: none;
}
`
var styleSheet = document.createElement("style")
styleSheet.textContent = styles
document.head.appendChild(styleSheet)
}
@@ -1,18 +0,0 @@
// File from: https://github.com/pydata/pydata-sphinx-theme/blob/main/docs/_static/custom-icon.js
/*******************************************************************************
* Set a custom icon for pypi as it's not available in the fa built-in brands
*/
FontAwesome.library.add(
(faListOldStyle = {
prefix: "fa-custom",
iconName: "pypi",
icon: [
17.313, // viewBox width
19.807, // viewBox height
[], // ligature
"e001", // unicode codepoint - private use area
"m10.383 0.2-3.239 1.1769 3.1883 1.1614 3.239-1.1798zm-3.4152 1.2411-3.2362 1.1769 3.1855 1.1614 3.2369-1.1769zm6.7177 0.00281-3.2947 1.2009v3.8254l3.2947-1.1988zm-3.4145 1.2439-3.2926 1.1981v3.8254l0.17548-0.064132 3.1171-1.1347zm-6.6564 0.018325v3.8247l3.244 1.1805v-3.8254zm10.191 0.20931v2.3137l3.1777-1.1558zm3.2947 1.2425-3.2947 1.1988v3.8254l3.2947-1.1988zm-8.7058 0.45739c0.00929-1.931e-4 0.018327-2.977e-4 0.027485 0 0.25633 0.00851 0.4263 0.20713 0.42638 0.49826 1.953e-4 0.38532-0.29327 0.80469-0.65542 0.93662-0.36226 0.13215-0.65608-0.073306-0.65613-0.4588-6.28e-5 -0.38556 0.2938-0.80504 0.65613-0.93662 0.068422-0.024919 0.13655-0.038114 0.20156-0.039466zm5.2913 0.78369-3.2947 1.1988v3.8247l3.2947-1.1981zm-10.132 1.239-3.2362 1.1769 3.1883 1.1614 3.2362-1.1769zm6.7177 0.00213-3.2926 1.2016v3.8247l3.2926-1.2009zm-3.4124 1.2439-3.2947 1.1988v3.8254l3.2947-1.1988zm-6.6585 0.016195v3.8275l3.244 1.1805v-3.8254zm16.9 0.21143-3.2947 1.1988v3.8247l3.2947-1.1981zm-3.4145 1.2411-3.2926 1.2016v3.8247l3.2926-1.2009zm-3.4145 1.2411-3.2926 1.2016v3.8247l3.2926-1.2009zm-3.4124 1.2432-3.2947 1.1988v3.8254l3.2947-1.1988zm-6.6585 0.019027v3.8247l3.244 1.1805v-3.8254zm13.485 1.4497-3.2947 1.1988v3.8247l3.2947-1.1981zm-3.4145 1.2411-3.2926 1.2016v3.8247l3.2926-1.2009zm2.4018 0.38127c0.0093-1.83e-4 0.01833-3.16e-4 0.02749 0 0.25633 0.0085 0.4263 0.20713 0.42638 0.49826 1.97e-4 0.38532-0.29327 0.80469-0.65542 0.93662-0.36188 0.1316-0.65525-0.07375-0.65542-0.4588-1.95e-4 -0.38532 0.29328-0.80469 0.65542-0.93662 0.06842-0.02494 0.13655-0.03819 0.20156-0.03947zm-5.8142 0.86403-3.244 1.1805v1.4201l3.244 1.1805z", // svg path (https://simpleicons.org/icons/pypi.svg)
],
}),
);
@@ -1,147 +0,0 @@
.bd-footer {
font-size: 0.8rem;
}
html[data-theme="light"] {
--pst-color-primary: hsl(222.2 47.4% 11.2%);
--pst-color-secondary: #1774E5;
--pst-color-secondary-bg: #1774E5;
--pst-color-accent: #1774E5;
--sd-color-secondary-highlight: #0062cc;
--pst-color-shadow: rgba(0, 0, 0, 0.0);
}
html[data-theme="dark"] {
--pst-color-primary: hsl(213 31% 91%);
--pst-color-secondary: #017FFF;
--pst-color-secondary-bg: #017FFF;
--pst-color-accent: #017FFF;
--sd-color-secondary-highlight: #0062cc;
--pst-color-shadow: rgba(0, 0, 0, 0.0);
}
.bd-header-announcement {
color: white;
}
.bd-header-announcement a {
color: white;
}
.bd-header-announcement a:hover {
color: white;
text-shadow: 0.5px 0 0 currentColor;
}
/* Adding header icon hover and focus effects */
.bd-header a:focus-visible {
color: var(--pst-color-secondary) !important;
text-decoration: underline !important;
text-shadow: 0.5px 0 0 currentColor;
transform: scale(1.05);
transition: all 0.2s ease-in-out;
outline: none;
}
nav.bd-links .current>a {
box-shadow: inset 1px 0 0 var(--pst-color-primary);
}
@media (forced-colors: active) {
/* Top breadcrumbs navigation (ie: Home > Core > ...) */
.bd-breadcrumbs .breadcrumb-item > a:focus-visible{
border: 2px solid var(--pst-color-primary);
}
/* Left sidebar */
nav.bd-links .navbar-nav .toctree-l1>a:focus-visible {
border: 2px solid var(--pst-color-primary);
}
nav.bd-links .current>a {
box-shadow: none;
border-left: 4px solid var(--pst-color-primary) !important;
}
/* Right sidebar */
.bd-sidebar-secondary .sidebar-secondary-items .nav-item .active {
box-shadow: none;
border-left: 5px solid var(--pst-color-primary) !important;
}
.bd-sidebar-secondary .sidebar-secondary-items .nav-item>a:focus-visible {
border: 2px solid var(--pst-color-primary);
}
}
html[data-theme="light"] .bd-header {
border-bottom: 1px solid var(--pst-color-border);
}
.admonition, div.admonition {
border: 1px solid var(--pst-color-border);
}
.api-card {
text-align: center;
font-size: 1.2rem;
}
.api-card svg {
font-size: 2rem;
}
.search-button-field {
border-radius: var(--bs-btn-border-radius);
}
.bd-content .sd-tab-set .sd-tab-content {
border: none;
border-top: 3px solid var(--pst-color-border);
}
.bd-content .sd-tab-set>input:checked+label {
border: none;
transform: translateY(0);
font-weight: 700;
border-bottom: 4px solid var(--pst-color-secondary);
}
.bd-content .sd-tab-set>input:focus-visible+label {
border: 2px outset var(--pst-color-secondary);
transform: translateY(0);
}
.bd-content .sd-tab-set>label {
border: none;
background-color: transparent;
font-weight: 500;
}
.card-title {
font-size: 1.2rem;
font-weight: bold;
}
.card-title svg {
font-size: 2rem;
vertical-align: bottom;
margin-right: 5px;
}
/* This is gross, but necessary to meet accessibility requirements */
.headerlink {
visibility: visible !important;
}
/* jupyter notebook output cells */
.bd-article .docutils .cell_output .output .highlight > pre:focus-visible{
border: 2px outset var(--pst-color-secondary);
}
/* Copy button */
.bd-article .docutils .docutils .copybtn:focus-visible:after {
/* border: 10px outset var(--pst-color-primary); */
display: block;
opacity: 1;
visibility: visible;
}
/* Long autodoc module names wrap on prev/next links */
/* TODO: Should we extend this to the entire site? */
.prev-next-title {
word-break: break-word;
}
@@ -1,208 +0,0 @@
document.addEventListener('DOMContentLoaded', function () {
let liveRegion = createLiveRegion();
document.querySelectorAll('.copybtn').forEach(button => {
// Return focus to copy button after activation
button.addEventListener('click', async function (event) {
// Save the current focus
const focusedElement = document.activeElement;
// Perform the copy action
await copyToClipboard(this);
announceMessage(liveRegion, 'Copied to clipboard');
// Restore the focus
focusedElement.focus();
});
});
document.querySelectorAll('.search-button-field').forEach(button => {
button.addEventListener('click', () => {
// Save the element that had focus before opening the search
const previousFocus = document.activeElement;
// Add an event listener to handle closing the search
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
// Restore focus to the previous element
previousFocus.focus();
}
});
});
});
// Set active TOCtree elements with aria-current=page
document.querySelectorAll('.bd-sidenav .active').forEach(function (element) {
element.setAttribute('aria-current', 'page');
});
// Set secondary navbar (in-page nagivation) active element with aria-current=page
document.addEventListener("activate.bs.scrollspy", function () {
const navLinks = document.querySelectorAll(".bd-toc-nav a");
navLinks.forEach((navLink) => {
navLink.parentElement.removeAttribute('aria-current');
});
const activeNavLinks = document.querySelectorAll(".bd-toc-nav a.active");
activeNavLinks.forEach((navLink) => {
navLink.parentElement.setAttribute('aria-current', 'page');
});
});
const themeButton = document.querySelector('.theme-switch-button');
if (themeButton) {
themeButton.addEventListener('click', function () {
const mode = document.documentElement.getAttribute('data-mode');
announceMessage(liveRegion, `Theme changed to ${mode}`);
});
}
// Enhance TOC sections for accessibility
document.querySelectorAll('.caption-text').forEach(caption => {
const sectionTitle = caption.textContent.trim();
const captionContainer = caption.closest('p.caption');
if (!captionContainer) return;
// Find and process navigation lists that belong to this section
findSectionNav(captionContainer, sectionTitle);
});
// Version dropdown menu is dynamically generated after page load. Listen for changes to set aria-selected
var observer = new MutationObserver(function () {
document.querySelectorAll('.dropdown-item').forEach(function (element) {
if (element.classList.contains('active')) {
element.setAttribute('aria-selected', 'true');
}
});
});
// Observe changes in the version-switcher__menu element
var targetNode = document.querySelector('.version-switcher__menu');
var config = { childList: true, subtree: true };
if (targetNode) {
observer.observe(targetNode, config);
}
});
async function copyToClipboard(button) {
const targetSelector = button.getAttribute('data-clipboard-target');
const codeBlock = document.querySelector(targetSelector);
try {
await navigator.clipboard.writeText(codeBlock.textContent);
} catch (err) {
console.error('Failed to copy text: ', err);
}
}
function createLiveRegion() {
const liveRegion = document.createElement('div');
liveRegion.setAttribute('role', 'status');
liveRegion.setAttribute('aria-live', 'assertive');
liveRegion.style.position = 'absolute';
liveRegion.style.width = '1px';
liveRegion.style.height = '1px';
liveRegion.style.padding = '0';
liveRegion.style.margin = '-1px';
liveRegion.style.overflow = 'hidden';
liveRegion.style.clipPath = 'inset(50%)';
liveRegion.style.whiteSpace = 'nowrap'; ` `
liveRegion.style.border = '0';
document.body.appendChild(liveRegion);
return liveRegion;
}
function announceMessage(liveRegion, message) {
liveRegion.textContent = '';
setTimeout(() => {
liveRegion.textContent = message;
}, 50);
}
/**
* Find navigation lists belonging to a section and process them
*/
function findSectionNav(captionContainer, sectionTitle) {
let nextElement = captionContainer.nextElementSibling;
while (nextElement) {
if (nextElement.classList && nextElement.classList.contains('caption')) {
break;
}
if (nextElement.matches('ul.bd-sidenav')) {
enhanceNavList(nextElement, sectionTitle);
}
nextElement = nextElement.nextElementSibling;
}
}
/**
* Process a navigation list by enhancing its links for accessibility
*/
function enhanceNavList(navList, sectionTitle) {
const topLevelItems = navList.querySelectorAll(':scope > li');
topLevelItems.forEach(item => {
const link = item.querySelector(':scope > a.reference.internal');
if (!link) return;
const linkText = link.textContent.trim();
link.setAttribute('aria-label', `${sectionTitle}: ${linkText}`);
enhanceExpandableSections(item, link, linkText, sectionTitle);
});
}
/**
* Process expandable sections (details elements) within a navigation item
*/
function enhanceExpandableSections(item, parentLink, parentText, sectionTitle) {
const detailsElements = item.querySelectorAll('details');
detailsElements.forEach(details => {
enhanceToggleButton(details, parentText);
enhanceNestedLinks(details, parentLink, parentText, sectionTitle);
});
}
/**
* Make toggle buttons more accessible by adding appropriate aria labels
*/
function enhanceToggleButton(details, parentText) {
const summary = details.querySelector('summary');
if (!summary) return;
function updateToggleLabel() {
const isExpanded = details.hasAttribute('open');
const action = isExpanded ? 'Collapse' : 'Expand';
summary.setAttribute('aria-label', `${action} ${parentText} section`);
}
updateToggleLabel();
summary.addEventListener('click', () => {
setTimeout(updateToggleLabel, 10);
});
}
/**
* Enhance nested links with hierarchical aria-labels
*/
function enhanceNestedLinks(details, parentLink, parentText, sectionTitle) {
const nestedLinks = details.querySelectorAll('a.reference.internal');
nestedLinks.forEach(link => {
const linkText = link.textContent.trim();
const parentLabel = parentLink.getAttribute('aria-label');
if (parentLabel) {
link.setAttribute('aria-label', `${parentLabel}: ${linkText}`);
} else {
link.setAttribute('aria-label', `${sectionTitle}: ${parentText}: ${linkText}`);
}
});
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

@@ -1,4 +0,0 @@
<svg width="96" height="85" viewBox="0 0 96 85" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="96" height="85" rx="6" fill="#2D2D2F"/>
<path d="M32.6484 28.7109L23.3672 57H15.8906L28.5703 22.875H33.3281L32.6484 28.7109ZM40.3594 57L31.0547 28.7109L30.3047 22.875H35.1094L47.8594 57H40.3594ZM39.9375 44.2969V49.8047H21.9141V44.2969H39.9375ZM77.6484 39.1641V52.6875C77.1172 53.3281 76.2969 54.0234 75.1875 54.7734C74.0781 55.5078 72.6484 56.1406 70.8984 56.6719C69.1484 57.2031 67.0312 57.4688 64.5469 57.4688C62.3438 57.4688 60.3359 57.1094 58.5234 56.3906C56.7109 55.6562 55.1484 54.5859 53.8359 53.1797C52.5391 51.7734 51.5391 50.0547 50.8359 48.0234C50.1328 45.9766 49.7812 43.6406 49.7812 41.0156V38.8828C49.7812 36.2578 50.1172 33.9219 50.7891 31.875C51.4766 29.8281 52.4531 28.1016 53.7188 26.6953C54.9844 25.2891 56.4922 24.2188 58.2422 23.4844C59.9922 22.75 61.9375 22.3828 64.0781 22.3828C67.0469 22.3828 69.4844 22.8672 71.3906 23.8359C73.2969 24.7891 74.75 26.1172 75.75 27.8203C76.7656 29.5078 77.3906 31.4453 77.625 33.6328H70.8047C70.6328 32.4766 70.3047 31.4688 69.8203 30.6094C69.3359 29.75 68.6406 29.0781 67.7344 28.5938C66.8438 28.1094 65.6875 27.8672 64.2656 27.8672C63.0938 27.8672 62.0469 28.1094 61.125 28.5938C60.2188 29.0625 59.4531 29.7578 58.8281 30.6797C58.2031 31.6016 57.7266 32.7422 57.3984 34.1016C57.0703 35.4609 56.9062 37.0391 56.9062 38.8359V41.0156C56.9062 42.7969 57.0781 44.375 57.4219 45.75C57.7656 47.1094 58.2734 48.2578 58.9453 49.1953C59.6328 50.1172 60.4766 50.8125 61.4766 51.2812C62.4766 51.75 63.6406 51.9844 64.9688 51.9844C66.0781 51.9844 67 51.8906 67.7344 51.7031C68.4844 51.5156 69.0859 51.2891 69.5391 51.0234C70.0078 50.7422 70.3672 50.4766 70.6172 50.2266V44.1797H64.1953V39.1641H77.6484Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,11 +0,0 @@
[
{
"name": "v0.2 (stable)",
"version": "0.2",
"url": "https://microsoft.github.io/autogen/0.2/"
},
{
"version": "dev",
"url": "https://microsoft.github.io/autogen/dev/"
}
]
@@ -1,16 +0,0 @@
{% if sourcename is defined and theme_use_edit_page_button and page_source_suffix %}
{% set src = sourcename.split('.') %}
<div class="tocsection editthispage">
<a href="{{ to_main(get_edit_provider_and_url()[1]) }}">
<i class="fa-solid fa-pencil"></i>
{% set provider = get_edit_provider_and_url()[0] %}
{% block edit_this_page_text %}
{% if provider %}
{% trans provider=provider %}Edit on {{ provider }}{% endtrans %}
{% else %}
{% trans %}Edit{% endtrans %}
{% endif %}
{% endblock %}
</a>
</div>
{% endif %}
@@ -1 +0,0 @@
<p><a href="https://go.microsoft.com/fwlink/?LinkId=521839">Privacy Policy</a> | <a href="https://go.microsoft.com/fwlink/?linkid=2259814">Consumer Health Privacy</a> </p>
@@ -1,39 +0,0 @@
{# Displays the TOC-subtree for pages nested under the currently active top-level TOCtree element. #}
<nav class="bd-docs-nav bd-links" aria-label="{{ _('Section Navigation') }}">
<div class="bd-toc-item navbar-nav">
{{- generate_toctree_html(
"sidebar",
show_nav_level=theme_show_nav_level | int,
maxdepth=theme_navigation_depth | int,
collapse=theme_collapse_navigation | tobool,
includehidden=theme_sidebar_includehidden | tobool,
titles_only=True
)
-}}
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="{{pathto('reference/python/autogen_agentchat')}}">
<i class="fa-solid fa-file-code"></i>
API Reference
</a>
</li>
<li class="toctree-l1">
<a target="_blank" class="reference internal"
href="https://pypi.org/project/autogen-agentchat/">
<i class="fa-brands fa-python"></i>
PyPi
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
<li class="toctree-l1">
<a target="_blank" class="reference internal"
href="https://github.com/microsoft/autogen/tree/main/python/packages/autogen-agentchat">
<i class="fa-brands fa-github"></i>
Source
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
</ul>
</div>
</nav>
@@ -1,38 +0,0 @@
{# Displays the TOC-subtree for pages nested under the currently active top-level TOCtree element. #}
<nav class="bd-docs-nav bd-links" aria-label="{{ _('Section Navigation') }}">
<div class="bd-toc-item navbar-nav">
{{- generate_toctree_html(
"sidebar",
show_nav_level=theme_show_nav_level | int,
maxdepth=theme_navigation_depth | int,
collapse=theme_collapse_navigation | tobool,
includehidden=theme_sidebar_includehidden | tobool,
titles_only=True
)
-}}
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="{{pathto('reference/python/autogen_core')}}">
<i class="fa-solid fa-file-code"></i>
API Reference
</a>
</li>
<li class="toctree-l1">
<a target="_blank" class="reference internal" href="https://pypi.org/project/autogen-core/">
<i class="fa-brands fa-python"></i>
PyPi
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
<li class="toctree-l1">
<a target="_blank" class="reference internal"
href="https://github.com/microsoft/autogen/tree/main/python/packages/autogen-core">
<i class="fa-brands fa-github"></i>
Source
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
</ul>
</div>
</nav>
@@ -1,39 +0,0 @@
{# Displays the TOC-subtree for pages nested under the currently active top-level TOCtree element. #}
<nav class="bd-docs-nav bd-links" aria-label="{{ _('Section Navigation') }}">
<div class="bd-toc-item navbar-nav">
{{- generate_toctree_html(
"sidebar",
show_nav_level=theme_show_nav_level | int,
maxdepth=theme_navigation_depth | int,
collapse=theme_collapse_navigation | tobool,
includehidden=theme_sidebar_includehidden | tobool,
titles_only=True
)
-}}
<p aria-level="2" class="caption" role="heading"><span class="caption-text">More</span></p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="{{pathto('reference/python/autogen_ext.agents.magentic_one')}}">
<i class="fa-solid fa-file-code"></i>
API Reference
</a>
</li>
<li class="toctree-l1">
<a target="_blank" class="reference internal" href="https://pypi.org/project/autogen-ext/">
<i class="fa-brands fa-python"></i>
PyPi
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
<li class="toctree-l1">
<a target="_blank" class="reference internal"
href="https://github.com/microsoft/autogen/tree/main/python/packages/autogen-ext">
<i class="fa-brands fa-github"></i>
Source
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
</ul>
</div>
</nav>
@@ -1,32 +0,0 @@
{# Displays the TOC-subtree for pages nested under the currently active top-level TOCtree element. #}
<nav class="bd-docs-nav bd-links"
aria-label="{{ _('Section Navigation') }}">
<div class="bd-toc-item navbar-nav">
{{- generate_toctree_html(
"sidebar",
show_nav_level=theme_show_nav_level | int,
maxdepth=theme_navigation_depth | int,
collapse=theme_collapse_navigation | tobool,
includehidden=theme_sidebar_includehidden | tobool,
titles_only=True
)
-}}
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a target="_blank" class="reference internal" href="https://pypi.org/project/autogenstudio/">
<i class="fa-brands fa-python"></i>
PyPi
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
<li class="toctree-l1">
<a target="_blank" class="reference internal" href="https://github.com/microsoft/autogen/tree/main/python/packages/autogen-studio">
<i class="fa-brands fa-github"></i>
Source
<i class="fa-solid fa-arrow-up-right-from-square fa-2xs"></i>
</a>
</li>
</ul>
</div>
</nav>
@@ -1,15 +0,0 @@
{# Displays the TOC-subtree for pages nested under the currently active top-level TOCtree element. #}
<nav class="bd-docs-nav bd-links"
aria-label="{{ _('Section Navigation') }}">
<div class="bd-toc-item navbar-nav">
{{- generate_toctree_html(
"sidebar",
show_nav_level=theme_show_nav_level | int,
maxdepth=theme_navigation_depth | int,
collapse=theme_collapse_navigation | tobool,
includehidden=theme_sidebar_includehidden | tobool,
titles_only=True
)
-}}
</div>
</nav>
@@ -1,7 +0,0 @@
{# Displays an icon to switch between light mode, dark mode, and auto (use browser's setting). #}
{# As the theme switcher will only work when JavaScript is enabled, we hide it with `pst-js-only`. #}
<button class="btn btn-sm nav-link pst-navbar-icon theme-switch-button pst-js-only" aria-label="{{ _('Color mode') }}" data-bs-title="{{ _('Color mode') }}" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="theme-switch fa-solid fa-sun fa-lg" data-mode="light" title="{{ _('Light') }}"></i>
<i class="theme-switch fa-solid fa-moon fa-lg" data-mode="dark" title="{{ _('Dark') }}"></i>
<i class="theme-switch fa-solid fa-circle-half-stroke fa-lg" data-mode="auto" title="{{ _('System Settings') }}"></i>
</button>
@@ -1 +0,0 @@
<script src="_static/banner-override.js"></script>
-210
View File
@@ -1,210 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import os
import sys
from pathlib import Path
from typing import Any
from sphinx.application import Sphinx
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
import agent_framework
import agent_framework_foundry
import agent_framework_azure
project = "agent_framework"
copyright = "2025, Microsoft"
author = "Microsoft"
version = "0.1.0b1"
release_override = os.getenv("SPHINX_RELEASE_OVERRIDE")
if release_override is None or release_override == "":
release = agent_framework.__version__
else:
release = release_override
sys.path.append(str(Path(".").resolve()))
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
"sphinx.ext.napoleon",
"sphinx.ext.autodoc",
"sphinx.ext.coverage",
"sphinx.ext.autosummary",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
"sphinx.ext.intersphinx",
"sphinx.ext.graphviz",
"sphinxext.rediraffe",
"sphinx_design",
"sphinx_copybutton",
"_extension.gallery_directive",
"myst_nb",
"sphinxcontrib.autodoc_pydantic",
"_extension.code_lint",
]
suppress_warnings = ["myst.header"]
# Napoleon settings
napoleon_google_docstring = True
napoleon_use_admonition_for_examples = False
napoleon_include_init_with_doc = True
napoleon_custom_sections = [("returns_style", "params_style")]
templates_path = ["_templates"]
# TODO: include all notebooks excluding those requiring remote API access.
nb_execution_mode = "off"
# Guides and tutorials must succeed.
nb_execution_raise_on_error = True
nb_execution_timeout = 60
myst_heading_anchors = 5
myst_enable_extensions = [
"colon_fence",
"linkify",
"strikethrough",
]
if (path := os.getenv("PY_DOCS_DIR")) is None:
path = "dev"
if (switcher_version := os.getenv("PY_SWITCHER_VERSION")) is None:
switcher_version = "dev"
html_baseurl = f"/agent-framework/{path}/"
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_title = "Agent Framework"
html_theme = "pydata_sphinx_theme"
html_static_path = ["_static"]
html_css_files = ["custom.css"]
add_module_names = False
html_logo = "_static/images/logo/logo.svg"
html_favicon = "_static/images/logo/favicon-512x512.png"
html_theme_options = {
"header_links_before_dropdown": 6,
"navbar_align": "left",
"check_switcher": False,
# "navbar_start": ["navbar-logo", "version-switcher"],
# "switcher": {
# "json_url": "/_static/switcher.json",
# },
"show_prev_next": True,
"icon_links": [
{
"name": "GitHub",
"url": "https://github.com/microsoft/agent-framework",
"icon": "fa-brands fa-github",
},
],
"footer_start": ["copyright"],
"footer_center": ["footer-middle-links"],
"footer_end": ["theme-version", "version-banner-override"],
"pygments_light_style": "xcode",
"pygments_dark_style": "monokai",
"navbar_start": ["navbar-logo", "version-switcher"],
"switcher": {
"json_url": "https://raw.githubusercontent.com/microsoft/agent-framework/refs/heads/main/docs/switcher.json",
"version_match": switcher_version,
},
"show_version_warning_banner": True,
}
html_js_files = ["custom-icon.js", "banner-override.js", "custom.js"]
html_sidebars = {"packages/index": []}
html_context = {
"display_github": True,
"github_user": "microsoft",
"github_repo": "agent-framework",
"github_version": "main",
"doc_path": "python/docs/agent-framework/",
}
autoclass_content = "both"
autodoc_default_options = {
"members": True,
"member-order": "alphabetical",
"undoc-members": True,
"show-inheritance": True,
"imported-members": True,
}
autodoc_pydantic_model_show_json = False
autodoc_pydantic_model_show_config_summary = False
autodoc_pydantic_model_show_json_error_strategy = "coerce"
python_use_unqualified_type_names = True
autodoc_preserve_defaults = True
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
code_lint_path_prefix = "reference/python"
nb_mime_priority_overrides = [
("code_lint", "image/jpeg", 100),
("code_lint", "image/png", 100),
("code_lint", "text/plain", 100),
]
rediraffe_redirects = {}
def setup_to_main(app: Sphinx, pagename: str, templatename: str, context, doctree) -> None:
"""Add a function that jinja can access for returning an "edit this page" link pointing to `main`."""
def to_main(link: str) -> str:
"""Transform "edit on github" links and make sure they always point to the main branch.
Args:
link: the link to the github edit interface
Returns:
the link to the tip of the main branch for the same file
"""
links = link.split("/")
idx = links.index("edit")
return "/".join(links[: idx + 1]) + "/main/" + "/".join(links[idx + 2 :])
context["to_main"] = to_main
def setup(app: Sphinx) -> dict[str, Any]:
"""Add custom configuration to sphinx app.
Args:
app: the Sphinx application
Returns:
the 2 parallel parameters set to ``True``.
"""
app.connect("html-page-context", setup_to_main)
# Adding here so it is inline and not in a separate file.
clarity_analytics = """(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "lnxpe6skj1");"""
app.add_js_file(None, body=clarity_analytics)
return {
"parallel_read_safe": True,
"parallel_write_safe": True,
}
-95
View File
@@ -1,95 +0,0 @@
---
myst:
html_meta:
"description lang=en": |
Top-level documentation for Agent Framework, a framework for developing applications using AI agents
html_theme.sidebar_secondary.remove: false
sd_hide_title: true
---
<style>
.hero-title {
font-size: 60px;
font-weight: bold;
margin: 2rem auto 0;
}
.wip-card {
border: 1px solid var(--pst-color-success);
background-color: var(--pst-color-success-bg);
border-radius: .25rem;
padding: 0.3rem;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 1rem;
}
</style>
# Agent Framework
<div class="container">
<div class="row text-center">
<div class="col-sm-12">
<h1 class="hero-title">
Agent Framework
</h1>
<h3>
A framework for building AI agents and applications
</h3>
</div>
</div>
</div>
<div style="margin-top: 2rem;">
::::{grid}
:gutter: 2
:::{grid-item-card} {fas}`cube;pst-color-primary` Agent Framework [![PyPi agent-framework](https://img.shields.io/badge/PyPi-agent--framework-blue?logo=pypi)](https://pypi.org/project/agent-framework/)
:shadow: none
:margin: 2 0 0 0
:columns: 12 12 12 12
Create and manage AI agents, workflows, and applications using the Agent Framework. It provides:
* Deterministic and dynamic agentic workflows for business processes.
* Research on multi-agent collaboration.
* Distributed agents for multi-language applications.
_Start here if you are getting serious about building multi-agent systems._
+++
```{button-ref} reference/index
:color: secondary
Get Started
```
:::
:::{grid-item-card} {fas}`puzzle-piece;pst-color-primary` Extensions [![PyPi agent-framework](https://img.shields.io/badge/PyPi-autogen--ext-blue?logo=pypi)](https://pypi.org/search/?q=agent-framework-)
:shadow: none
:margin: 2 0 0 0
:columns: 12 12 12 12
Implementations of connectors and other external components for the Agent Framework. These extensions allow you to connect to various AI models, services, and tools, enhancing the capabilities of your agents.
* {py:mod}`~agent-framework.azure` for using Azure services.
* {py:mod}`~agent-framework.foundry` for using Foundry models.
* {py:mod}`~agent-framework.openai` for using OpenAI models.
+++
:::
::::
</div>
```{toctree}
:maxdepth: 3
:hidden:
reference/index
```
@@ -1,19 +0,0 @@
---
myst:
html_meta:
"description lang=en": |
Agent Framework is a community-driven project. Learn how to get involved, contribute, and connect with the community.
---
# API Reference
```{toctree}
:caption: Agent Framework
:maxdepth: 2
python/agent_framework
python/agent_framework.exceptions
python/agent_framework.openai
python/agent_framework.azure
python/agent_framework.foundry
```
@@ -1,4 +0,0 @@
agent_framework.azure
==========================
.. automodule:: agent_framework.azure
@@ -1,5 +0,0 @@
agent_framework.exceptions
==========================
.. automodule:: agent_framework.exceptions
:member-order: bysource
@@ -1,4 +0,0 @@
agent_framework.foundry
==========================
.. automodule:: agent_framework.foundry
@@ -1,4 +0,0 @@
agent_framework.openai
==========================
.. automodule:: agent_framework.openai
@@ -1,4 +0,0 @@
agent_framework
===============
.. automodule:: agent_framework
+107
View File
@@ -0,0 +1,107 @@
# Copyright (c) Microsoft. All rights reserved.
import debugpy
import asyncio
import json
import os
from pathlib import Path
from dotenv import load_dotenv
from agent_framework import __version__ as agent_framework_version
from py2docfx.__main__ import main as py2docfx_main
load_dotenv()
async def generate_af_docs(root_path: Path):
"""Generate documentation for the Agent Framework using py2docfx.
This function runs the py2docfx command with the specified parameters.
"""
package = {
"packages": [
{
"package_info": {
"name": "agent-framework",
"version": agent_framework_version,
"install_type": "pypi",
"extras": ["all"],
},
"sphinx_extensions": [
"sphinxcontrib.autodoc_pydantic",
"sphinx-pydantic",
"sphinx.ext.autosummary"
],
"extension_config": {
"napoleon_google_docstring": True,
"napoleon_preprocess_types": True,
"napoleon_use_param": True,
"autodoc_pydantic_field_doc_policy": "both",
"autodoc_pydantic_model_show_json": False,
"autodoc_pydantic_model_show_config_summary": True,
"autodoc_pydantic_model_show_field_summary": True,
"autodoc_pydantic_model_hide_paramlist": False,
"autodoc_pydantic_model_show_json_error_strategy": "coerce",
"autodoc_pydantic_settings_show_config_summary": True,
"autodoc_pydantic_settings_show_field_summary": True,
"python_use_unqualified_type_names": True,
"autodoc_preserve_defaults": True,
"autodoc_class_signature": "separated",
"autodoc_typehints": "both",
"autodoc_typehints_format": "fully-qualified",
"autodoc_default_options": {
"members": True,
"member-order": "alphabetical",
"undoc-members": True,
"show-inheritance": True,
"imported-members": True,
"inherited-members": 'AFBaseModel',
},
},
}
],
"required_packages": [
{
"install_type": "pypi",
"name": "autodoc_pydantic",
"version": ">=2.0.0",
},
{
"install_type": "pypi",
"name": "sphinx-pydantic",
}
],
}
args = [
"-o",
str((root_path / "docs" / "build").absolute()),
"-j",
json.dumps(package),
]
try:
await py2docfx_main(args)
except Exception as e:
print(f"Error generating documentation: {e}")
if __name__ == "__main__":
# Ensure the script is run from the correct directory
debug = False
if debug:
debugpy.listen(("localhost", 5678))
debugpy.wait_for_client()
debugpy.breakpoint()
current_path = Path(__file__).parent.parent.resolve()
print(f"Current path: {current_path}")
# ensure the dist folder exists
dist_path = current_path / "dist"
if not dist_path.exists():
print(" Please run `poe build` to generate the dist folder.")
exit(1)
if os.getenv("PIP_FIND_LINKS") != str(dist_path.absolute()):
print(f"Setting PIP_FIND_LINKS to {dist_path.absolute()}")
os.environ["PIP_FIND_LINKS"] = str(dist_path.absolute())
print(f"Generating documentation in: {current_path / 'docs' / 'build'}")
# Generate the documentation
asyncio.run(generate_af_docs(current_path))
+2 -6
View File
@@ -82,10 +82,6 @@ include = "../../shared_tasks.toml"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure"
test = "pytest --cov=agent_framework_azure --cov-report=term-missing:skip-covered tests"
[tool.uv.build-backend]
module-name = "agent_framework_azure"
module-root = ""
[build-system]
requires = ["uv_build>=0.8.2,<0.9.0"]
build-backend = "uv_build"
requires = ["flit-core >= 3.9,<4.0"]
build-backend = "flit_core.buildapi"
+7 -7
View File
@@ -23,7 +23,7 @@ Before using the Copilot Studio agent, you need:
The following environment variables are used for configuration:
- `COPILOTSTUDIOAGENT__ENVIRONMENTID` - Your Copilot Studio environment ID
- `COPILOTSTUDIOAGENT__SCHEMANAME` - Your copilot's agent identifier/schema name
- `COPILOTSTUDIOAGENT__SCHEMANAME` - Your copilot's agent identifier/schema name
- `COPILOTSTUDIOAGENT__AGENTAPPID` - Your App Registration client ID
- `COPILOTSTUDIOAGENT__TENANTID` - Your Azure AD tenant ID
@@ -36,7 +36,7 @@ from agent_framework.copilotstudio import CopilotStudioAgent
async def main():
# Create agent using environment variables
agent = CopilotStudioAgent()
# Run a simple query
result = await agent.run("What is the capital of France?")
print(result)
@@ -58,19 +58,20 @@ async def main():
client_id=os.environ["COPILOTSTUDIOAGENT__AGENTAPPID"],
tenant_id=os.environ["COPILOTSTUDIOAGENT__TENANTID"]
)
# Create connection settings
settings = ConnectionSettings(
environment_id=os.environ["COPILOTSTUDIOAGENT__ENVIRONMENTID"],
agent_identifier=os.environ["COPILOTSTUDIOAGENT__SCHEMANAME"],
cloud=PowerPlatformCloud.PROD,
copilot_agent_type=AgentType.PUBLISHED
copilot_agent_type=AgentType.PUBLISHED,
custom_power_platform_cloud=None
)
# Create client and agent
client = CopilotClient(settings=settings, token=token)
agent = CopilotStudioAgent(client=client)
# Run a query
result = await agent.run("What is the capital of Italy?")
print(result)
@@ -94,4 +95,3 @@ For more comprehensive examples, see the [Copilot Studio examples](https://githu
- Explicit settings and manual token acquisition
- Different authentication patterns
- Error handling and troubleshooting
+2 -6
View File
@@ -84,10 +84,6 @@ include = "../../shared_tasks.toml"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_foundry"
test = "pytest --cov=agent_framework_foundry --cov-report=term-missing:skip-covered tests"
[tool.uv.build-backend]
module-name = "agent_framework_foundry"
module-root = ""
[build-system]
requires = ["uv_build>=0.8.2,<0.9.0"]
build-backend = "uv_build"
requires = ["flit-core >= 3.9,<4.0"]
build-backend = "flit_core.buildapi"
+4 -4
View File
@@ -50,10 +50,10 @@ You can also override environment variables by explicitly passing configuration
from agent_framework.azure import AzureChatClient
chat_client = AzureChatClient(
api_key=...,
endpoint=...,
deployment_name=...,
api_version=...,
api_key="",
endpoint="",
deployment_name="",
api_version="",
)
```
@@ -17,6 +17,8 @@ else:
# region Context
__all__ = ["AggregateContextProvider", "Context", "ContextProvider"]
class Context(AFBaseModel):
"""A class containing any context that should be provided to the AI model as supplied by an ContextProvider.
+15 -10
View File
@@ -73,6 +73,9 @@ DEFAULT_MAX_ITERATIONS: Final[int] = 10
TChatClient = TypeVar("TChatClient", bound="ChatClientProtocol")
# region Helpers
ArgsT = TypeVar("ArgsT", bound=BaseModel)
ReturnT = TypeVar("ReturnT")
def _parse_inputs(
inputs: "Contents | dict[str, Any] | str | list[Contents | dict[str, Any] | str] | None",
@@ -121,13 +124,10 @@ def _parse_inputs(
class ToolProtocol(Protocol):
"""Represents a generic tool that can be specified to an AI service.
Attributes:
Parameters:
name: The name of the tool.
description: A description of the tool.
additional_properties: Additional properties associated with the tool.
Methods:
parameters: The parameters accepted by the tool, in a json schema format.
"""
name: str
@@ -142,10 +142,6 @@ class ToolProtocol(Protocol):
...
ArgsT = TypeVar("ArgsT", bound=BaseModel)
ReturnT = TypeVar("ReturnT")
class BaseTool(AFBaseModel):
"""Base class for AI tools, providing common attributes and methods.
@@ -516,11 +512,20 @@ def ai_function(
In order to add descriptions to parameters, in your function signature,
use the `Annotated` type from `typing` and the `Field` class from `pydantic`:
from typing import Annotated
Example:
.. code-block:: python
from typing import Annotated
from pydantic import Field
<field_name>: Annotated[<type>, Field(description="<description>")]
def ai_function_example(
arg1: Annotated[str, Field(description="The first argument")],
arg2: Annotated[int, Field(description="The second argument")],
) -> str:
# An example function that takes two arguments and returns a string.
return f"arg1: {arg1}, arg2: {arg2}"
Args:
func: The function to wrap. If None, returns a decorator.
@@ -1297,7 +1297,7 @@ FinishReason.TOOL_CALLS = FinishReason(value="tool_calls") # type: ignore[assig
class ChatMessage(AFBaseModel):
"""Represents a chat message used by a `ModelClient`.
"""Represents a chat message.
Attributes:
role: The role of the author of the message.
+12 -6
View File
@@ -49,6 +49,16 @@ workflow = [
runtime = [
"agent-framework-runtime"
]
mem0 = [
"agent-framework-mem0"
]
all = [
"agent-framework-azure",
"agent-framework-foundry",
"agent-framework-workflow",
"agent-framework-runtime",
"agent-framework-mem0"
]
[tool.uv]
prerelease = "if-necessary-or-explicit"
@@ -106,10 +116,6 @@ include = "../../shared_tasks.toml"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework"
test = "pytest --cov=agent_framework --cov-report=term-missing:skip-covered tests"
[tool.uv.build-backend]
module-name = "agent_framework"
module-root = ""
[build-system]
requires = ["uv_build>=0.8.2,<0.9.0"]
build-backend = "uv_build"
requires = ["flit-core >= 3.9,<4.0"]
build-backend = "flit_core.buildapi"
+2 -6
View File
@@ -84,10 +84,6 @@ include = "../../shared_tasks.toml"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_mem0"
test = "pytest --cov=agent_framework_mem0 --cov-report=term-missing:skip-covered tests"
[tool.uv.build-backend]
module-name = "agent_framework_mem0"
module-root = ""
[build-system]
requires = ["uv_build>=0.8.2,<0.9.0"]
build-backend = "uv_build"
requires = ["flit-core >= 3.9,<4.0"]
build-backend = "flit_core.buildapi"
+2 -6
View File
@@ -81,10 +81,6 @@ include = "../../shared_tasks.toml"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_runtime"
test = "pytest --cov=agent_framework_runtime --cov-report=term-missing:skip-covered tests"
[tool.uv.build-backend]
module-name = "agent_framework_runtime"
module-root = ""
[build-system]
requires = ["uv_build>=0.8.2,<0.9.0"]
build-backend = "uv_build"
requires = ["flit-core >= 3.9,<4.0"]
build-backend = "flit_core.buildapi"
+2 -6
View File
@@ -86,10 +86,6 @@ include = "../../shared_tasks.toml"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_workflow"
test = "pytest --cov=agent_framework_workflow --cov-report=term-missing:skip-covered tests"
[tool.uv.build-backend]
module-name = "agent_framework_workflow"
module-root = ""
[build-system]
requires = ["uv_build>=0.8.2,<0.9.0"]
build-backend = "uv_build"
requires = ["flit-core >= 3.9,<4.0"]
build-backend = "flit_core.buildapi"
+33 -22
View File
@@ -29,23 +29,12 @@ dev = [
"rich",
"tomli",
"tomli-w",
"markdownify",
]
docs = [
# Documentation
"myst-nb==1.1.2",
"pydata-sphinx-theme==0.16.1",
"sphinx-copybutton",
"sphinx-design",
"sphinx",
"autodoc_pydantic>=2",
"pygments",
"sphinxext-rediraffe",
"opentelemetry-instrumentation-openai",
"markdown-it-py[linkify]",
# Documentation tooling
"diskcache",
"redis",
"sphinx-autobuild",
"aiofiles>=24.1.0",
"debugpy>=1.8.16",
"py2docfx>=0.1.20",
"pip",
]
[tool.uv]
@@ -175,15 +164,14 @@ exclude_dirs = ["tests", "./run_tasks_in_packages_if_exists.py", "./check_md_cod
executor.type = "uv"
[tool.poe.tasks]
markdown-code-lint = """python check_md_code_blocks.py ./docs/agent-framework/**/*.md README.md"""
markdown-code-lint = """python check_md_code_blocks.py README.md ./packages/**/README.md ./samples/**/*.md"""
samples-code-check = """pyright ./samples"""
docs-install = "uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit --group=docs"
docs-clean = "rm -rf docs/build"
docs-build = "sphinx-build docs/agent-framework docs/build"
docs-serve = "sphinx-autobuild --watch docs/agent-framework docs/build --port 8000 --jobs auto"
docs-check = "sphinx-build --fail-on-warning docs/agent-framework docs/build"
docs-check-examples = "sphinx-build -b code_lint docs/agent-framework docs/build"
docs-build = "uv run python ./docs/generate_docs.py"
docs-debug = "uv run python -m debugpy --listen 5678 ./docs/generate_docs.py"
pre-commit-install = "uv run pre-commit install --install-hooks --overwrite"
install = "uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit"
install = "uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit --no-group=docs"
test = "python run_tasks_in_packages_if_exists.py test"
fmt = "python run_tasks_in_packages_if_exists.py fmt"
format.ref = "fmt"
@@ -206,6 +194,29 @@ sequence = [
{ ref = "pre-commit-install" }
]
args = [{ name = "python", default = "3.13", options = ['-p', '--python'] }]
[tool.poe.tasks.docs-full]
sequence = [
{ ref = 'build' },
{ ref = "docs-clean" },
{ ref = "docs-build" }
]
[tool.poe.tasks.docs-full-install]
sequence = [
{ ref = "docs-install" },
{ ref = 'build' },
{ ref = "docs-clean" },
{ ref = "docs-build" }
]
[tool.poe.tasks.docs-rebuild]
sequence = [
{ ref = "docs-clean" },
{ ref = "docs-build" }
]
[tool.poe.tasks.docs-rebuild-debug]
sequence = [
{ ref = "docs-clean" },
{ ref = "docs-debug" }
]
[tool.setuptools.packages.find]
where = ["packages"]
@@ -29,15 +29,15 @@ The examples use MSAL (Microsoft Authentication Library) for authentication. The
Your Azure AD App Registration should have:
1. **API Permissions**:
1. **API Permissions**:
- Power Platform API permissions (https://api.powerplatform.com/.default)
- Appropriate delegated permissions for your organization
2. **Redirect URIs**:
- For public client flows: `http://localhost`
2. **Redirect URIs**:
- For public client flows: `http://localhost`
- Configure as appropriate for your authentication method
3. **Authentication**:
3. **Authentication**:
- Enable "Allow public client flows" if using interactive authentication
## Usage Patterns
@@ -45,11 +45,19 @@ Your Azure AD App Registration should have:
### Basic Usage with Environment Variables
```python
import asyncio
from agent_framework.copilotstudio import CopilotStudioAgent
# Uses environment variables for configuration
agent = CopilotStudioAgent()
result = await agent.run("What is the capital of France?")
async def main():
# Create agent using environment variables
agent = CopilotStudioAgent()
# Run a simple query
result = await agent.run("What is the capital of France?")
print(result)
asyncio.run(main())
```
### Explicit Configuration
@@ -69,7 +77,8 @@ settings = ConnectionSettings(
environment_id="your-environment-id",
agent_identifier="your-agent-schema-name",
cloud=PowerPlatformCloud.PROD,
copilot_agent_type=AgentType.PUBLISHED
copilot_agent_type=AgentType.PUBLISHED,
custom_power_platform_cloud=None
)
client = CopilotClient(settings=settings, token=token)
@@ -80,7 +89,7 @@ agent = CopilotStudioAgent(client=client)
### Common Issues
1. **Authentication Errors**:
1. **Authentication Errors**:
- Verify your App Registration has correct permissions
- Ensure environment variables are set correctly
- Check that your tenant ID and client ID are valid
+279 -1142
View File
File diff suppressed because it is too large Load Diff