From 9a3faebea281be1cfac25dd2a4f1b21a16296015 Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Thu, 9 Apr 2026 22:45:02 -0500 Subject: [PATCH 1/3] Bump uv_build upper bound to <0.11.0 uv 0.10.x is current; the <0.10.0 constraint caused build warnings. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 01470ecf..1339e34e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["uv_build>=0.9.10,<0.10.0"] +requires = ["uv_build>=0.9.10,<0.11.0"] build-backend = "uv_build" [project] From 75ec37b0d07027f2281e92ef70c01f39220fe528 Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Fri, 10 Apr 2026 04:13:38 -0500 Subject: [PATCH 2/3] Fix parse_azure_endpoint passing query string to AsyncAzureOpenAI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_azure_endpoint returned the raw URL including ?api-version=... which AsyncAzureOpenAI then mangled into invalid paths like ...?api-version=2024-06-01/openai/. Strip the query string before returning — api_version is already returned as a separate value and passed to the SDK independently. --- src/typeagent/aitools/utils.py | 6 ++++- tests/test_utils.py | 40 +++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/typeagent/aitools/utils.py b/src/typeagent/aitools/utils.py index adba401a..46e81fc1 100644 --- a/src/typeagent/aitools/utils.py +++ b/src/typeagent/aitools/utils.py @@ -197,7 +197,11 @@ def parse_azure_endpoint( f"{endpoint_envvar}={azure_endpoint} doesn't contain valid api-version field" ) - return azure_endpoint, m.group(1) + # Strip query string — AsyncAzureOpenAI expects a clean base URL and + # receives api_version as a separate parameter. + clean_endpoint = azure_endpoint.split("?", 1)[0] + + return clean_endpoint, m.group(1) def get_azure_api_key(azure_api_key: str) -> str: diff --git a/tests/test_utils.py b/tests/test_utils.py index 5966af61..7f806f74 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -67,7 +67,7 @@ def test_api_version_after_question_mark( ) endpoint, version = utils.parse_azure_endpoint("TEST_ENDPOINT") assert version == "2025-01-01-preview" - assert endpoint.startswith("https://") + assert endpoint == "https://myhost.openai.azure.com/openai/deployments/gpt-4" def test_api_version_after_ampersand(self, monkeypatch: pytest.MonkeyPatch) -> None: """api-version preceded by & (not the first query parameter).""" @@ -84,6 +84,44 @@ def test_missing_env_var_raises(self, monkeypatch: pytest.MonkeyPatch) -> None: with pytest.raises(RuntimeError, match="not found"): utils.parse_azure_endpoint("NONEXISTENT_ENDPOINT") + def test_query_string_stripped_from_endpoint( + self, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Returned endpoint should not contain query string parameters.""" + monkeypatch.setenv( + "TEST_ENDPOINT", + "https://myhost.openai.azure.com?api-version=2024-06-01", + ) + endpoint, version = utils.parse_azure_endpoint("TEST_ENDPOINT") + assert endpoint == "https://myhost.openai.azure.com" + assert version == "2024-06-01" + + def test_query_string_stripped_with_path( + self, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Query string stripped even when endpoint includes a path.""" + monkeypatch.setenv( + "TEST_ENDPOINT", + "https://myhost.openai.azure.com/openai/deployments/gpt-4?api-version=2025-01-01-preview", + ) + endpoint, version = utils.parse_azure_endpoint("TEST_ENDPOINT") + assert endpoint == "https://myhost.openai.azure.com/openai/deployments/gpt-4" + assert "?" not in endpoint + assert version == "2025-01-01-preview" + + def test_query_string_stripped_multiple_params( + self, monkeypatch: pytest.MonkeyPatch + ) -> None: + """All query parameters stripped, not just api-version.""" + monkeypatch.setenv( + "TEST_ENDPOINT", + "https://myhost.openai.azure.com?foo=bar&api-version=2024-06-01", + ) + endpoint, version = utils.parse_azure_endpoint("TEST_ENDPOINT") + assert endpoint == "https://myhost.openai.azure.com" + assert "foo" not in endpoint + assert version == "2024-06-01" + def test_no_api_version_raises(self, monkeypatch: pytest.MonkeyPatch) -> None: """RuntimeError when the endpoint has no api-version field.""" monkeypatch.setenv( From bcf75b44032a1582fdcb9c88ac8a7e4344799b6d Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Thu, 9 Apr 2026 23:17:37 -0500 Subject: [PATCH 3/3] Defer black import to first use black is only used in create_context_prompt() and format_code() -- both cold paths. Moving the import inside the functions avoids loading black and its transitive deps (pathspec, black.nodes, etc.) on every import typeagent. --- src/typeagent/aitools/utils.py | 3 ++- src/typeagent/knowpro/answers.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/typeagent/aitools/utils.py b/src/typeagent/aitools/utils.py index 46e81fc1..eb21f9ec 100644 --- a/src/typeagent/aitools/utils.py +++ b/src/typeagent/aitools/utils.py @@ -11,7 +11,6 @@ import sys import time -import black import colorama import typechat @@ -57,6 +56,8 @@ def format_code(text: str, line_width=None) -> str: NOTE: The text must be a valid Python expression or code block. """ + import black + if line_width is None: # Use the terminal width, but cap it to 200 characters. line_width = min(200, shutil.get_terminal_size().columns) diff --git a/src/typeagent/knowpro/answers.py b/src/typeagent/knowpro/answers.py index 2c300506..58a536ed 100644 --- a/src/typeagent/knowpro/answers.py +++ b/src/typeagent/knowpro/answers.py @@ -5,8 +5,6 @@ from dataclasses import dataclass from typing import Any -import black - import typechat from .answer_context_schema import AnswerContext, RelevantKnowledge, RelevantMessage @@ -127,6 +125,8 @@ def create_question_prompt(question: str) -> str: def create_context_prompt(context: AnswerContext) -> str: # TODO: Use a more compact representation of the context than JSON. + import black + prompt = [ "[ANSWER CONTEXT]", "===",