mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-28 06:23:07 +00:00
feat(azure_ai): reach Foundry agents through the project Responses API
This commit is contained in:
parent
f5d65b8c06
commit
a58f1d620e
5 changed files with 102 additions and 1 deletions
|
|
@ -140,6 +140,15 @@ def get_azure_ai_auth_headers(
|
|||
AZURE_MODEL_ROUTER_SELECTED_MODEL_KEY: Final = "azure_model_router_selected_model"
|
||||
|
||||
|
||||
FOUNDRY_CLASSIC_AGENT_ID_PREFIX: Final = "asst_"
|
||||
|
||||
|
||||
def is_foundry_project_agent(model: str | None) -> bool:
|
||||
if model is None or AzureFoundryModelInfo.get_azure_ai_route(model) != "agents":
|
||||
return False
|
||||
return not model.split("agents/", 1)[1].startswith(FOUNDRY_CLASSIC_AGENT_ID_PREFIX)
|
||||
|
||||
|
||||
def azure_ai_supports_native_responses(model: str | None, api_base: str | None) -> bool:
|
||||
resolved_base: Final = AzureFoundryModelInfo.get_api_base(api_base)
|
||||
if resolved_base is not None and not is_azure_openai_v1_host(resolved_base):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from collections.abc import Collection, Mapping
|
||||
from typing import Final
|
||||
|
||||
import httpx
|
||||
|
|
@ -5,15 +6,19 @@ import httpx
|
|||
from litellm.llms.azure.responses.transformation import AzureOpenAIResponsesAPIConfig
|
||||
from litellm.llms.azure_ai.chat.transformation import AzureAIGPT5Config
|
||||
from litellm.llms.azure_ai.common_utils import (
|
||||
AZURE_AI_AGENTS_SCOPE,
|
||||
AzureFoundryModelInfo,
|
||||
api_key_header_for_base,
|
||||
get_azure_ai_auth_headers,
|
||||
)
|
||||
from litellm.types.llms.azure_ai import FoundryAgentReference
|
||||
from litellm.types.llms.openai import ResponseInputParam, ResponsesAPIOptionalRequestParams
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
_PROJECT_PATH_PREFIX: Final = ("api", "projects")
|
||||
_RESPONSES_PATH: Final = ("openai", "v1", "responses")
|
||||
_AGENT_OWNED_PARAMS: Final = frozenset({"instructions", "temperature", "top_p", "tools", "text", "reasoning"})
|
||||
|
||||
|
||||
def _responses_url(api_base: str) -> str:
|
||||
|
|
@ -64,3 +69,74 @@ class AzureAIResponsesAPIConfig(AzureOpenAIResponsesAPIConfig):
|
|||
"Set the api_base parameter or the AZURE_AI_API_BASE environment variable."
|
||||
)
|
||||
return _responses_url(resolved_base)
|
||||
|
||||
|
||||
def _agent_reference(model: str) -> FoundryAgentReference:
|
||||
name, _, version = model.split("agents/", 1)[1].partition(":")
|
||||
if version:
|
||||
return FoundryAgentReference(type="agent_reference", name=name, version=version)
|
||||
return FoundryAgentReference(type="agent_reference", name=name)
|
||||
|
||||
|
||||
class AzureAIAgentResponsesAPIConfig(AzureAIResponsesAPIConfig):
|
||||
def validate_environment(
|
||||
self,
|
||||
headers: dict[str, str], # mutable-ok: signature fixed by the override contract
|
||||
model: str,
|
||||
litellm_params: GenericLiteLLMParams | None,
|
||||
) -> dict[str, str]: # mutable-ok: same
|
||||
params: Final = litellm_params or GenericLiteLLMParams()
|
||||
return super().validate_environment( # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] # parent returns a bare dict
|
||||
headers, model, params.model_copy(update={"azure_scope": params.azure_scope or AZURE_AI_AGENTS_SCOPE})
|
||||
)
|
||||
|
||||
def should_fake_stream(
|
||||
self, model: str | None, stream: bool | None, custom_llm_provider: str | None = None
|
||||
) -> bool:
|
||||
return False
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list[str]: # mutable-ok: override contract
|
||||
inherited: Final[list[str]] = super().get_supported_openai_params(model) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] # parent returns a bare list # mutable-ok: same
|
||||
return [param for param in inherited if param not in _AGENT_OWNED_PARAMS]
|
||||
|
||||
def map_openai_params_for_deployment(
|
||||
self,
|
||||
response_api_optional_params: ResponsesAPIOptionalRequestParams,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
*,
|
||||
declared_reasoning_efforts: Collection[str],
|
||||
) -> Mapping[str, object]:
|
||||
mapped: Final = super().map_openai_params_for_deployment(
|
||||
response_api_optional_params, model, drop_params, declared_reasoning_efforts=declared_reasoning_efforts
|
||||
)
|
||||
return {key: value for key, value in mapped.items() if key not in _AGENT_OWNED_PARAMS}
|
||||
|
||||
def transform_responses_api_request(
|
||||
self,
|
||||
model: str,
|
||||
input: str | ResponseInputParam,
|
||||
response_api_optional_request_params: dict[str, object], # mutable-ok: signature fixed by the override contract
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
headers: dict[str, str], # mutable-ok: same
|
||||
) -> dict[str, object]: # mutable-ok: same
|
||||
request: Final[dict[str, object]] = super().transform_responses_api_request( # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] # parent returns a bare dict # mutable-ok: same
|
||||
model=model,
|
||||
input=input,
|
||||
response_api_optional_request_params=response_api_optional_request_params,
|
||||
litellm_params=litellm_params,
|
||||
headers=headers,
|
||||
)
|
||||
return {
|
||||
**{key: value for key, value in request.items() if key != "model"},
|
||||
"agent_reference": _agent_reference(model),
|
||||
}
|
||||
|
||||
def transform_extra_body(
|
||||
self,
|
||||
extra_body: Mapping[str, object],
|
||||
request: Mapping[str, object],
|
||||
model: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
) -> Mapping[str, object]:
|
||||
return {**extra_body, "agent_reference": _agent_reference(model)}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from litellm.llms.azure_ai.chat.transformation import AzureAIGPT5Config
|
|||
from litellm.llms.azure_ai.common_utils import (
|
||||
azure_ai_supports_native_responses,
|
||||
foundry_chat_rejects_function_tools_while_reasoning,
|
||||
is_foundry_project_agent,
|
||||
)
|
||||
from litellm.llms.openai.chat.gpt_5_transformation import OpenAIGPT5Config
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
|
|
@ -181,6 +182,9 @@ def responses_api_bridge_check(
|
|||
elif pinned_mode == "chat" and backend_serves_chat_completions(model_info):
|
||||
model_info["mode"] = "chat"
|
||||
|
||||
if custom_llm_provider == LlmProviders.AZURE_AI.value and is_foundry_project_agent(model):
|
||||
model_info["mode"] = "responses"
|
||||
|
||||
if web_search_options is not None and custom_llm_provider == "xai":
|
||||
model_info["mode"] = "responses"
|
||||
model = model.replace("responses/", "")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Literal
|
||||
|
||||
from typing_extensions import Required, TypedDict
|
||||
from typing_extensions import NotRequired, ReadOnly, Required, TypedDict
|
||||
|
||||
|
||||
class ImageEmbeddingInput(TypedDict, total=False):
|
||||
|
|
@ -15,3 +15,9 @@ class ImageEmbeddingRequest(TypedDict, total=False):
|
|||
input: Required[list[ImageEmbeddingInput]]
|
||||
dimensions: int
|
||||
encoding_format: EncodingFormat
|
||||
|
||||
|
||||
class FoundryAgentReference(TypedDict):
|
||||
type: ReadOnly[Literal["agent_reference"]]
|
||||
name: ReadOnly[str]
|
||||
version: ReadOnly[NotRequired[str]]
|
||||
|
|
|
|||
|
|
@ -9832,8 +9832,14 @@ class ProviderConfigManager:
|
|||
elif litellm.LlmProviders.AZURE_AI == provider:
|
||||
from litellm.llms.azure_ai.common_utils import (
|
||||
azure_ai_supports_native_responses,
|
||||
is_foundry_project_agent,
|
||||
)
|
||||
from litellm.llms.azure_ai.responses.transformation import (
|
||||
AzureAIAgentResponsesAPIConfig,
|
||||
)
|
||||
|
||||
if is_foundry_project_agent(model):
|
||||
return AzureAIAgentResponsesAPIConfig()
|
||||
if azure_ai_supports_native_responses(model, api_base):
|
||||
return litellm.AzureAIResponsesAPIConfig()
|
||||
return None
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue