diff --git a/python/packages/foundry_local/LICENSE b/python/packages/foundry_local/LICENSE new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/python/packages/foundry_local/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/python/packages/foundry_local/README.md b/python/packages/foundry_local/README.md new file mode 100644 index 0000000000..c65e5a0386 --- /dev/null +++ b/python/packages/foundry_local/README.md @@ -0,0 +1,9 @@ +# Get Started with Microsoft Agent Framework Foundry Local + +Please install this package as the extra for `agent-framework`: + +```bash +pip install agent-framework-foundry-local --pre +``` + +and see the [README](https://github.com/microsoft/agent-framework/tree/main/python/README.md) for more information. diff --git a/python/packages/foundry_local/agent_framework_foundry_local/__init__.py b/python/packages/foundry_local/agent_framework_foundry_local/__init__.py new file mode 100644 index 0000000000..dbea932348 --- /dev/null +++ b/python/packages/foundry_local/agent_framework_foundry_local/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) Microsoft. All rights reserved. + +import importlib.metadata + +from ._foundry_local_client import FoundryLocalClient + +try: + __version__ = importlib.metadata.version(__name__) +except importlib.metadata.PackageNotFoundError: + __version__ = "0.0.0" # Fallback for development mode + +__all__ = [ + "FoundryLocalClient", + "__version__", +] diff --git a/python/packages/foundry_local/agent_framework_foundry_local/_foundry_local_client.py b/python/packages/foundry_local/agent_framework_foundry_local/_foundry_local_client.py new file mode 100644 index 0000000000..c2b7bd34ab --- /dev/null +++ b/python/packages/foundry_local/agent_framework_foundry_local/_foundry_local_client.py @@ -0,0 +1,160 @@ +# Copyright (c) Microsoft. All rights reserved. + +from typing import Any, ClassVar + +from agent_framework import use_chat_middleware, use_function_invocation +from agent_framework._pydantic import AFBaseSettings +from agent_framework.exceptions import ServiceInitializationError +from agent_framework.observability import use_instrumentation +from agent_framework.openai._chat_client import OpenAIBaseChatClient +from foundry_local import FoundryLocalManager +from foundry_local.models import DeviceType +from openai import AsyncOpenAI + +__all__ = [ + "FoundryLocalClient", +] + + +class FoundryLocalSettings(AFBaseSettings): + """Foundry local model settings. + + The settings are first loaded from environment variables with the prefix 'FOUNDRY_LOCAL_'. + If the environment variables are not found, the settings can be loaded from a .env file + with the encoding 'utf-8'. If the settings are not found in the .env file, the settings + are ignored; however, validation will fail alerting that the settings are missing. + + Attributes: + model_id: The name of the model deployment to use. + (Env var FOUNDRY_LOCAL_MODEL_ID) + Parameters: + env_file_path: If provided, the .env settings are read from this file path location. + env_file_encoding: The encoding of the .env file, defaults to 'utf-8'. + """ + + env_prefix: ClassVar[str] = "FOUNDRY_LOCAL_" + + model_id: str + + +@use_function_invocation +@use_instrumentation +@use_chat_middleware +class FoundryLocalClient(OpenAIBaseChatClient): + """Foundry Local Chat completion class.""" + + def __init__( + self, + model_id: str | None = None, + *, + bootstrap: bool = True, + timeout: float | None = None, + prepare_model: bool = True, + device: DeviceType | None = None, + env_file_path: str | None = None, + env_file_encoding: str = "utf-8", + **kwargs: Any, + ) -> None: + """Initialize a FoundryLocalClient. + + Keyword Args: + model_id: The Foundry Local model ID or alias to use. If not provided, + it will be loaded from the FoundryLocalSettings. + bootstrap: Whether to start the Foundry Local service if not already running. + Default is True. + timeout: Optional timeout for requests to Foundry Local. + This timeout is applied to any call to the Foundry Local service. + prepare_model: Whether to download the model into the cache, and load the model into + the inferencing service upon initialization. Default is True. + If false, the first call to generate a completion will load the model, + and might take a long time. + device: The device type to use for model inference. + The device is used to select the appropriate model variant. + If not provided, the default device for your system will be used. + The values are in the foundry_local.models.DeviceType enum. + env_file_path: If provided, the .env settings are read from this file path location. + env_file_encoding: The encoding of the .env file, defaults to 'utf-8'. + kwargs: Additional keyword arguments, are passed to the OpenAIBaseChatClient. + This can include middleware and additional properties. + + Examples: + + .. code-block:: python + + # Create a FoundryLocalClient with a specific model ID: + from agent_framework_foundry_local import FoundryLocalClient + + client = FoundryLocalClient(model_id="phi-4-mini") + + agent = client.create_agent( + name="LocalAgent", + instructions="You are a helpful agent.", + tools=get_weather, + ) + response = await agent.run("What's the weather like in Seattle?") + + # Or you can set the model id in the environment: + os.environ["FOUNDRY_LOCAL_MODEL_ID"] = "phi-4-mini" + client = FoundryLocalClient() + + # A FoundryLocalManager is created and if set, the service is started. + # The FoundryLocalManager is available via the `manager` property. + # For instance to find out which models are available: + for model in client.manager.list_catalog_models(): + print(f"- {model.alias} for {model.task} - id={model.id}") + + # Other options include specifying the device type: + from foundry_local.models import DeviceType + + client = FoundryLocalClient( + model_id="phi-4-mini", + device=DeviceType.GPU, + ) + # and choosing if the model should be prepared on initialization: + client = FoundryLocalClient( + model_id="phi-4-mini", + prepare_model=False, + ) + # Beware, in this case the first request to generate a completion + # will take a long time as the model is loaded then. + # Alternatively, you could call the `download_model` and `load_model` methods + # on the `manager` property manually. + client.manager.download_model(alias_or_model_id="phi-4-mini", device=DeviceType.CPU) + client.manager.load_model(alias_or_model_id="phi-4-mini", device=DeviceType.CPU) + + # You can also use the CLI: + `foundry model load phi-4-mini --device Auto` + + Raises: + ServiceInitializationError: If the specified model ID or alias is not found. + Sometimes a model might be available but if you have specified a device + type that is not supported by the model, it will not be found. + + """ + settings = FoundryLocalSettings( + model_id=model_id, # type: ignore + env_file_path=env_file_path, + env_file_encoding=env_file_encoding, + ) + manager = FoundryLocalManager(bootstrap=bootstrap, timeout=timeout) + model_info = manager.get_model_info( + alias_or_model_id=settings.model_id, + device=device, + ) + if model_info is None: + message = ( + f"Model with ID or alias '{settings.model_id}:{device.value}' not found in Foundry Local." + if device + else f"Model with ID or alias '{settings.model_id}' for your current device not found in Foundry Local." + ) + raise ServiceInitializationError(message) + if prepare_model: + manager.download_model(alias_or_model_id=model_info.id, device=device) + manager.load_model(alias_or_model_id=model_info.id, device=device) + + super().__init__( + model_id=model_info.id, + client=AsyncOpenAI(base_url=manager.endpoint, api_key=manager.api_key), + **kwargs, + ) + self.manager = manager diff --git a/python/packages/foundry_local/agent_framework_foundry_local/py.typed b/python/packages/foundry_local/agent_framework_foundry_local/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/packages/foundry_local/pyproject.toml b/python/packages/foundry_local/pyproject.toml new file mode 100644 index 0000000000..bca5cfc8c1 --- /dev/null +++ b/python/packages/foundry_local/pyproject.toml @@ -0,0 +1,87 @@ +[project] +name = "agent-framework-foundry-local" +description = "Foundry Local integration for Microsoft Agent Framework." +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] +readme = "README.md" +requires-python = ">=3.10" +version = "1.0.0b251218" +license-files = ["LICENSE"] +urls.homepage = "https://aka.ms/agent-framework" +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 :: 4 - Beta", + "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", + "Programming Language :: Python :: 3.14", + "Typing :: Typed", +] +dependencies = [ + "agent-framework-core", + "foundry-local-sdk>=0.5.1,<1", +] + +[tool.uv] +prerelease = "if-necessary-or-explicit" +environments = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform == 'win32'" +] + +[tool.uv-dynamic-versioning] +fallback-version = "0.0.0" +[tool.pytest.ini_options] +testpaths = 'tests' +addopts = "-ra -q -r fEX" +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" +filterwarnings = [] +timeout = 120 + +[tool.ruff] +extend = "../../pyproject.toml" + +[tool.coverage.run] +omit = [ + "**/__init__.py" +] + +[tool.pyright] +extends = "../../pyproject.toml" +exclude = ['tests'] + +[tool.mypy] +plugins = ['pydantic.mypy'] +strict = true +python_version = "3.10" +ignore_missing_imports = true +disallow_untyped_defs = true +no_implicit_optional = true +check_untyped_defs = true +warn_return_any = true +show_error_codes = true +warn_unused_ignores = false +disallow_incomplete_defs = true +disallow_untyped_decorators = true + +[tool.bandit] +targets = ["agent_framework_foundry_local"] +exclude_dirs = ["tests"] + +[tool.poe] +executor.type = "uv" +include = "../../shared_tasks.toml" +[tool.poe.tasks] +mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_foundry_local" +test = "pytest --cov=agent_framework_foundry_local --cov-report=term-missing:skip-covered tests" + +[build-system] +requires = ["flit-core >= 3.11,<4.0"] +build-backend = "flit_core.buildapi" diff --git a/python/packages/foundry_local/samples/foundry_local_agent.py b/python/packages/foundry_local/samples/foundry_local_agent.py new file mode 100644 index 0000000000..e74f3de073 --- /dev/null +++ b/python/packages/foundry_local/samples/foundry_local_agent.py @@ -0,0 +1,78 @@ +# Copyright (c) Microsoft. All rights reserved. +# ruff: noqa + +import asyncio +from random import randint +from typing import TYPE_CHECKING, Annotated + +from agent_framework_foundry_local import FoundryLocalClient + +if TYPE_CHECKING: + from agent_framework import ChatAgent + +""" +This sample demonstrates basic usage of the FoundryLocalClient. +Shows both streaming and non-streaming responses with function tools. + +Running this sample the first time will be slow, as the model needs to be +downloaded and initialized. + +Also, not every model supports function calling, so be sure to check the +model capabilities in the Foundry catalog, or pick one from the list printed +when running this sample. +""" + + +def get_weather( + location: Annotated[str, "The location to get the weather for."], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def non_streaming_example(agent: "ChatAgent") -> None: + """Example of non-streaming response (get the complete result at once).""" + print("=== Non-streaming Response Example ===") + + query = "What's the weather like in Seattle?" + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}\n") + + +async def streaming_example(agent: "ChatAgent") -> None: + """Example of streaming response (get results as they are generated).""" + print("=== Streaming Response Example ===") + + query = "What's the weather like in Amsterdam?" + print(f"User: {query}") + print("Agent: ", end="", flush=True) + async for chunk in agent.run_stream(query): + if chunk.text: + print(chunk.text, end="", flush=True) + print("\n") + + +async def main() -> None: + print("=== Basic Foundry Local Client Agent Example ===") + + client = FoundryLocalClient(model_id="phi-4-mini") + print(f"Client Model ID: {client.model_id}\n") + print("Other available models (tool calling supported only):") + for model in client.manager.list_catalog_models(): + if model.supports_tool_calling: + print( + f"- {model.alias} for {model.task} - id={model.id} - {(model.file_size_mb / 1000):.2f} GB - {model.license}" + ) + agent = client.create_agent( + name="LocalAgent", + instructions="You are a helpful agent.", + tools=get_weather, + ) + await non_streaming_example(agent) + await streaming_example(agent) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/packages/foundry_local/tests/conftest.py b/python/packages/foundry_local/tests/conftest.py new file mode 100644 index 0000000000..0afc223356 --- /dev/null +++ b/python/packages/foundry_local/tests/conftest.py @@ -0,0 +1,55 @@ +# Copyright (c) Microsoft. All rights reserved. +from typing import Any +from unittest.mock import MagicMock + +from pytest import fixture + + +@fixture +def exclude_list(request: Any) -> list[str]: + """Fixture that returns a list of environment variables to exclude.""" + return request.param if hasattr(request, "param") else [] + + +@fixture +def override_env_param_dict(request: Any) -> dict[str, str]: + """Fixture that returns a dict of environment variables to override.""" + return request.param if hasattr(request, "param") else {} + + +@fixture() +def foundry_local_unit_test_env(monkeypatch: Any, exclude_list: list[str], override_env_param_dict: dict[str, str]): + """Fixture to set environment variables for FoundryLocalSettings.""" + if exclude_list is None: + exclude_list = [] + + if override_env_param_dict is None: + override_env_param_dict = {} + + env_vars = { + "FOUNDRY_LOCAL_MODEL_ID": "test-model-id", + } + + env_vars.update(override_env_param_dict) + + for key, value in env_vars.items(): + if key in exclude_list: + monkeypatch.delenv(key, raising=False) + continue + monkeypatch.setenv(key, value) + + return env_vars + + +@fixture +def mock_foundry_local_manager() -> MagicMock: + """Fixture that provides a mock FoundryLocalManager.""" + mock_manager = MagicMock() + mock_manager.endpoint = "http://localhost:5272/v1" + mock_manager.api_key = "test-api-key" + + mock_model_info = MagicMock() + mock_model_info.id = "test-model-id" + mock_manager.get_model_info.return_value = mock_model_info + + return mock_manager diff --git a/python/packages/foundry_local/tests/test_foundry_local_client.py b/python/packages/foundry_local/tests/test_foundry_local_client.py new file mode 100644 index 0000000000..324c94630e --- /dev/null +++ b/python/packages/foundry_local/tests/test_foundry_local_client.py @@ -0,0 +1,198 @@ +# Copyright (c) Microsoft. All rights reserved. + +from unittest.mock import MagicMock, patch + +import pytest +from agent_framework import ChatClientProtocol +from agent_framework.exceptions import ServiceInitializationError +from pydantic import ValidationError + +from agent_framework_foundry_local import FoundryLocalClient +from agent_framework_foundry_local._foundry_local_client import FoundryLocalSettings + +# Settings Tests + + +def test_foundry_local_settings_init_from_env(foundry_local_unit_test_env: dict[str, str]) -> None: + """Test FoundryLocalSettings initialization from environment variables.""" + settings = FoundryLocalSettings(env_file_path="test.env") + + assert settings.model_id == foundry_local_unit_test_env["FOUNDRY_LOCAL_MODEL_ID"] + + +def test_foundry_local_settings_init_with_explicit_values() -> None: + """Test FoundryLocalSettings initialization with explicit values.""" + settings = FoundryLocalSettings(model_id="custom-model-id", env_file_path="test.env") + + assert settings.model_id == "custom-model-id" + + +@pytest.mark.parametrize("exclude_list", [["FOUNDRY_LOCAL_MODEL_ID"]], indirect=True) +def test_foundry_local_settings_missing_model_id(foundry_local_unit_test_env: dict[str, str]) -> None: + """Test FoundryLocalSettings when model_id is missing raises ValidationError.""" + with pytest.raises(ValidationError): + FoundryLocalSettings(env_file_path="test.env") + + +def test_foundry_local_settings_explicit_overrides_env(foundry_local_unit_test_env: dict[str, str]) -> None: + """Test that explicit values override environment variables.""" + settings = FoundryLocalSettings(model_id="override-model-id", env_file_path="test.env") + + assert settings.model_id == "override-model-id" + assert settings.model_id != foundry_local_unit_test_env["FOUNDRY_LOCAL_MODEL_ID"] + + +# Client Initialization Tests + + +def test_foundry_local_client_init(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient initialization with mocked manager.""" + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ): + client = FoundryLocalClient(model_id="test-model-id", env_file_path="test.env") + + assert client.model_id == "test-model-id" + assert client.manager is mock_foundry_local_manager + assert isinstance(client, ChatClientProtocol) + + +def test_foundry_local_client_init_with_bootstrap_false(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient initialization with bootstrap=False.""" + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ) as mock_manager_class: + FoundryLocalClient(model_id="test-model-id", bootstrap=False, env_file_path="test.env") + + mock_manager_class.assert_called_once_with( + bootstrap=False, + timeout=None, + ) + + +def test_foundry_local_client_init_with_timeout(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient initialization with custom timeout.""" + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ) as mock_manager_class: + FoundryLocalClient(model_id="test-model-id", timeout=60.0, env_file_path="test.env") + + mock_manager_class.assert_called_once_with( + bootstrap=True, + timeout=60.0, + ) + + +def test_foundry_local_client_init_model_not_found(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient initialization when model is not found.""" + mock_foundry_local_manager.get_model_info.return_value = None + + with ( + patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ), + pytest.raises(ServiceInitializationError, match="not found in Foundry Local"), + ): + FoundryLocalClient(model_id="unknown-model", env_file_path="test.env") + + +def test_foundry_local_client_uses_model_info_id(mock_foundry_local_manager: MagicMock) -> None: + """Test that client uses the model ID from model_info, not the alias.""" + mock_model_info = MagicMock() + mock_model_info.id = "resolved-model-id" + mock_foundry_local_manager.get_model_info.return_value = mock_model_info + + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ): + client = FoundryLocalClient(model_id="model-alias", env_file_path="test.env") + + assert client.model_id == "resolved-model-id" + + +def test_foundry_local_client_init_from_env( + foundry_local_unit_test_env: dict[str, str], mock_foundry_local_manager: MagicMock +) -> None: + """Test FoundryLocalClient initialization using environment variables.""" + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ): + client = FoundryLocalClient(env_file_path="test.env") + + assert client.model_id == foundry_local_unit_test_env["FOUNDRY_LOCAL_MODEL_ID"] + + +def test_foundry_local_client_init_with_device(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient initialization with device parameter.""" + from foundry_local.models import DeviceType + + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ): + FoundryLocalClient(model_id="test-model-id", device=DeviceType.CPU, env_file_path="test.env") + + mock_foundry_local_manager.get_model_info.assert_called_once_with( + alias_or_model_id="test-model-id", + device=DeviceType.CPU, + ) + mock_foundry_local_manager.download_model.assert_called_once_with( + alias_or_model_id="test-model-id", + device=DeviceType.CPU, + ) + mock_foundry_local_manager.load_model.assert_called_once_with( + alias_or_model_id="test-model-id", + device=DeviceType.CPU, + ) + + +def test_foundry_local_client_init_model_not_found_with_device(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient error message includes device when model not found with device specified.""" + from foundry_local.models import DeviceType + + mock_foundry_local_manager.get_model_info.return_value = None + + with ( + patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ), + pytest.raises(ServiceInitializationError, match="unknown-model:GPU.*not found"), + ): + FoundryLocalClient(model_id="unknown-model", device=DeviceType.GPU, env_file_path="test.env") + + +def test_foundry_local_client_init_with_prepare_model_false(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient initialization with prepare_model=False skips download and load.""" + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ): + FoundryLocalClient(model_id="test-model-id", prepare_model=False, env_file_path="test.env") + + mock_foundry_local_manager.download_model.assert_not_called() + mock_foundry_local_manager.load_model.assert_not_called() + + +def test_foundry_local_client_init_calls_download_and_load(mock_foundry_local_manager: MagicMock) -> None: + """Test FoundryLocalClient initialization calls download_model and load_model by default.""" + with patch( + "agent_framework_foundry_local._foundry_local_client.FoundryLocalManager", + return_value=mock_foundry_local_manager, + ): + FoundryLocalClient(model_id="test-model-id", env_file_path="test.env") + + mock_foundry_local_manager.download_model.assert_called_once_with( + alias_or_model_id="test-model-id", + device=None, + ) + mock_foundry_local_manager.load_model.assert_called_once_with( + alias_or_model_id="test-model-id", + device=None, + ) diff --git a/python/pyproject.toml b/python/pyproject.toml index ecee05161e..48dd976ad5 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -94,6 +94,7 @@ agent-framework-chatkit = { workspace = true } agent-framework-copilotstudio = { workspace = true } agent-framework-declarative = { workspace = true } agent-framework-devui = { workspace = true } +agent-framework-foundry-local = { workspace = true } agent-framework-lab = { workspace = true } agent-framework-mem0 = { workspace = true } agent-framework-ollama = { workspace = true } diff --git a/python/uv.lock b/python/uv.lock index 6a5252e49d..346d760b03 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -38,6 +38,7 @@ members = [ "agent-framework-core", "agent-framework-declarative", "agent-framework-devui", + "agent-framework-foundry-local", "agent-framework-lab", "agent-framework-mem0", "agent-framework-ollama", @@ -429,6 +430,21 @@ requires-dist = [ ] provides-extras = ["dev", "all"] +[[package]] +name = "agent-framework-foundry-local" +version = "1.0.0b251218" +source = { editable = "packages/foundry_local" } +dependencies = [ + { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "foundry-local-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] + +[package.metadata] +requires-dist = [ + { name = "agent-framework-core", editable = "packages/core" }, + { name = "foundry-local-sdk", specifier = ">=0.5.1,<1" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251218" @@ -1343,7 +1359,7 @@ name = "clr-loader" version = "0.2.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/54/c2/da52aaf19424e3f0abec003d08dd1ccae52c88a3b41e31151a03bed18488/clr_loader-0.2.9.tar.gz", hash = "sha256:6af3d582c3de55ce9e9e676d2b3dbf6bc680c4ea8f76c58786739a5bdcf6b52d", size = 84829, upload-time = "2025-12-05T16:57:12.466Z" } wheels = [ @@ -1822,7 +1838,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -2039,6 +2055,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" }, ] +[[package]] +name = "foundry-local-sdk" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/6b/76a7fe8f9f4c52cc84eaa1cd1b66acddf993496d55d6ea587bf0d0854d1c/foundry_local_sdk-0.5.1-py3-none-any.whl", hash = "sha256:f3639a3666bc3a94410004a91671338910ac2e1b8094b1587cc4db0f4a7df07e", size = 14003, upload-time = "2025-11-21T05:39:58.099Z" }, +] + [[package]] name = "frozenlist" version = "1.8.0" @@ -4475,8 +4504,8 @@ name = "powerfx" version = "0.0.33" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pythonnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "pythonnet", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5e/41/8f95f72f4f3b7ea54357c449bf5bd94813b6321dec31db9ffcbf578e2fa3/powerfx-0.0.33.tar.gz", hash = "sha256:85e8330bef8a7a207c3e010aa232df0ae38825e94d590c73daf3a3f44115cb09", size = 3236647, upload-time = "2025-11-20T19:31:09.414Z" } wheels = [ @@ -5145,7 +5174,7 @@ name = "pythonnet" version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "clr-loader", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "clr-loader", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } wheels = [