mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: added poe setup and docs (#131)
* added poe setup and docs * smaller bandit exclude * updated poe * updated naming * added something in samples * exclude docs from bandit * updated readme * removed ds_store * updated readme
This commit is contained in:
committed by
GitHub
Unverified
parent
94c5d59984
commit
91c5414836
+6
-4
@@ -113,9 +113,9 @@ ipython_config.py
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
# .pdm.toml
|
||||
# .pdm-python
|
||||
# .pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
@@ -171,4 +171,6 @@ cython_debug/
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
.pypirc
|
||||
|
||||
.DS_Store
|
||||
Vendored
BIN
Binary file not shown.
@@ -1,18 +1,24 @@
|
||||
files: ^python/
|
||||
fail_fast: true
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-toml
|
||||
name: Check TOML files
|
||||
files: \.toml$
|
||||
- id: check-yaml
|
||||
name: Check YAML files
|
||||
files: \.yaml$
|
||||
- id: check-json
|
||||
name: Check JSON files
|
||||
files: \.json$
|
||||
exclude: ^python\/\.vscode\/.*
|
||||
- id: end-of-file-fixer
|
||||
name: Fix End of File
|
||||
files: \.py$
|
||||
- id: mixed-line-ending
|
||||
name: Check Mixed Line Endings
|
||||
files: \.py$
|
||||
- id: check-ast
|
||||
name: Check Valid Python Samples
|
||||
@@ -27,25 +33,29 @@ repos:
|
||||
rev: v3.20.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
name: Upgrade Python syntax
|
||||
args: [--py310-plus]
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.12.1
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
- id: poe-check
|
||||
name: Run checks through Poe
|
||||
entry: uv --directory ./python run poe pre-commit-check
|
||||
language: system
|
||||
files: ^python/
|
||||
pass_filenames: false
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
# uv version.
|
||||
rev: 0.7.18
|
||||
hooks:
|
||||
# Update the uv lockfile
|
||||
- id: uv-lock
|
||||
name: Update uv lockfile
|
||||
files: python/pyproject.toml
|
||||
args: [--project, python]
|
||||
# TODO: add pyright through poe like in AG
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.8.5
|
||||
hooks:
|
||||
- id: bandit
|
||||
name: Bandit Security Checks
|
||||
args: ["-c", "python/pyproject.toml"]
|
||||
additional_dependencies: ["bandit[toml]"]
|
||||
|
||||
Vendored
+107
-19
@@ -32,14 +32,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Mypy",
|
||||
"label": "Format",
|
||||
"type": "shell",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"mypy",
|
||||
"-p",
|
||||
"agent_framework",
|
||||
"poe",
|
||||
"fmt",
|
||||
],
|
||||
"problemMatcher": {
|
||||
"owner": "python",
|
||||
@@ -60,22 +59,112 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Tests - Code Coverage",
|
||||
"label": "Lint",
|
||||
"type": "shell",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"pytest",
|
||||
"--cov=semantic_kernel",
|
||||
"--cov-report=term-missing:skip-covered",
|
||||
"tests/unit/"
|
||||
"poe",
|
||||
"lint",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
"problemMatcher": {
|
||||
"owner": "python",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"pattern": {
|
||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"message": 4
|
||||
}
|
||||
},
|
||||
"problemMatcher": []
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Mypy",
|
||||
"type": "shell",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"poe",
|
||||
"mypy",
|
||||
],
|
||||
"problemMatcher": {
|
||||
"owner": "python",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"pattern": {
|
||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"message": 4
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Pyright",
|
||||
"type": "shell",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"poe",
|
||||
"pyright",
|
||||
],
|
||||
"problemMatcher": {
|
||||
"owner": "python",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"pattern": {
|
||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"message": 4
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Test",
|
||||
"type": "shell",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"poe",
|
||||
"test",
|
||||
],
|
||||
"problemMatcher": {
|
||||
"owner": "python",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"pattern": {
|
||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"message": 4
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Create Venv",
|
||||
@@ -92,11 +181,10 @@
|
||||
"type": "shell",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"sync",
|
||||
"--all-extras",
|
||||
"--dev",
|
||||
"-U",
|
||||
"--prerelease=if-necessary-or-explicit"
|
||||
"run",
|
||||
"poe",
|
||||
"uv-setup",
|
||||
"--python=${input:py_version}"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
|
||||
+153
-2
@@ -1,8 +1,159 @@
|
||||
# Python
|
||||
|
||||
## Checks
|
||||
This project uses [poethepoet](https://github.com/nat-n/poethepoet) for task management and [uv](https://github.com/astral-sh/uv) for dependency management.
|
||||
|
||||
Run all checks using pre-commit:
|
||||
## Available Poe Tasks
|
||||
|
||||
### Setup and Installation
|
||||
|
||||
Once uv is installed, and you do not yet have a virtual environment setup:
|
||||
|
||||
```bash
|
||||
uv venv
|
||||
```
|
||||
|
||||
and then you can run the following tasks:
|
||||
```bash
|
||||
uv sync --all-extras --dev
|
||||
```
|
||||
|
||||
After this initial setup, you can use the following tasks to manage your development environment, it is adviced to use the following setup command since that also installs the pre-commit hooks.
|
||||
|
||||
#### `setup`
|
||||
Set up the development environment with a virtual environment, install dependencies and pre-commit hooks:
|
||||
```bash
|
||||
uv run poe setup
|
||||
# or with specific Python version
|
||||
uv run poe setup --python 3.12
|
||||
```
|
||||
|
||||
#### `install`
|
||||
Install all dependencies including extras and dev dependencies, including updates:
|
||||
```bash
|
||||
uv run poe install
|
||||
```
|
||||
|
||||
#### `venv`
|
||||
Create a virtual environment with specified Python version or switch python version:
|
||||
```bash
|
||||
uv run poe venv
|
||||
# or with specific Python version
|
||||
uv run poe venv --python 3.12
|
||||
```
|
||||
|
||||
#### `pre-commit-install`
|
||||
Install pre-commit hooks:
|
||||
```bash
|
||||
uv run poe pre-commit-install
|
||||
```
|
||||
|
||||
### Code Quality and Formatting
|
||||
|
||||
Each of the following tasks are designed to run against both the main `agent-framework` package and the extension packages, ensuring consistent code quality across the project.
|
||||
|
||||
#### `fmt` (format)
|
||||
Format code using ruff:
|
||||
```bash
|
||||
uv run poe fmt
|
||||
```
|
||||
|
||||
#### `lint`
|
||||
Run linting checks and fix issues:
|
||||
```bash
|
||||
uv run poe lint
|
||||
```
|
||||
|
||||
#### `pyright`
|
||||
Run Pyright type checking:
|
||||
```bash
|
||||
uv run poe pyright
|
||||
```
|
||||
|
||||
#### `mypy`
|
||||
Run MyPy type checking:
|
||||
```bash
|
||||
uv run poe mypy
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
#### `test`
|
||||
Run unit tests with coverage:
|
||||
```bash
|
||||
uv run poe test
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
#### `docs-clean`
|
||||
Remove the docs build directory:
|
||||
```bash
|
||||
uv run poe docs-clean
|
||||
```
|
||||
|
||||
#### `docs-build`
|
||||
Build the documentation:
|
||||
```bash
|
||||
uv run poe docs-build
|
||||
```
|
||||
|
||||
#### `docs-serve`
|
||||
Serve documentation locally with auto-reload:
|
||||
```bash
|
||||
uv run poe docs-serve
|
||||
```
|
||||
|
||||
#### `docs-check`
|
||||
Build documentation and fail on warnings:
|
||||
```bash
|
||||
uv run poe docs-check
|
||||
```
|
||||
|
||||
#### `docs-check-examples`
|
||||
Check documentation examples for code correctness:
|
||||
```bash
|
||||
uv run poe docs-check-examples
|
||||
```
|
||||
|
||||
### Code Validation
|
||||
|
||||
#### `markdown-code-lint`
|
||||
Lint markdown code blocks:
|
||||
```bash
|
||||
uv run poe markdown-code-lint
|
||||
```
|
||||
|
||||
#### `samples-code-check`
|
||||
Run type checking on samples:
|
||||
```bash
|
||||
uv run poe samples-code-check
|
||||
```
|
||||
|
||||
### Comprehensive Checks
|
||||
|
||||
#### `check`
|
||||
Run all quality checks (format, lint, pyright, mypy, test, markdown lint, samples check):
|
||||
```bash
|
||||
uv run poe check
|
||||
```
|
||||
|
||||
#### `pre-commit-check`
|
||||
Run pre-commit specific checks (all of the above, excluding `mypy`):
|
||||
```bash
|
||||
uv run poe pre-commit-check
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
#### `build`
|
||||
Build the package:
|
||||
```bash
|
||||
uv run poe build
|
||||
```
|
||||
|
||||
## Pre-commit Hooks
|
||||
|
||||
You can also run all checks using pre-commit directly:
|
||||
|
||||
```bash
|
||||
uv run pre-commit run -a
|
||||
|
||||
@@ -92,7 +92,15 @@ def ai_function(
|
||||
description: str | None = None,
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
) -> AIFunction[Any, ReturnT] | Callable[[Callable[..., ReturnT | Awaitable[ReturnT]]], AIFunction[Any, ReturnT]]:
|
||||
"""Create a AIFunction from a function and return the callable tool object."""
|
||||
"""Decorate a function to turn it into a AIFunction that can be passed to models.
|
||||
|
||||
Args:
|
||||
func: The function to wrap. If None, returns a decorator.
|
||||
name: The name of the tool. Defaults to the function's name.
|
||||
description: A description of the tool. Defaults to the function's docstring.
|
||||
additional_properties: Additional properties to set on the tool.
|
||||
|
||||
"""
|
||||
|
||||
def wrapper(f: Callable[..., ReturnT | Awaitable[ReturnT]]) -> AIFunction[Any, ReturnT]:
|
||||
tool_name: str = name or getattr(f, "__name__", "unknown_function") # type: ignore[assignment]
|
||||
@@ -106,7 +114,9 @@ def ai_function(
|
||||
for pname, param in sig.parameters.items()
|
||||
if pname not in {"self", "cls"}
|
||||
}
|
||||
input_model = create_model(f"{tool_name}_input", **fields) # type: ignore[call-overload]
|
||||
input_model: Any = create_model(f"{tool_name}_input", **fields) # type: ignore[call-overload]
|
||||
if not issubclass(input_model, BaseModel):
|
||||
raise TypeError(f"Input model for {tool_name} must be a subclass of BaseModel, got {input_model}")
|
||||
|
||||
return functools.update_wrapper( # type: ignore[return-value]
|
||||
AIFunction[Any, ReturnT](
|
||||
|
||||
@@ -71,7 +71,7 @@ class UsageDetails(AFBaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True, arbitrary_types_allowed=True, validate_assignment=True, extra="allow"
|
||||
)
|
||||
__pydantic_extra__: dict[str, int]
|
||||
__pydantic_extra__: dict[str, int] # type: ignore[reportIncompatibleVariableOverride]
|
||||
"""Overriding the default extras type, to make sure all extras are integers."""
|
||||
|
||||
input_token_count: int | None = None
|
||||
@@ -81,6 +81,29 @@ class UsageDetails(AFBaseModel):
|
||||
total_token_count: int | None = None
|
||||
"""The total number of tokens used to produce the response."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
input_token_count: int | None = None,
|
||||
output_token_count: int | None = None,
|
||||
total_token_count: int | None = None,
|
||||
**kwargs: int,
|
||||
) -> None:
|
||||
"""Initializes the UsageDetails instance.
|
||||
|
||||
Args:
|
||||
input_token_count: The number of tokens in the input.
|
||||
output_token_count: The number of tokens in the output.
|
||||
total_token_count: The total number of tokens used to produce the response.
|
||||
**kwargs: Additional token counts, can be set by passing keyword arguments.
|
||||
They can be retrieved through the `additional_counts` property.
|
||||
"""
|
||||
super().__init__(
|
||||
input_token_count=input_token_count, # type: ignore[reportCallIssue]
|
||||
output_token_count=output_token_count, # type: ignore[reportCallIssue]
|
||||
total_token_count=total_token_count, # type: ignore[reportCallIssue]
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@property
|
||||
def additional_counts(self) -> dict[str, int]:
|
||||
"""Represents well-known additional counts for usage. This is not an exhaustive list.
|
||||
@@ -112,29 +135,6 @@ class UsageDetails(AFBaseModel):
|
||||
**additional_counts,
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
input_token_count: int | None = None,
|
||||
output_token_count: int | None = None,
|
||||
total_token_count: int | None = None,
|
||||
**kwargs: int,
|
||||
) -> None:
|
||||
"""Initializes the UsageDetails instance.
|
||||
|
||||
Args:
|
||||
input_token_count: The number of tokens in the input.
|
||||
output_token_count: The number of tokens in the output.
|
||||
total_token_count: The total number of tokens used to produce the response.
|
||||
**kwargs: Additional token counts, can be set by passing keyword arguments.
|
||||
They can be retrieved through the `additional_counts` property.
|
||||
"""
|
||||
super().__init__(
|
||||
input_token_count=input_token_count,
|
||||
output_token_count=output_token_count,
|
||||
total_token_count=total_token_count,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def _process_update(response: "ChatResponse", update: "ChatResponseUpdate") -> None:
|
||||
"""Processes a single update and modifies the response in place."""
|
||||
@@ -185,7 +185,7 @@ def _coalesce_text_content(
|
||||
if not contents:
|
||||
return
|
||||
coalesced_contents: list["AIContents"] = []
|
||||
current_texts = []
|
||||
current_texts: list[str] = []
|
||||
first_new_content = None
|
||||
for i, content in enumerate(contents):
|
||||
if isinstance(content, type_):
|
||||
@@ -267,7 +267,7 @@ class TextContent(AIContent):
|
||||
**kwargs: Any additional keyword arguments.
|
||||
"""
|
||||
super().__init__(
|
||||
text=text,
|
||||
text=text, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
@@ -309,7 +309,7 @@ class TextReasoningContent(AIContent):
|
||||
**kwargs: Any additional keyword arguments.
|
||||
"""
|
||||
super().__init__(
|
||||
text=text,
|
||||
text=text, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
@@ -410,7 +410,7 @@ class DataContent(AIContent):
|
||||
raise ValueError("Either 'data' and 'media_type' or 'uri' must be provided.")
|
||||
uri = f"data:{media_type};base64,{base64.b64encode(data).decode('utf-8')}"
|
||||
super().__init__(
|
||||
uri=uri,
|
||||
uri=uri, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
@@ -477,8 +477,8 @@ class UriContent(AIContent):
|
||||
**kwargs: Any additional keyword arguments.
|
||||
"""
|
||||
super().__init__(
|
||||
uri=uri,
|
||||
media_type=media_type,
|
||||
uri=uri, # type: ignore[reportCallIssue]
|
||||
media_type=media_type, # type: ignore[reportCallIssue]
|
||||
additional_properties=additional_properties,
|
||||
raw_representation=raw_representation,
|
||||
**kwargs,
|
||||
@@ -532,9 +532,9 @@ class ErrorContent(AIContent):
|
||||
**kwargs: Any additional keyword arguments.
|
||||
"""
|
||||
super().__init__(
|
||||
message=message,
|
||||
error_code=error_code,
|
||||
details=details,
|
||||
message=message, # type: ignore[reportCallIssue]
|
||||
error_code=error_code, # type: ignore[reportCallIssue]
|
||||
details=details, # type: ignore[reportCallIssue]
|
||||
additional_properties=additional_properties,
|
||||
raw_representation=raw_representation,
|
||||
**kwargs,
|
||||
@@ -592,10 +592,10 @@ class FunctionCallContent(AIContent):
|
||||
**kwargs: Any additional keyword arguments.
|
||||
"""
|
||||
super().__init__(
|
||||
call_id=call_id,
|
||||
name=name,
|
||||
arguments=arguments,
|
||||
exception=exception,
|
||||
call_id=call_id, # type: ignore[reportCallIssue]
|
||||
name=name, # type: ignore[reportCallIssue]
|
||||
arguments=arguments, # type: ignore[reportCallIssue]
|
||||
exception=exception, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
@@ -644,9 +644,9 @@ class FunctionResultContent(AIContent):
|
||||
**kwargs: Any additional keyword arguments.
|
||||
"""
|
||||
super().__init__(
|
||||
call_id=call_id,
|
||||
result=result,
|
||||
exception=exception,
|
||||
call_id=call_id, # type: ignore[reportCallIssue]
|
||||
result=result, # type: ignore[reportCallIssue]
|
||||
exception=exception, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
@@ -685,8 +685,7 @@ class UsageContent(AIContent):
|
||||
**kwargs: Any additional keyword arguments.
|
||||
"""
|
||||
super().__init__(
|
||||
type=self.type,
|
||||
details=details,
|
||||
details=details, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
@@ -885,12 +884,12 @@ class ChatMessage(AFBaseModel):
|
||||
if isinstance(role, str):
|
||||
role = ChatRole(value=role)
|
||||
super().__init__(
|
||||
role=role,
|
||||
contents=contents,
|
||||
author_name=author_name,
|
||||
message_id=message_id,
|
||||
additional_properties=additional_properties,
|
||||
raw_representation=raw_representation,
|
||||
role=role, # type: ignore[reportCallIssue]
|
||||
contents=contents, # type: ignore[reportCallIssue]
|
||||
author_name=author_name, # type: ignore[reportCallIssue]
|
||||
message_id=message_id, # type: ignore[reportCallIssue]
|
||||
additional_properties=additional_properties, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation, # type: ignore[reportCallIssue]
|
||||
)
|
||||
|
||||
|
||||
@@ -1022,15 +1021,15 @@ class ChatResponse(AFBaseModel):
|
||||
messages.append(ChatMessage(role=ChatRole.ASSISTANT, contents=[text]))
|
||||
|
||||
super().__init__(
|
||||
messages=messages,
|
||||
response_id=response_id,
|
||||
conversation_id=conversation_id,
|
||||
ai_model_id=model_id,
|
||||
created_at=created_at,
|
||||
finish_reason=finish_reason,
|
||||
usage_details=usage_details,
|
||||
additional_properties=additional_properties,
|
||||
raw_representation=raw_representation,
|
||||
messages=messages, # type: ignore[reportCallIssue]
|
||||
response_id=response_id, # type: ignore[reportCallIssue]
|
||||
conversation_id=conversation_id, # type: ignore[reportCallIssue]
|
||||
ai_model_id=model_id, # type: ignore[reportCallIssue]
|
||||
created_at=created_at, # type: ignore[reportCallIssue]
|
||||
finish_reason=finish_reason, # type: ignore[reportCallIssue]
|
||||
usage_details=usage_details, # type: ignore[reportCallIssue]
|
||||
additional_properties=additional_properties, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation, # type: ignore[reportCallIssue]
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -1260,17 +1259,17 @@ class ChatResponseUpdate(AFBaseModel):
|
||||
contents.append(text)
|
||||
|
||||
super().__init__(
|
||||
contents=contents,
|
||||
additional_properties=additional_properties,
|
||||
author_name=author_name,
|
||||
conversation_id=conversation_id,
|
||||
created_at=created_at,
|
||||
finish_reason=finish_reason,
|
||||
message_id=message_id,
|
||||
ai_model_id=ai_model_id,
|
||||
raw_representation=raw_representation,
|
||||
response_id=response_id,
|
||||
role=role,
|
||||
contents=contents, # type: ignore[reportCallIssue]
|
||||
additional_properties=additional_properties, # type: ignore[reportCallIssue]
|
||||
author_name=author_name, # type: ignore[reportCallIssue]
|
||||
conversation_id=conversation_id, # type: ignore[reportCallIssue]
|
||||
created_at=created_at, # type: ignore[reportCallIssue]
|
||||
finish_reason=finish_reason, # type: ignore[reportCallIssue]
|
||||
message_id=message_id, # type: ignore[reportCallIssue]
|
||||
ai_model_id=ai_model_id, # type: ignore[reportCallIssue]
|
||||
raw_representation=raw_representation, # type: ignore[reportCallIssue]
|
||||
response_id=response_id, # type: ignore[reportCallIssue]
|
||||
role=role, # type: ignore[reportCallIssue]
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Check code blocks in Markdown files for syntax errors."""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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:
|
||||
lines = file.readlines()
|
||||
|
||||
code_blocks: list[tuple[str, int]] = []
|
||||
in_code_block = False
|
||||
current_block: list[str] = []
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip().startswith("```python"):
|
||||
in_code_block = True
|
||||
current_block = []
|
||||
elif line.strip().startswith("```"):
|
||||
in_code_block = False
|
||||
code_blocks.append(("\n".join(current_block), i - len(current_block) + 1))
|
||||
elif in_code_block:
|
||||
current_block.append(line)
|
||||
|
||||
return code_blocks
|
||||
|
||||
|
||||
def check_code_blocks(markdown_file_paths: list[str]) -> None:
|
||||
"""Check Python code blocks in a Markdown file for syntax errors."""
|
||||
files_with_errors = []
|
||||
|
||||
for markdown_file_path in markdown_file_paths:
|
||||
code_blocks = extract_python_code_blocks(markdown_file_path)
|
||||
had_errors = False
|
||||
for code_block, line_no in code_blocks:
|
||||
markdown_file_path_with_line_no = f"{markdown_file_path}:{line_no}"
|
||||
logger.info("Checking a code block in %s...", markdown_file_path_with_line_no)
|
||||
|
||||
# Skip blocks that don't import agent_framework modules
|
||||
if all(
|
||||
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]"))
|
||||
continue
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as temp_file:
|
||||
temp_file.write(code_block.encode("utf-8"))
|
||||
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)
|
||||
had_errors = True
|
||||
else:
|
||||
logger.info(" " + darkgreen("OK"))
|
||||
|
||||
if had_errors:
|
||||
files_with_errors.append(markdown_file_path)
|
||||
|
||||
if files_with_errors:
|
||||
raise RuntimeError("Syntax errors found in the following files:\n" + "\n".join(files_with_errors))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Check code blocks in Markdown files for syntax errors.")
|
||||
# Argument is a list of markdown files containing glob patterns
|
||||
parser.add_argument("markdown_files", nargs="+", help="Markdown files to check.")
|
||||
args = parser.parse_args()
|
||||
check_code_blocks(args.markdown_files)
|
||||
@@ -0,0 +1,37 @@
|
||||
## 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.
|
||||
@@ -0,0 +1,8 @@
|
||||
{%- if show_headings %}
|
||||
{{- basename | e | heading }}
|
||||
|
||||
{% endif -%}
|
||||
.. automodule:: {{ qualname }}
|
||||
{%- for option in automodule_options %}
|
||||
:{{ option }}:
|
||||
{%- endfor %}
|
||||
@@ -0,0 +1,53 @@
|
||||
{%- 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 %}
|
||||
@@ -0,0 +1,98 @@
|
||||
# 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,
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
"""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"}) \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,
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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)
|
||||
],
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,147 @@
|
||||
.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;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,4 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"name": "v0.2 (stable)",
|
||||
"version": "0.2",
|
||||
"url": "https://microsoft.github.io/autogen/0.2/"
|
||||
},
|
||||
{
|
||||
"version": "dev",
|
||||
"url": "https://microsoft.github.io/autogen/dev/"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1 @@
|
||||
<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>
|
||||
@@ -0,0 +1,39 @@
|
||||
{# 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>
|
||||
@@ -0,0 +1,38 @@
|
||||
{# 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>
|
||||
@@ -0,0 +1,39 @@
|
||||
{# 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>
|
||||
@@ -0,0 +1,32 @@
|
||||
{# 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>
|
||||
@@ -0,0 +1,15 @@
|
||||
{# 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>
|
||||
@@ -0,0 +1,7 @@
|
||||
{# 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>
|
||||
@@ -0,0 +1 @@
|
||||
<script src="_static/banner-override.js"></script>
|
||||
@@ -0,0 +1,200 @@
|
||||
# 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
|
||||
|
||||
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.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_custom_sections = [("Returns", "params_style")]
|
||||
|
||||
templates_path = ["_templates"]
|
||||
|
||||
autoclass_content = "class"
|
||||
|
||||
# TODO: incldue 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/",
|
||||
}
|
||||
|
||||
autodoc_default_options = {
|
||||
"members": True,
|
||||
"undoc-members": True,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
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 [](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 [](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:class}`~agent-framework-openai` for using OpenAI models.
|
||||
* {py:class}`~agent-framework-azure` for using Azure services.
|
||||
+++
|
||||
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
</div>
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 3
|
||||
:hidden:
|
||||
|
||||
reference/index
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
agent_framework.exceptions
|
||||
==========================
|
||||
|
||||
.. automodule:: agent_framework.exceptions
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:member-order: bysource
|
||||
@@ -0,0 +1,9 @@
|
||||
agent_framework
|
||||
===============
|
||||
|
||||
.. automodule:: agent_framework
|
||||
:members:
|
||||
:undoc-members:
|
||||
:imported-members:
|
||||
:show-inheritance:
|
||||
:member-order: groupwise
|
||||
@@ -0,0 +1,51 @@
|
||||
[project]
|
||||
name = "agent-framework-azure"
|
||||
description = "Azure integrations for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "0.1.0b1"
|
||||
license = {file = "../../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 :: 5 - Production/Stable",
|
||||
"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",
|
||||
"Framework :: Pydantic :: 2",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
dependencies = [
|
||||
"agent-framework",
|
||||
"agent-framework-openai",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
extend = "../../pyproject.toml"
|
||||
include = ["agent-framework/openai/**", "tests/*.py"]
|
||||
|
||||
[tool.poe]
|
||||
include = "../../shared_tasks.toml"
|
||||
|
||||
[tool.pyright]
|
||||
extends = "../../pyproject.toml"
|
||||
include = ["agent_framework", "samples"]
|
||||
|
||||
[tool.uv.sources]
|
||||
agent-framework = { workspace = true }
|
||||
agent-framework-openai = { workspace = true }
|
||||
|
||||
[tool.uv.build-backend]
|
||||
module-name = "azure"
|
||||
module-root = "agent_framework"
|
||||
|
||||
[build-system]
|
||||
requires = ["uv_build>=0.7.19,<0.8.0"]
|
||||
build-backend = "uv_build"
|
||||
@@ -0,0 +1,51 @@
|
||||
[project]
|
||||
name = "agent-framework-openai"
|
||||
description = "OpenAI integrations for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "0.1.0b1"
|
||||
license = {file = "../../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 :: 5 - Production/Stable",
|
||||
"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",
|
||||
"Framework :: Pydantic :: 2",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
dependencies = [
|
||||
"agent-framework",
|
||||
"openai>=1.93.0",
|
||||
]
|
||||
|
||||
|
||||
[tool.ruff]
|
||||
extend = "../../pyproject.toml"
|
||||
include = ["agent-framework/openai/**", "tests/*.py"]
|
||||
|
||||
[tool.poe]
|
||||
include = "../../shared_tasks.toml"
|
||||
|
||||
[tool.pyright]
|
||||
extends = "../../pyproject.toml"
|
||||
include = ["agent_framework", "samples"]
|
||||
|
||||
[tool.uv.sources]
|
||||
agent-framework = { workspace = true }
|
||||
|
||||
[tool.uv.build-backend]
|
||||
module-name = "openai"
|
||||
module-root = "agent_framework"
|
||||
|
||||
[build-system]
|
||||
requires = ["uv_build>=0.7.19,<0.8.0"]
|
||||
build-backend = "uv_build"
|
||||
@@ -1 +0,0 @@
|
||||
3.13
|
||||
@@ -1,24 +0,0 @@
|
||||
[project]
|
||||
name = "azure"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{ name = "eavanvalkenburg", email = "github@vanvalkenburg.eu" }
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"agent-framework",
|
||||
"openai",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
extend = "../../pyproject.toml"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.uv.sources]
|
||||
openai = { workspace = true }
|
||||
agent-framework = { workspace = true }
|
||||
@@ -1 +0,0 @@
|
||||
3.13
|
||||
@@ -1,22 +0,0 @@
|
||||
[project]
|
||||
name = "openai"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{ name = "eavanvalkenburg", email = "github@vanvalkenburg.eu" }
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"agent-framework",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
extend = "../../pyproject.toml"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.uv.sources]
|
||||
agent-framework = { workspace = true }
|
||||
+86
-7
@@ -23,8 +23,8 @@ classifiers = [
|
||||
"Typing :: Typed",
|
||||
]
|
||||
dependencies = [
|
||||
"azure",
|
||||
"openai",
|
||||
"agent-framework-azure",
|
||||
"agent-framework-openai",
|
||||
"pydantic>=2.11.7",
|
||||
"typing-extensions>=4.14.0",
|
||||
]
|
||||
@@ -34,13 +34,36 @@ prerelease = "if-necessary-or-explicit"
|
||||
dev-dependencies = [
|
||||
"pre-commit >= 3.7",
|
||||
"ruff>=0.11.8",
|
||||
"poethepoet>=0.36.0",
|
||||
"pytest>=8.4.1",
|
||||
"pytest-asyncio>=1.0.0",
|
||||
"pytest-cov>=6.2.1",
|
||||
"pytest-xdist[psutil]>=3.8.0",
|
||||
"pytest-timeout>=2.3.1",
|
||||
"mypy>=1.16.1",
|
||||
"pyright>=1.1.402",
|
||||
|
||||
#tasks
|
||||
"poethepoet>=0.36.0",
|
||||
"rich",
|
||||
"tomli",
|
||||
"tomli-w",
|
||||
"markdownify",
|
||||
# Documentation
|
||||
"myst-nb==1.1.2",
|
||||
"pydata-sphinx-theme==0.16.0",
|
||||
"sphinx-copybutton",
|
||||
"sphinx-design",
|
||||
"sphinx",
|
||||
"sphinxcontrib-apidoc",
|
||||
"autodoc_pydantic~=2.2",
|
||||
"pygments",
|
||||
"sphinxext-rediraffe",
|
||||
"opentelemetry-instrumentation-openai",
|
||||
"markdown-it-py[linkify]",
|
||||
# Documentation tooling
|
||||
"diskcache",
|
||||
"redis",
|
||||
"sphinx-autobuild",
|
||||
]
|
||||
environments = [
|
||||
"sys_platform == 'darwin'",
|
||||
@@ -60,8 +83,8 @@ timeout = 120
|
||||
members = ["extensions/*"]
|
||||
|
||||
[tool.uv.sources]
|
||||
openai = { workspace = true }
|
||||
azure = { workspace = true }
|
||||
agent-framework-openai = { workspace = true }
|
||||
agent-framework-azure = { workspace = true }
|
||||
|
||||
[tool.uv-dynamic-versioning]
|
||||
fallback-version = "0.0.0"
|
||||
@@ -71,6 +94,7 @@ line-length = 120
|
||||
target-version = "py310"
|
||||
fix = true
|
||||
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
|
||||
exclude = ["docs/*", "run_tasks_in_extensions_if_exists.py", "check_md_code_blocks.py"]
|
||||
preview = true
|
||||
|
||||
[tool.ruff.lint]
|
||||
@@ -124,7 +148,7 @@ notice-rgx = "^# Copyright \\(c\\) Microsoft\\. All rights reserved\\."
|
||||
min-file-size = 1
|
||||
|
||||
[tool.pyright]
|
||||
include = ["agent_framework", "tests", "samples"]
|
||||
include = ["agent_framework", "samples"]
|
||||
typeCheckingMode = "strict"
|
||||
reportUnnecessaryIsInstance = false
|
||||
reportMissingTypeStubs = false
|
||||
@@ -146,7 +170,62 @@ disallow_any_unimported = true
|
||||
|
||||
[tool.bandit]
|
||||
targets = ["agent_framework"]
|
||||
exclude_dirs = ["tests"]
|
||||
exclude_dirs = ["tests", "./run_tasks_in_extensions_if_exists.py", "./check_md_code_blocks.py", "docs"]
|
||||
|
||||
[tool.poe.tasks]
|
||||
markdown-code-lint = """python check_md_code_blocks.py ../README.md ./docs/agent-framework/**/*.md README.md"""
|
||||
samples-code-check = """pyright ./samples"""
|
||||
docs-clean = "rm -rf docs/build"
|
||||
docs-build = "sphinx-build docs/agent-framework docs/build"
|
||||
docs-serve = "sphinx-autobuild --watch src 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"
|
||||
pre-commit-install = "uv run pre-commit install --install-hooks --overwrite"
|
||||
install = "uv sync --all-extras --dev -U --prerelease=if-necessary-or-explicit"
|
||||
format.ref = "fmt"
|
||||
check = ["fmt", "lint", "pyright", "mypy", "test", "markdown-code-lint", "samples-code-check"]
|
||||
pre-commit-check = ["fmt", "lint", "pyright", "test", "markdown-code-lint", "samples-code-check"]
|
||||
|
||||
[tool.poe.tasks.fmt]
|
||||
sequence = [
|
||||
{ cmd = "python run_tasks_in_extensions_if_exists.py fmt"},
|
||||
{ cmd = "ruff format agent_framework tests samples" }
|
||||
]
|
||||
[tool.poe.tasks.lint]
|
||||
sequence = [
|
||||
{ cmd = "python run_tasks_in_extensions_if_exists.py lint" },
|
||||
{ cmd = "ruff check --fix --exit-non-zero-on-fix agent_framework tests samples" }
|
||||
]
|
||||
[tool.poe.tasks.pyright]
|
||||
sequence = [
|
||||
{ cmd = "python run_tasks_in_extensions_if_exists.py pyright" },
|
||||
{ cmd = "pyright" }
|
||||
]
|
||||
[tool.poe.tasks.mypy]
|
||||
sequence = [
|
||||
{ cmd = "python run_tasks_in_extensions_if_exists.py mypy" },
|
||||
{ cmd = "mypy --config-file pyproject.toml agent_framework" }
|
||||
]
|
||||
[tool.poe.tasks.test]
|
||||
sequence = [
|
||||
{ cmd = "python run_tasks_in_extensions_if_exists.py test" },
|
||||
{ cmd = "pytest --cov=agent_framework --cov-report=term-missing:skip-covered tests/unit/" }
|
||||
]
|
||||
[tool.poe.tasks.build]
|
||||
sequence = [
|
||||
{ cmd = "python run_tasks_in_extensions_if_exists.py build" },
|
||||
{ cmd = "uv build" }
|
||||
]
|
||||
[tool.poe.tasks.venv]
|
||||
cmd = "uv venv --python $python"
|
||||
args = [{ name = "python", default = "3.13", options = ['-p', '--python'] }]
|
||||
[tool.poe.tasks.setup]
|
||||
sequence = [
|
||||
{ ref = "venv --python $python"},
|
||||
{ ref = "install" },
|
||||
{ ref = "pre-commit-install" }
|
||||
]
|
||||
args = [{ name = "python", default = "3.13", options = ['-p', '--python'] }]
|
||||
|
||||
[tool.uv.build-backend]
|
||||
module-name = "agent_framework"
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import glob
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import tomli
|
||||
from poethepoet.app import PoeThePoet
|
||||
from rich import print
|
||||
|
||||
|
||||
def discover_projects(workspace_pyproject_file: Path) -> list[Path]:
|
||||
with workspace_pyproject_file.open("rb") as f:
|
||||
data = tomli.load(f)
|
||||
|
||||
projects = data["tool"]["uv"]["workspace"]["members"]
|
||||
exclude = data["tool"]["uv"]["workspace"].get("exclude", [])
|
||||
|
||||
all_projects: list[Path] = []
|
||||
for project in projects:
|
||||
if "*" in project:
|
||||
globbed = glob.glob(str(project), root_dir=workspace_pyproject_file.parent)
|
||||
globbed_paths = [Path(p) for p in globbed]
|
||||
all_projects.extend(globbed_paths)
|
||||
else:
|
||||
all_projects.append(Path(project))
|
||||
|
||||
for project in exclude:
|
||||
if "*" in project:
|
||||
globbed = glob.glob(str(project), root_dir=workspace_pyproject_file.parent)
|
||||
globbed_paths = [Path(p) for p in globbed]
|
||||
all_projects = [p for p in all_projects if p not in globbed_paths]
|
||||
else:
|
||||
all_projects = [p for p in all_projects if p != Path(project)]
|
||||
|
||||
return all_projects
|
||||
|
||||
|
||||
def extract_poe_tasks(file: Path) -> set[str]:
|
||||
with file.open("rb") as f:
|
||||
data = tomli.load(f)
|
||||
|
||||
tasks = set(data.get("tool", {}).get("poe", {}).get("tasks", {}).keys())
|
||||
|
||||
# Check if there is an include too
|
||||
include: str | None = data.get("tool", {}).get("poe", {}).get("include", None)
|
||||
if include:
|
||||
include_file = file.parent / include
|
||||
if include_file.exists():
|
||||
tasks = tasks.union(extract_poe_tasks(include_file))
|
||||
|
||||
return tasks
|
||||
|
||||
|
||||
def main() -> None:
|
||||
pyproject_file = Path(__file__).parent / "pyproject.toml"
|
||||
projects = discover_projects(pyproject_file)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Please provide a task name")
|
||||
sys.exit(1)
|
||||
|
||||
task_name = sys.argv[1]
|
||||
for project in projects:
|
||||
tasks = extract_poe_tasks(project / "pyproject.toml")
|
||||
if task_name in tasks:
|
||||
print(f"Running task {task_name} in {project}")
|
||||
app = PoeThePoet(cwd=project)
|
||||
result = app(cli_args=sys.argv[1:])
|
||||
if result:
|
||||
sys.exit(result)
|
||||
else:
|
||||
print(f"Task {task_name} not found in {project}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,84 @@
|
||||
# Sample Guidelines
|
||||
|
||||
Samples are extremely important for developers to get started with Agent Framework. We strive to provide a wide range of samples that demonstrate the capabilities of Agent Framework with consistency and quality. This document outlines the guidelines for creating samples.
|
||||
|
||||
## General Guidelines
|
||||
|
||||
- **Clear and Concise**: Samples should be clear and concise. They should demonstrate a specific set of features or capabilities of Agent Framework. The less concepts a sample demonstrates, the better.
|
||||
- **Consistent Structure**: All samples should have a consistent structure. This includes the folder structure, file naming, and the content of the sample.
|
||||
- **Incremental Complexity**: Samples should start simple and gradually increase in complexity. This helps developers understand the concepts and features of Agent Framework.
|
||||
- **Documentation**: Samples should be over-documented.
|
||||
|
||||
### **Clear and Concise**
|
||||
|
||||
Try not to include too many concepts in a single sample. The goal is to demonstrate a specific feature or capability of Agent Framework. If you find yourself including too many concepts, consider breaking the sample into multiple samples. A good example of this is to break non-streaming and streaming modes into separate samples.
|
||||
|
||||
### **Consistent Structure**
|
||||
|
||||
! TODO: Update folder structure to our new needs.
|
||||
! TODO: Decide on single samples folder or also samples in extensions
|
||||
|
||||
#### Getting Started Samples
|
||||
|
||||
The getting started samples are the simplest samples that require minimal setup. These samples should be named in the following format: `step<number>_<name>.py`. One exception to this rule is when the sample is a notebook, in which case the sample should be named in the following format: `<number>_<name>.ipynb`.
|
||||
|
||||
#### Concept Samples
|
||||
|
||||
Concept samples under [./concepts](./concepts) should be grouped by feature or capability. These samples should be relatively short and demonstrate a specific concept. These samples are more advanced than the getting started samples.
|
||||
|
||||
#### Demos
|
||||
|
||||
Demos under [./demos](./demos) are full console applications that demonstrate a specific set of features or capabilities of Agent Framework, potentially with external dependencies. Each of the demos should have a README.md file that explains the purpose of the demo and how to run it.
|
||||
|
||||
### **Incremental Complexity**
|
||||
|
||||
Try to do a best effort to make sure that the samples are incremental in complexity. For example, in the getting started samples, each step should build on the previous step, and the concept samples should build on the getting started samples, same with the demos.
|
||||
|
||||
### **Documentation**
|
||||
|
||||
Try to over-document the samples. This includes comments in the code, README.md files, and any other documentation that is necessary to understand the sample. We use the guidance from [PEP8](https://peps.python.org/pep-0008/#comments) for comments in the code, with a deviation for the initial summary comment in samples and the output of the samples.
|
||||
|
||||
For the getting started samples and the concept samples, we should have the following:
|
||||
|
||||
1. A README.md file is included in each set of samples that explains the purpose of the samples and the setup required to run them.
|
||||
2. A summary should be included at the top of the file that explains the purpose of the sample and required components/concepts to understand the sample. For example:
|
||||
|
||||
```python
|
||||
'''
|
||||
This sample shows how to create a chatbot. This sample uses the following two main components:
|
||||
- a ChatCompletionService: This component is responsible for generating responses to user messages.
|
||||
- a ChatHistory: This component is responsible for keeping track of the chat history.
|
||||
The chatbot in this sample is called Mosscap, who responds to user messages with long flowery prose.
|
||||
'''
|
||||
```
|
||||
|
||||
3. Mark the code with comments to explain the purpose of each section of the code. For example:
|
||||
|
||||
```python
|
||||
# 1. Create the instance of the Kernel to register the plugin and service.
|
||||
...
|
||||
|
||||
# 2. Create the agent with the kernel instance.
|
||||
...
|
||||
```
|
||||
|
||||
> This will also allow the sample creator to track if the sample is getting too complex.
|
||||
|
||||
4. At the end of the sample, include a section that explains the expected output of the sample. For example:
|
||||
|
||||
```python
|
||||
'''
|
||||
Sample output:
|
||||
User:> Why is the sky blue in one sentence?
|
||||
Mosscap:> The sky is blue due to the scattering of sunlight by the molecules in the Earth's atmosphere,
|
||||
a phenomenon known as Rayleigh scattering, which causes shorter blue wavelengths to become more
|
||||
prominent in our visual perception.
|
||||
'''
|
||||
```
|
||||
|
||||
For the demos, a README.md file must be included that explains the purpose of the demo and how to run it. The README.md file should include the following:
|
||||
|
||||
- A description of the demo.
|
||||
- A list of dependencies required to run the demo.
|
||||
- Instructions on how to run the demo.
|
||||
- Expected output of the demo.
|
||||
@@ -0,0 +1,7 @@
|
||||
[tool.poe.tasks]
|
||||
fmt = "ruff format"
|
||||
format.ref = "fmt"
|
||||
lint = "ruff check"
|
||||
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework tests"
|
||||
pyright = "pyright"
|
||||
build = "uv build"
|
||||
Generated
+2258
-43
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user