Skip to content

Commit ccd7a44

Browse files
authored
Python: Implemented FoundryChatClient (#193)
* Initial version of FoundryChatClient * Updates to the tool call streaming wrapper * Small fixes * Small updates and addressed PR feedback * Handle automatic client creation * Small improvement * Added credential parameter * Small improvements * Made FoundryChatClient disposable * Small fixes * Added unit tests * Refactored samples * Small improvements * Small fix * Addressed PR feedback * Small fixes * Small updates * Small fix * Addressed PR feedback
1 parent 9287572 commit ccd7a44

26 files changed

Lines changed: 2050 additions & 22 deletions

python/packages/foundry/README.md

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
import importlib.metadata
4+
5+
from ._chat_client import FoundryChatClient, FoundrySettings
6+
7+
try:
8+
__version__ = importlib.metadata.version(__name__)
9+
except importlib.metadata.PackageNotFoundError:
10+
__version__ = "0.0.0" # Fallback for development mode
11+
12+
__all__ = [
13+
"FoundryChatClient",
14+
"FoundrySettings",
15+
"__version__",
16+
]

python/packages/foundry/agent_framework_foundry/_chat_client.py

Lines changed: 569 additions & 0 deletions
Large diffs are not rendered by default.

python/packages/foundry/agent_framework_foundry/py.typed

Whitespace-only changes.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
[project]
2+
name = "agent-framework-foundry"
3+
description = "Azure AI Foundry integration for Microsoft Agent Framework."
4+
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
5+
readme = "README.md"
6+
requires-python = ">=3.10"
7+
version = "0.1.0b1"
8+
license = {file = "../../LICENSE"}
9+
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
10+
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
11+
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
12+
urls.issues = "https://github.com/microsoft/agent-framework/issues"
13+
classifiers = [
14+
"License :: OSI Approved :: MIT License",
15+
"Development Status :: 5 - Production/Stable",
16+
"Intended Audience :: Developers",
17+
"Programming Language :: Python :: 3",
18+
"Programming Language :: Python :: 3.10",
19+
"Programming Language :: Python :: 3.11",
20+
"Programming Language :: Python :: 3.12",
21+
"Programming Language :: Python :: 3.13",
22+
"Framework :: Pydantic :: 2",
23+
"Typing :: Typed",
24+
]
25+
dependencies = [
26+
"agent-framework",
27+
"azure-ai-projects >= 1.0.0b11",
28+
"azure-ai-agents >= 1.1.0b1",
29+
"aiohttp ~= 3.8"
30+
]
31+
32+
[dependency-groups]
33+
dev = [
34+
"pre-commit >= 3.7",
35+
"ruff>=0.11.8",
36+
"pytest>=8.4.1",
37+
"pytest-asyncio>=1.0.0",
38+
"pytest-cov>=6.2.1",
39+
"pytest-xdist[psutil]>=3.8.0",
40+
"pytest-timeout>=2.3.1",
41+
"mypy>=1.16.1",
42+
"pyright>=1.1.402",
43+
44+
#tasks
45+
"poethepoet>=0.36.0",
46+
"rich",
47+
"tomli",
48+
"tomli-w",
49+
"markdownify",
50+
# Documentation
51+
"myst-nb==1.1.2",
52+
"pydata-sphinx-theme==0.16.0",
53+
"sphinx-copybutton",
54+
"sphinx-design",
55+
"sphinx",
56+
"sphinxcontrib-apidoc",
57+
"autodoc_pydantic~=2.2",
58+
"pygments",
59+
"sphinxext-rediraffe",
60+
"opentelemetry-instrumentation-openai",
61+
"markdown-it-py[linkify]",
62+
# Documentation tooling
63+
"diskcache",
64+
"redis",
65+
"sphinx-autobuild",
66+
]
67+
68+
[tool.uv]
69+
prerelease = "if-necessary-or-explicit"
70+
environments = [
71+
"sys_platform == 'darwin'",
72+
"sys_platform == 'linux'",
73+
"sys_platform == 'win32'"
74+
]
75+
76+
[tool.uv-dynamic-versioning]
77+
fallback-version = "0.0.0"
78+
[tool.pytest.ini_options]
79+
testpaths = 'tests'
80+
addopts = "-ra -q -r fEX"
81+
asyncio_mode = "auto"
82+
asyncio_default_fixture_loop_scope = "function"
83+
filterwarnings = []
84+
timeout = 120
85+
86+
[tool.ruff]
87+
extend = "../../pyproject.toml"
88+
89+
[tool.pyright]
90+
extend = "../../pyproject.toml"
91+
exclude = ['tests']
92+
93+
[tool.mypy]
94+
plugins = ['pydantic.mypy']
95+
strict = true
96+
python_version = "3.10"
97+
ignore_missing_imports = true
98+
disallow_untyped_defs = true
99+
no_implicit_optional = true
100+
check_untyped_defs = true
101+
warn_return_any = true
102+
show_error_codes = true
103+
warn_unused_ignores = false
104+
disallow_incomplete_defs = true
105+
disallow_untyped_decorators = true
106+
disallow_any_unimported = true
107+
108+
[tool.bandit]
109+
targets = ["agent_framework_foundry"]
110+
exclude_dirs = ["tests"]
111+
112+
[tool.poe]
113+
executor.type = "uv"
114+
include = "../../shared_tasks.toml"
115+
116+
[tool.uv.build-backend]
117+
module-name = "agent_framework_foundry"
118+
module-root = ""
119+
120+
[build-system]
121+
requires = ["uv_build>=0.7.19,<0.8.0"]
122+
build-backend = "uv_build"

python/packages/foundry/tests/__init.__.py

Whitespace-only changes.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
from typing import Any
3+
from unittest.mock import AsyncMock, MagicMock
4+
5+
from pytest import fixture
6+
7+
8+
@fixture
9+
def exclude_list(request: Any) -> list[str]:
10+
"""Fixture that returns a list of environment variables to exclude."""
11+
return request.param if hasattr(request, "param") else []
12+
13+
14+
@fixture
15+
def override_env_param_dict(request: Any) -> dict[str, str]:
16+
"""Fixture that returns a dict of environment variables to override."""
17+
return request.param if hasattr(request, "param") else {}
18+
19+
20+
@fixture()
21+
def foundry_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): # type: ignore
22+
"""Fixture to set environment variables for FoundrySettings."""
23+
if exclude_list is None:
24+
exclude_list = []
25+
26+
if override_env_param_dict is None:
27+
override_env_param_dict = {}
28+
29+
env_vars = {
30+
"FOUNDRY_PROJECT_ENDPOINT": "https://test-project.cognitiveservices.azure.com/",
31+
"FOUNDRY_MODEL_DEPLOYMENT_NAME": "test-gpt-4o",
32+
"FOUNDRY_AGENT_NAME": "TestAgent",
33+
}
34+
35+
env_vars.update(override_env_param_dict) # type: ignore
36+
37+
for key, value in env_vars.items():
38+
if key not in exclude_list:
39+
monkeypatch.setenv(key, value) # type: ignore
40+
else:
41+
monkeypatch.delenv(key, raising=False) # type: ignore
42+
43+
return env_vars
44+
45+
46+
@fixture
47+
def mock_ai_project_client() -> MagicMock:
48+
"""Fixture that provides a mock AIProjectClient."""
49+
mock_client = MagicMock()
50+
51+
# Mock agents property
52+
mock_client.agents = MagicMock()
53+
mock_client.agents.create_agent = AsyncMock()
54+
mock_client.agents.delete_agent = AsyncMock()
55+
56+
# Mock agent creation response
57+
mock_agent = MagicMock()
58+
mock_agent.id = "test-agent-id"
59+
mock_client.agents.create_agent.return_value = mock_agent
60+
61+
# Mock threads property
62+
mock_client.agents.threads = MagicMock()
63+
mock_client.agents.threads.create = AsyncMock()
64+
65+
# Mock runs property
66+
mock_client.agents.runs = MagicMock()
67+
mock_client.agents.runs.list = AsyncMock()
68+
mock_client.agents.runs.cancel = AsyncMock()
69+
mock_client.agents.runs.stream = AsyncMock()
70+
mock_client.agents.runs.submit_tool_outputs_stream = AsyncMock()
71+
72+
return mock_client
73+
74+
75+
@fixture
76+
def mock_azure_credential() -> MagicMock:
77+
"""Fixture that provides a mock AsyncTokenCredential."""
78+
return MagicMock()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
4+
def test_self_through_main():
5+
try:
6+
from agent_framework.foundry import __version__
7+
except ImportError:
8+
__version__ = None
9+
10+
assert __version__ is not None
11+
12+
13+
def test_self():
14+
try:
15+
from agent_framework_foundry import __version__
16+
except ImportError:
17+
__version__ = None
18+
19+
assert __version__ is not None
20+
21+
22+
def test_agent_framework():
23+
try:
24+
from agent_framework import __version__
25+
except ImportError:
26+
__version__ = None
27+
28+
assert __version__ is not None

0 commit comments

Comments
 (0)