mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-02-12 08:58:19 +00:00
Compare commits
2 Commits
dependabot
...
codex/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afa4d01c22 | ||
|
|
78ad77e619 |
@@ -4,6 +4,7 @@ import json
|
||||
import logging
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, TypedDict
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Self
|
||||
@@ -175,11 +176,51 @@ class AzureCompletion(BaseLLM):
|
||||
prefix in model.lower() for prefix in ["gpt-", "o1-", "text-"]
|
||||
)
|
||||
|
||||
self.is_azure_openai_endpoint = (
|
||||
"openai.azure.com" in self.endpoint
|
||||
and "/openai/deployments/" in self.endpoint
|
||||
self.is_azure_openai_endpoint = self._is_azure_openai_deployment_endpoint(
|
||||
self.endpoint
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse_endpoint_url(endpoint: str):
|
||||
parsed_endpoint = urlparse(endpoint)
|
||||
if parsed_endpoint.hostname:
|
||||
return parsed_endpoint
|
||||
|
||||
# Support endpoint values without a URL scheme.
|
||||
return urlparse(f"https://{endpoint}")
|
||||
|
||||
@staticmethod
|
||||
def _is_azure_openai_hostname(endpoint: str) -> bool:
|
||||
parsed_endpoint = AzureCompletion._parse_endpoint_url(endpoint)
|
||||
hostname = parsed_endpoint.hostname or ""
|
||||
labels = [label for label in hostname.lower().split(".") if label]
|
||||
|
||||
return len(labels) >= 3 and labels[-3:] == ["openai", "azure", "com"]
|
||||
|
||||
@staticmethod
|
||||
def _get_endpoint_path_segments(endpoint: str) -> list[str]:
|
||||
parsed_endpoint = AzureCompletion._parse_endpoint_url(endpoint)
|
||||
return [segment for segment in parsed_endpoint.path.split("/") if segment]
|
||||
|
||||
@staticmethod
|
||||
def _is_azure_openai_deployment_endpoint(endpoint: str) -> bool:
|
||||
if not AzureCompletion._is_azure_openai_hostname(endpoint):
|
||||
return False
|
||||
|
||||
path_segments = AzureCompletion._get_endpoint_path_segments(endpoint)
|
||||
return len(path_segments) >= 3 and path_segments[:2] == [
|
||||
"openai",
|
||||
"deployments",
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _is_azure_openai_deployments_collection(endpoint: str) -> bool:
|
||||
if not AzureCompletion._is_azure_openai_hostname(endpoint):
|
||||
return False
|
||||
|
||||
path_segments = AzureCompletion._get_endpoint_path_segments(endpoint)
|
||||
return path_segments == ["openai", "deployments"]
|
||||
|
||||
@staticmethod
|
||||
def _validate_and_fix_endpoint(endpoint: str, model: str) -> str:
|
||||
"""Validate and fix Azure endpoint URL format.
|
||||
@@ -194,10 +235,12 @@ class AzureCompletion(BaseLLM):
|
||||
Returns:
|
||||
Validated and potentially corrected endpoint URL
|
||||
"""
|
||||
if "openai.azure.com" in endpoint and "/openai/deployments/" not in endpoint:
|
||||
if AzureCompletion._is_azure_openai_hostname(
|
||||
endpoint
|
||||
) and not AzureCompletion._is_azure_openai_deployment_endpoint(endpoint):
|
||||
endpoint = endpoint.rstrip("/")
|
||||
|
||||
if not endpoint.endswith("/openai/deployments"):
|
||||
if not AzureCompletion._is_azure_openai_deployments_collection(endpoint):
|
||||
deployment_name = model.replace("azure/", "")
|
||||
endpoint = f"{endpoint}/openai/deployments/{deployment_name}"
|
||||
logging.info(f"Constructed Azure OpenAI endpoint URL: {endpoint}")
|
||||
|
||||
@@ -958,6 +958,34 @@ def test_azure_endpoint_detection_flags():
|
||||
assert llm_other.is_azure_openai_endpoint == False
|
||||
|
||||
|
||||
def test_azure_endpoint_detection_ignores_spoofed_urls():
|
||||
"""
|
||||
Test that endpoint detection does not trust spoofed host/path substrings
|
||||
"""
|
||||
with patch.dict(os.environ, {
|
||||
"AZURE_API_KEY": "test-key",
|
||||
"AZURE_ENDPOINT": (
|
||||
"https://evil.example.com/?redirect="
|
||||
"https://test.openai.azure.com/openai/deployments/gpt-4"
|
||||
),
|
||||
}):
|
||||
llm_query_spoof = LLM(model="azure/gpt-4")
|
||||
assert llm_query_spoof.is_azure_openai_endpoint == False
|
||||
assert "model" in llm_query_spoof._prepare_completion_params(
|
||||
messages=[{"role": "user", "content": "test"}]
|
||||
)
|
||||
|
||||
with patch.dict(os.environ, {
|
||||
"AZURE_API_KEY": "test-key",
|
||||
"AZURE_ENDPOINT": "https://test.openai.azure.com.evil/openai/deployments/gpt-4",
|
||||
}):
|
||||
llm_host_spoof = LLM(model="azure/gpt-4")
|
||||
assert llm_host_spoof.is_azure_openai_endpoint == False
|
||||
assert "model" in llm_host_spoof._prepare_completion_params(
|
||||
messages=[{"role": "user", "content": "test"}]
|
||||
)
|
||||
|
||||
|
||||
def test_azure_improved_error_messages():
|
||||
"""
|
||||
Test that improved error messages are provided for common HTTP errors
|
||||
|
||||
@@ -10,7 +10,7 @@ authors = [
|
||||
dev = [
|
||||
"ruff==0.14.7",
|
||||
"mypy==1.19.0",
|
||||
"pre-commit==4.5.1",
|
||||
"pre-commit==4.5.0",
|
||||
"bandit==1.9.2",
|
||||
"pytest==8.4.2",
|
||||
"pytest-asyncio==1.3.0",
|
||||
|
||||
8
uv.lock
generated
8
uv.lock
generated
@@ -41,7 +41,7 @@ dev = [
|
||||
{ name = "bandit", specifier = "==1.9.2" },
|
||||
{ name = "boto3-stubs", extras = ["bedrock-runtime"], specifier = "==1.40.54" },
|
||||
{ name = "mypy", specifier = "==1.19.0" },
|
||||
{ name = "pre-commit", specifier = "==4.5.1" },
|
||||
{ name = "pre-commit", specifier = "==4.5.0" },
|
||||
{ name = "pytest", specifier = "==8.4.2" },
|
||||
{ name = "pytest-asyncio", specifier = "==1.3.0" },
|
||||
{ name = "pytest-randomly", specifier = "==4.0.1" },
|
||||
@@ -5346,7 +5346,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "4.5.1"
|
||||
version = "4.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cfgv" },
|
||||
@@ -5355,9 +5355,9 @@ dependencies = [
|
||||
{ name = "pyyaml" },
|
||||
{ name = "virtualenv" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428, upload-time = "2025-11-22T21:02:42.304Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429, upload-time = "2025-11-22T21:02:40.836Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user