Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bc2/core/common/azure_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def estimate(
self, call: dict[str, Any], runtime_config: dict[str, Any]
) -> dict[str, Any]:
"""Estimate the cost of one Azure service call."""
region = runtime_config.get("azure_region")
region = runtime_config.get("azure_region") or call.get("azure_region")
if not region:
raise AzurePricingUnavailable(
"azure_region is required to look up Azure retail pricing"
Expand Down
13 changes: 13 additions & 0 deletions bc2/core/common/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ def _record_response_usage(
"model": config.openai_model
or (reported_model if isinstance(reported_model, str) else config.model),
"deployment": config.model if provider == "azure" else None,
"azure_region": _azure_region(client) if provider == "azure" else None,
"response_id": getattr(response, "id", None),
"status": getattr(response, "status", None),
"usage": token_usage,
Expand All @@ -618,5 +619,17 @@ def _openai_provider(client: OpenAI | AsyncOpenAI) -> str:
return "openai"


def _azure_region(client: OpenAI | AsyncOpenAI) -> str | None:
"""Infer the region suffix from an Azure OpenAI resource hostname."""
base_url = str(getattr(client, "base_url", ""))
host = (urlparse(base_url).hostname or "").lower()
suffix = ".openai.azure.com"
if not host.endswith(suffix):
return None

resource_name = host.removesuffix(suffix).rsplit(".", 1)[-1]
return resource_name.rsplit("-", 1)[-1] or None


class OpenAIConfig(BaseModel):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The _azure_region function may incorrectly extract a region from an Azure OpenAI hostname if the name doesn't contain a hyphen, causing cost estimation to fail.
Severity: MEDIUM

Suggested Fix

The _azure_region function should be updated to handle hostnames without a hyphen. One approach is to return None if rsplit("-", 1) results in a list with only one element, indicating no hyphen was found. Alternatively, the extracted value could be validated against a list of known Azure region names.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: bc2/core/common/openai.py#L634

Potential issue: The `_azure_region` function attempts to infer the Azure region by
splitting the resource hostname on the last hyphen. However, if an Azure OpenAI resource
name does not contain a hyphen (e.g., `myresource.openai.azure.com`), the function will
incorrectly return the entire resource name as the region. This invalid region name is
then used to query the Azure Retail Prices API, which returns no results. Consequently,
the application will raise an `AzurePricingUnavailable` exception, preventing cost
estimation for that resource.

Did we get this right? 👍 / 👎 to inform future reviews.

client: OpenAIClientConfig
24 changes: 24 additions & 0 deletions bc2/core/common/test_azure_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,30 @@ def test_missing_region_fails_without_fetching_prices():
)


def test_region_falls_back_to_usage_call(monkeypatch):
pricing = AzureRetailPricing()
monkeypatch.setattr(
pricing,
"_estimate_openai_tokens",
lambda call, runtime_config, region: {
"estimated_cost": 0.0,
"currency": "USD",
},
)

estimate = pricing.estimate(
{
"service": "responses",
"model": "gpt-4.1",
"azure_region": "eastus",
"usage": {"input_tokens": 10},
},
{},
)

assert estimate["region"] == "eastus"


def test_ambiguous_meter_fails_gracefully(monkeypatch):
pricing = AzureRetailPricing()
meters = [
Expand Down
3 changes: 2 additions & 1 deletion bc2/core/common/test_openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ def test_invoke_completed_response_is_not_truncated():
def test_invoke_records_response_usage():
cfg = _build_chat_config()
client = MagicMock()
client.base_url = "https://example.openai.azure.com/openai/v1/"
client.base_url = "https://hks-cpl-blindcharging-eastus.openai.azure.com/openai/v1/"
client.responses.create.return_value = _mock_response(
status="completed",
output_text="full answer",
Expand All @@ -331,6 +331,7 @@ def test_invoke_records_response_usage():

call = report["calls"][0]
assert call["provider"] == "azure"
assert call["azure_region"] == "eastus"
assert call["service"] == "responses"
assert call["operation"] == "parse:openai"
assert call["response_id"] == "resp_test"
Expand Down
Loading