From 0ac425516d400acd36669fac727d1cd2951b750a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 17:57:41 +0900 Subject: [PATCH 1/2] fix: accept deployment alias for orchestrated chat --- contextual_orchestrator/server.py | 4 +++ ...st_chat_orchestration_mode_http_honesty.py | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/contextual_orchestrator/server.py b/contextual_orchestrator/server.py index 6221f8932..bebe339b1 100644 --- a/contextual_orchestrator/server.py +++ b/contextual_orchestrator/server.py @@ -2118,6 +2118,10 @@ def _require_pool_model( answering with a different pool agent hides capacity/routing mismatches. """ agents = getattr(orchestrator, "agents", None) or [] + if model_name == "contextual-orchestrator" and required_capability is None: + if any(not getattr(agent, "disabled", False) for agent in agents): + return model_name + raise RequestError(400, "invalid_model", "no enabled orchestration agent is available") if model_name in {TaskOrchestrator.AUTO_MODEL, TaskOrchestrator.FREE_MODEL}: if required_capability is None: if model_name == TaskOrchestrator.AUTO_MODEL or any( diff --git a/tests/test_chat_orchestration_mode_http_honesty.py b/tests/test_chat_orchestration_mode_http_honesty.py index 8ce21f8e6..78b89e338 100644 --- a/tests/test_chat_orchestration_mode_http_honesty.py +++ b/tests/test_chat_orchestration_mode_http_honesty.py @@ -86,6 +86,31 @@ def test_http_chat_accepts_orchestration_mode_auto() -> None: thread.join(timeout=5) +def test_http_chat_conduct_accepts_advertised_deployment_alias() -> None: + """The listed deployment alias must reach the multi-agent conduct path.""" + server = build_server(build(), port=0, security=SecurityConfig(auth_token=_TEST_AUTH_TOKEN)) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + port = server.server_address[1] + try: + status, body = _post( + port, + { + "model": "contextual-orchestrator", + "messages": [{"role": "user", "content": "analyze and verify this synthetic task"}], + "orchestration_mode": "conduct", + "include_orchestration_trace": True, + }, + ) + assert status == 200, body + assert body["model"] == "contextual-orchestrator" + assert body["orchestration"]["mode"] == "conduct" + assert len(body["orchestration"]["trace"]) > 1 + finally: + server.shutdown() + thread.join(timeout=5) + + def test_http_chat_rejects_invalid_mode() -> None: server = build_server(build(), port=0, security=SecurityConfig(auth_token=_TEST_AUTH_TOKEN)) thread = threading.Thread(target=server.serve_forever, daemon=True) @@ -131,6 +156,7 @@ def test_http_chat_rejects_mode_non_string() -> None: if __name__ == "__main__": test_http_chat_accepts_mode_route() test_http_chat_accepts_orchestration_mode_auto() + test_http_chat_conduct_accepts_advertised_deployment_alias() test_http_chat_rejects_invalid_mode() test_http_chat_rejects_mode_non_string() print("ok") From 3cd539cb44c42546a525b2835ddc839c05493402 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 18:41:00 +0900 Subject: [PATCH 2/2] test: pin deployment alias across text endpoints --- ...st_chat_orchestration_mode_http_honesty.py | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/test_chat_orchestration_mode_http_honesty.py b/tests/test_chat_orchestration_mode_http_honesty.py index 78b89e338..2e4c50661 100644 --- a/tests/test_chat_orchestration_mode_http_honesty.py +++ b/tests/test_chat_orchestration_mode_http_honesty.py @@ -27,9 +27,14 @@ def build() -> TaskOrchestrator: ) -def _post(port: int, payload: dict) -> tuple[int, dict]: +def _post( + port: int, + payload: dict, + *, + endpoint: str = "/v1/chat/completions", +) -> tuple[int, dict]: request = urllib.request.Request( - f"http://127.0.0.1:{port}/v1/chat/completions", + f"http://127.0.0.1:{port}{endpoint}", data=json.dumps(payload).encode("utf-8"), headers={ "content-type": "application/json", @@ -111,6 +116,29 @@ def test_http_chat_conduct_accepts_advertised_deployment_alias() -> None: thread.join(timeout=5) +def test_http_legacy_completions_accepts_advertised_deployment_alias() -> None: + """The provider-neutral deployment alias also serves legacy text completions.""" + server = build_server(build(), port=0, security=SecurityConfig(auth_token=_TEST_AUTH_TOKEN)) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + port = server.server_address[1] + try: + status, body = _post( + port, + { + "model": "contextual-orchestrator", + "prompt": "summarize this synthetic task", + }, + endpoint="/v1/completions", + ) + assert status == 200, body + assert body["object"] == "text_completion" + assert body["model"] == "contextual-orchestrator" + finally: + server.shutdown() + thread.join(timeout=5) + + def test_http_chat_rejects_invalid_mode() -> None: server = build_server(build(), port=0, security=SecurityConfig(auth_token=_TEST_AUTH_TOKEN)) thread = threading.Thread(target=server.serve_forever, daemon=True) @@ -157,6 +185,7 @@ def test_http_chat_rejects_mode_non_string() -> None: test_http_chat_accepts_mode_route() test_http_chat_accepts_orchestration_mode_auto() test_http_chat_conduct_accepts_advertised_deployment_alias() + test_http_legacy_completions_accepts_advertised_deployment_alias() test_http_chat_rejects_invalid_mode() test_http_chat_rejects_mode_non_string() print("ok")