swarmauri_llm_azureopenai 0.11.0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: swarmauri_llm_azureopenai
3
+ Version: 0.11.0.dev2
4
+ Summary: Provider-native Swarmauri chat and tool-calling adapters for Azure OpenAI.
5
+ License-Expression: Apache-2.0
6
+ Keywords: swarmauri,llm,azure openai,microsoft foundry,entra id,tool calling,streaming
7
+ Author: Jacob Stewart
8
+ Author-email: jacob@swarmauri.com
9
+ Requires-Python: >=3.10,<3.13
10
+ Classifier: Development Status :: 1 - Planning
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: swarmauri_base (>=0.11.0.dev5)
16
+ Requires-Dist: swarmauri_core (>=0.11.0.dev5)
17
+ Requires-Dist: swarmauri_standard (>=0.11.0.dev5)
18
+ Requires-Dist: swarmauri_transport_httpx (>=0.1.0.dev4)
19
+ Requires-Dist: swarmauri_transport_retry (>=0.1.0.dev1)
20
+ Description-Content-Type: text/markdown
21
+
22
+ ![Swarmauri](https://raw.githubusercontent.com/swarmauri/swarmauri-sdk/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
23
+
24
+ <p align="center">
25
+ <a href="https://pepy.tech/project/swarmauri_llm_azureopenai"><img src="https://static.pepy.tech/badge/swarmauri_llm_azureopenai/month" alt="Downloads"></a>
26
+ <a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/community/swarmauri_llm_azureopenai/"><img src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/community/swarmauri_llm_azureopenai.svg" alt="Hits"></a>
27
+ <a href="https://pypi.org/project/swarmauri_llm_azureopenai/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue" alt="Python"></a>
28
+ <a href="https://pypi.org/project/swarmauri_llm_azureopenai/"><img src="https://img.shields.io/pypi/l/swarmauri_llm_azureopenai" alt="License"></a>
29
+ <a href="https://pypi.org/project/swarmauri_llm_azureopenai/"><img src="https://img.shields.io/pypi/v/swarmauri_llm_azureopenai" alt="Release"></a>
30
+ </p>
31
+
32
+ # Swarmauri Azure OpenAI LLM
33
+
34
+ `swarmauri_llm_azureopenai` provides provider-native Swarmauri adapters for Azure OpenAI and Microsoft Foundry chat-completions deployments.
35
+
36
+ ## Features
37
+
38
+ - `AzureOpenAIModel` directly implements the Swarmauri LLM base contract.
39
+ - `AzureOpenAIToolModel` directly implements the tool-LLM base contract.
40
+ - API-key authentication with Azure's `api-key` header.
41
+ - Refreshable Microsoft Entra bearer-token providers kept outside serialized state.
42
+ - Sync, async, streaming, and batch workflows against the current `/openai/v1` API.
43
+ - Persistent, injectable Swarmauri HTTP clients with conservative rate-limit retries.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ uv add swarmauri_llm_azureopenai
49
+ ```
50
+
51
+ ```bash
52
+ pip install swarmauri_llm_azureopenai
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ ```python
58
+ import os
59
+
60
+ from swarmauri_llm_azureopenai import AzureOpenAIModel
61
+ from swarmauri_standard.conversations.Conversation import Conversation
62
+ from swarmauri_standard.messages.HumanMessage import HumanMessage
63
+
64
+ conversation = Conversation(messages=[HumanMessage(content="Summarize this release.")])
65
+ model = AzureOpenAIModel(
66
+ endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
67
+ api_key=os.environ["AZURE_OPENAI_API_KEY"],
68
+ name="my-gpt-deployment",
69
+ )
70
+ model.predict(conversation)
71
+ print(conversation.get_last().content)
72
+ ```
73
+
74
+ For keyless authentication, omit `api_key` and pass a callable as `token_provider`; it is invoked for every request so refreshed Entra tokens are used. Use `AzureOpenAIToolModel` with a Swarmauri `Toolkit` for function calling.
75
+
76
+ Pass an `IHttpClient` as `http_client` to integrate a custom transport or to test without network access. Pass an `IHttpRetryPolicy` as `retry_policy` to replace the default policy, which retries safe reads and rate-limited chat submissions without replaying failed POST requests for generic server errors.
77
+
78
+ See the [Azure OpenAI chat REST reference](https://learn.microsoft.com/en-us/rest/api/microsoft-foundry/azureopenai/chat) and [Entra authentication guide](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/how-to/configure-entra-id).
79
+
@@ -0,0 +1,57 @@
1
+ ![Swarmauri](https://raw.githubusercontent.com/swarmauri/swarmauri-sdk/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
2
+
3
+ <p align="center">
4
+ <a href="https://pepy.tech/project/swarmauri_llm_azureopenai"><img src="https://static.pepy.tech/badge/swarmauri_llm_azureopenai/month" alt="Downloads"></a>
5
+ <a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/community/swarmauri_llm_azureopenai/"><img src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/community/swarmauri_llm_azureopenai.svg" alt="Hits"></a>
6
+ <a href="https://pypi.org/project/swarmauri_llm_azureopenai/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue" alt="Python"></a>
7
+ <a href="https://pypi.org/project/swarmauri_llm_azureopenai/"><img src="https://img.shields.io/pypi/l/swarmauri_llm_azureopenai" alt="License"></a>
8
+ <a href="https://pypi.org/project/swarmauri_llm_azureopenai/"><img src="https://img.shields.io/pypi/v/swarmauri_llm_azureopenai" alt="Release"></a>
9
+ </p>
10
+
11
+ # Swarmauri Azure OpenAI LLM
12
+
13
+ `swarmauri_llm_azureopenai` provides provider-native Swarmauri adapters for Azure OpenAI and Microsoft Foundry chat-completions deployments.
14
+
15
+ ## Features
16
+
17
+ - `AzureOpenAIModel` directly implements the Swarmauri LLM base contract.
18
+ - `AzureOpenAIToolModel` directly implements the tool-LLM base contract.
19
+ - API-key authentication with Azure's `api-key` header.
20
+ - Refreshable Microsoft Entra bearer-token providers kept outside serialized state.
21
+ - Sync, async, streaming, and batch workflows against the current `/openai/v1` API.
22
+ - Persistent, injectable Swarmauri HTTP clients with conservative rate-limit retries.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ uv add swarmauri_llm_azureopenai
28
+ ```
29
+
30
+ ```bash
31
+ pip install swarmauri_llm_azureopenai
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ```python
37
+ import os
38
+
39
+ from swarmauri_llm_azureopenai import AzureOpenAIModel
40
+ from swarmauri_standard.conversations.Conversation import Conversation
41
+ from swarmauri_standard.messages.HumanMessage import HumanMessage
42
+
43
+ conversation = Conversation(messages=[HumanMessage(content="Summarize this release.")])
44
+ model = AzureOpenAIModel(
45
+ endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
46
+ api_key=os.environ["AZURE_OPENAI_API_KEY"],
47
+ name="my-gpt-deployment",
48
+ )
49
+ model.predict(conversation)
50
+ print(conversation.get_last().content)
51
+ ```
52
+
53
+ For keyless authentication, omit `api_key` and pass a callable as `token_provider`; it is invoked for every request so refreshed Entra tokens are used. Use `AzureOpenAIToolModel` with a Swarmauri `Toolkit` for function calling.
54
+
55
+ Pass an `IHttpClient` as `http_client` to integrate a custom transport or to test without network access. Pass an `IHttpRetryPolicy` as `retry_policy` to replace the default policy, which retries safe reads and rate-limited chat submissions without replaying failed POST requests for generic server errors.
56
+
57
+ See the [Azure OpenAI chat REST reference](https://learn.microsoft.com/en-us/rest/api/microsoft-foundry/azureopenai/chat) and [Entra authentication guide](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/how-to/configure-entra-id).
@@ -0,0 +1,46 @@
1
+ [project]
2
+ name = "swarmauri_llm_azureopenai"
3
+ version = "0.11.0.dev2"
4
+ description = "Provider-native Swarmauri chat and tool-calling adapters for Azure OpenAI."
5
+ license = "Apache-2.0"
6
+ readme = "README.md"
7
+ requires-python = ">=3.10,<3.13"
8
+ authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
9
+ keywords = ["swarmauri", "llm", "azure openai", "microsoft foundry", "entra id", "tool calling", "streaming"]
10
+ classifiers = [
11
+ "Development Status :: 1 - Planning",
12
+ "License :: OSI Approved :: Apache Software License",
13
+ "Programming Language :: Python :: 3.10",
14
+ "Programming Language :: Python :: 3.11",
15
+ "Programming Language :: Python :: 3.12",
16
+ ]
17
+ dependencies = [
18
+ "swarmauri_base>=0.11.0.dev5",
19
+ "swarmauri_core>=0.11.0.dev5",
20
+ "swarmauri_standard>=0.11.0.dev5",
21
+ "swarmauri_transport_httpx>=0.1.0.dev4",
22
+ "swarmauri_transport_retry>=0.1.0.dev1",
23
+ ]
24
+
25
+ [tool.uv.sources]
26
+ swarmauri_base = { workspace = true }
27
+ swarmauri_core = { workspace = true }
28
+ swarmauri_standard = { workspace = true }
29
+ swarmauri_transport_httpx = { workspace = true }
30
+ swarmauri_transport_retry = { workspace = true }
31
+
32
+ [project.entry-points."swarmauri.llms"]
33
+ AzureOpenAIModel = "swarmauri_llm_azureopenai:AzureOpenAIModel"
34
+
35
+ [project.entry-points."swarmauri.tool_llms"]
36
+ AzureOpenAIToolModel = "swarmauri_llm_azureopenai:AzureOpenAIToolModel"
37
+
38
+ [build-system]
39
+ requires = ["poetry-core>=1.0.0"]
40
+ build-backend = "poetry.core.masonry.api"
41
+
42
+ [dependency-groups]
43
+ dev = ["pytest>=8.0", "pytest-asyncio>=0.24", "ruff>=0.9.9"]
44
+
45
+ [tool.pytest.ini_options]
46
+ markers = ["unit: Unit tests"]
@@ -0,0 +1,34 @@
1
+ """Retry policy for Azure OpenAI chat operations."""
2
+
3
+ from swarmauri_core.transports.i_http_response import IHttpResponse
4
+ from swarmauri_transport_retry import ExponentialBackoffRetryPolicy
5
+
6
+
7
+ class AzureOpenAIChatRetryPolicy(ExponentialBackoffRetryPolicy):
8
+ """Retry safe reads and rate-limited chat submissions."""
9
+
10
+ def __init__(self, **kwargs) -> None:
11
+ kwargs.setdefault("retryable_methods", {"GET", "POST"})
12
+ super().__init__(**kwargs)
13
+
14
+ def should_retry(
15
+ self,
16
+ *,
17
+ method: str,
18
+ attempt: int,
19
+ response: IHttpResponse | None = None,
20
+ error: Exception | None = None,
21
+ ) -> bool:
22
+ if method.upper() == "POST":
23
+ return (
24
+ attempt < self.max_attempts
25
+ and error is None
26
+ and response is not None
27
+ and response.status_code == 429
28
+ )
29
+ return super().should_retry(
30
+ method=method,
31
+ attempt=attempt,
32
+ response=response,
33
+ error=error,
34
+ )
@@ -0,0 +1,111 @@
1
+ from typing import Any, Callable, ClassVar, FrozenSet, List, Literal, Optional
2
+
3
+ from pydantic import Field, PrivateAttr
4
+ from swarmauri_base.ComponentBase import ComponentBase
5
+ from swarmauri_base.llms.LLMBase import LLMBase
6
+ from swarmauri_core.transports.i_http_client import IHttpClient
7
+ from swarmauri_core.transports.i_http_retry_policy import IHttpRetryPolicy
8
+ from swarmauri_core.transports.i_managed_http_client import IManagedHttpClient
9
+ from swarmauri_standard.utils.provider_chat_transport import (
10
+ abatch_chat,
11
+ apredict_chat,
12
+ astream_chat,
13
+ batch_chat,
14
+ predict_chat,
15
+ stream_chat,
16
+ )
17
+
18
+ from ._client import build_azure_openai_client
19
+
20
+
21
+ @ComponentBase.register_type()
22
+ class AzureOpenAIModel(LLMBase):
23
+ """Provider-native Azure OpenAI chat-completions model."""
24
+
25
+ type: Literal["AzureOpenAIModel"] = "AzureOpenAIModel"
26
+ endpoint: str = Field(min_length=1)
27
+ api_version: str = "v1"
28
+ name: str = Field(min_length=1)
29
+ discover_models: bool = False
30
+ capabilities: ClassVar[FrozenSet[str]] = frozenset(
31
+ {"chat_completions", "structured_output", "streaming", "tools"}
32
+ )
33
+ _token_provider: Optional[Callable[[], Any]] = PrivateAttr(default=None)
34
+ _http_client: IHttpClient = PrivateAttr()
35
+ _retry_owned_by_transport: bool = PrivateAttr(default=True)
36
+
37
+ predict = predict_chat
38
+ apredict = apredict_chat
39
+ stream = stream_chat
40
+ astream = astream_chat
41
+ batch = batch_chat
42
+ abatch = abatch_chat
43
+
44
+ def __init__(
45
+ self,
46
+ *,
47
+ token_provider: Optional[Callable[[], Any]] = None,
48
+ http_client: IHttpClient | None = None,
49
+ retry_policy: IHttpRetryPolicy | None = None,
50
+ **data: Any,
51
+ ) -> None:
52
+ selected_name = data.get("name")
53
+ if selected_name:
54
+ data.setdefault("allowed_models", [selected_name])
55
+ super().__init__(**data)
56
+ self._token_provider = token_provider
57
+ if self.api_key is None and self._token_provider is None:
58
+ raise ValueError("api_key or token_provider is required")
59
+ self._http_client = build_azure_openai_client(
60
+ base_url=self._api_root(),
61
+ timeout=self.timeout,
62
+ max_attempts=self.max_retries,
63
+ http_client=http_client,
64
+ retry_policy=retry_policy,
65
+ )
66
+ if self.discover_models:
67
+ self.allowed_models = self._get_models()
68
+ elif not self.allowed_models:
69
+ self.allowed_models = [self.name]
70
+
71
+ def _api_root(self) -> str:
72
+ root = self.endpoint.rstrip("/")
73
+ suffix = f"/openai/{self.api_version}"
74
+ return root if root.endswith(suffix) else f"{root}{suffix}"
75
+
76
+ def _build_endpoint(self) -> str:
77
+ return f"{self._api_root()}/chat/completions"
78
+
79
+ def _build_headers(self) -> dict[str, str]:
80
+ headers = {"Content-Type": "application/json"}
81
+ if self.api_key is not None:
82
+ headers["api-key"] = self.api_key.get_secret_value()
83
+ else:
84
+ token = self._token_provider()
85
+ headers["Authorization"] = (
86
+ f"Bearer {getattr(token, 'token', token)}"
87
+ )
88
+ return headers
89
+
90
+ def _get_models(self) -> List[str]:
91
+ if not self.discover_models:
92
+ return [self.name]
93
+ response = self._http_client.request(
94
+ "GET",
95
+ f"{self._api_root()}/models",
96
+ headers=self._build_headers(),
97
+ )
98
+ response.raise_for_status()
99
+ return [
100
+ item["id"]
101
+ for item in response.json().get("data", [])
102
+ if item.get("id")
103
+ ]
104
+
105
+ def close(self) -> None:
106
+ if isinstance(self._http_client, IManagedHttpClient):
107
+ self._http_client.close()
108
+
109
+ async def aclose(self) -> None:
110
+ if isinstance(self._http_client, IManagedHttpClient):
111
+ await self._http_client.aclose()
@@ -0,0 +1,121 @@
1
+ from typing import Any, Callable, ClassVar, FrozenSet, List, Literal, Optional
2
+
3
+ from pydantic import Field, PrivateAttr
4
+ from swarmauri_base.ComponentBase import ComponentBase
5
+ from swarmauri_base.tool_llms.ToolLLMBase import ToolLLMBase
6
+ from swarmauri_core.transports.i_http_client import IHttpClient
7
+ from swarmauri_core.transports.i_http_retry_policy import IHttpRetryPolicy
8
+ from swarmauri_core.transports.i_managed_http_client import IManagedHttpClient
9
+ from swarmauri_standard.schema_converters.OpenAISchemaConverter import (
10
+ OpenAISchemaConverter,
11
+ )
12
+
13
+ from ._client import build_azure_openai_client
14
+ from swarmauri_standard.utils.provider_chat_transport import (
15
+ abatch_tools,
16
+ apredict_tools,
17
+ astream_tools,
18
+ batch_tools,
19
+ format_messages,
20
+ predict_tools,
21
+ stream_tools,
22
+ )
23
+
24
+
25
+ @ComponentBase.register_type()
26
+ class AzureOpenAIToolModel(ToolLLMBase):
27
+ """Provider-native Azure OpenAI tool-calling model."""
28
+
29
+ type: Literal["AzureOpenAIToolModel"] = "AzureOpenAIToolModel"
30
+ endpoint: str = Field(min_length=1)
31
+ api_version: str = "v1"
32
+ name: str = Field(min_length=1)
33
+ discover_models: bool = False
34
+ capabilities: ClassVar[FrozenSet[str]] = frozenset(
35
+ {"chat_completions", "streaming", "tools"}
36
+ )
37
+ _token_provider: Optional[Callable[[], Any]] = PrivateAttr(default=None)
38
+ _http_client: IHttpClient = PrivateAttr()
39
+ _retry_owned_by_transport: bool = PrivateAttr(default=True)
40
+
41
+ predict = predict_tools
42
+ apredict = apredict_tools
43
+ stream = stream_tools
44
+ astream = astream_tools
45
+ batch = batch_tools
46
+ abatch = abatch_tools
47
+
48
+ def __init__(
49
+ self,
50
+ *,
51
+ token_provider: Optional[Callable[[], Any]] = None,
52
+ http_client: IHttpClient | None = None,
53
+ retry_policy: IHttpRetryPolicy | None = None,
54
+ **data: Any,
55
+ ) -> None:
56
+ selected_name = data.get("name")
57
+ if selected_name:
58
+ data.setdefault("allowed_models", [selected_name])
59
+ super().__init__(**data)
60
+ self._token_provider = token_provider
61
+ if self.api_key is None and self._token_provider is None:
62
+ raise ValueError("api_key or token_provider is required")
63
+ self._http_client = build_azure_openai_client(
64
+ base_url=self._api_root(),
65
+ timeout=self.timeout,
66
+ max_attempts=self.max_retries,
67
+ http_client=http_client,
68
+ retry_policy=retry_policy,
69
+ )
70
+ if self.discover_models:
71
+ self.allowed_models = self._get_models()
72
+ elif not self.allowed_models:
73
+ self.allowed_models = [self.name]
74
+
75
+ def get_schema_converter(self):
76
+ return OpenAISchemaConverter()
77
+
78
+ def _format_messages(self, messages):
79
+ return format_messages(messages)
80
+
81
+ def _api_root(self) -> str:
82
+ root = self.endpoint.rstrip("/")
83
+ suffix = f"/openai/{self.api_version}"
84
+ return root if root.endswith(suffix) else f"{root}{suffix}"
85
+
86
+ def _build_endpoint(self) -> str:
87
+ return f"{self._api_root()}/chat/completions"
88
+
89
+ def _build_headers(self) -> dict[str, str]:
90
+ headers = {"Content-Type": "application/json"}
91
+ if self.api_key is not None:
92
+ headers["api-key"] = self.api_key.get_secret_value()
93
+ else:
94
+ token = self._token_provider()
95
+ headers["Authorization"] = (
96
+ f"Bearer {getattr(token, 'token', token)}"
97
+ )
98
+ return headers
99
+
100
+ def _get_models(self) -> List[str]:
101
+ if not self.discover_models:
102
+ return [self.name]
103
+ response = self._http_client.request(
104
+ "GET",
105
+ f"{self._api_root()}/models",
106
+ headers=self._build_headers(),
107
+ )
108
+ response.raise_for_status()
109
+ return [
110
+ item["id"]
111
+ for item in response.json().get("data", [])
112
+ if item.get("id")
113
+ ]
114
+
115
+ def close(self) -> None:
116
+ if isinstance(self._http_client, IManagedHttpClient):
117
+ self._http_client.close()
118
+
119
+ async def aclose(self) -> None:
120
+ if isinstance(self._http_client, IManagedHttpClient):
121
+ await self._http_client.aclose()
@@ -0,0 +1,16 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ from .AzureOpenAIChatRetryPolicy import AzureOpenAIChatRetryPolicy
4
+ from .AzureOpenAIModel import AzureOpenAIModel
5
+ from .AzureOpenAIToolModel import AzureOpenAIToolModel
6
+
7
+ try:
8
+ __version__ = version("swarmauri_llm_azureopenai")
9
+ except PackageNotFoundError:
10
+ __version__ = "0.0.0"
11
+
12
+ __all__ = [
13
+ "AzureOpenAIChatRetryPolicy",
14
+ "AzureOpenAIModel",
15
+ "AzureOpenAIToolModel",
16
+ ]
@@ -0,0 +1,29 @@
1
+ """HTTP client composition for Azure OpenAI models."""
2
+
3
+ from swarmauri_core.transports.i_http_client import IHttpClient
4
+ from swarmauri_core.transports.i_http_retry_policy import IHttpRetryPolicy
5
+ from swarmauri_transport_httpx import HttpxClient
6
+ from swarmauri_transport_retry import RetryingHttpClient
7
+
8
+ from .AzureOpenAIChatRetryPolicy import AzureOpenAIChatRetryPolicy
9
+
10
+
11
+ def build_azure_openai_client(
12
+ *,
13
+ base_url: str,
14
+ timeout: float,
15
+ max_attempts: int,
16
+ http_client: IHttpClient | None,
17
+ retry_policy: IHttpRetryPolicy | None,
18
+ ) -> IHttpClient:
19
+ """Build a persistent client while preserving injected ownership."""
20
+ concrete_client = http_client or HttpxClient(
21
+ base_url=base_url,
22
+ timeout=timeout,
23
+ )
24
+ return RetryingHttpClient(
25
+ client=concrete_client,
26
+ policy=retry_policy
27
+ or AzureOpenAIChatRetryPolicy(max_attempts=max(max_attempts, 1)),
28
+ own_client=http_client is None,
29
+ )