From 4667cb64582a5b24e7411c84f5c5040cd402d7a2 Mon Sep 17 00:00:00 2001 From: Alexandru Groza Date: Thu, 13 Aug 2026 12:26:40 +0000 Subject: [PATCH] feat: send codedagentsplayground agenthub-config for coded agents Make LlamaIndex and OpenAI-Agents coded agents meter their design-time LLM calls as CodedAgents.Playground, using the shared resolve_coded_agenthub_config() helper from uipath.platform (design-time -> "codedagentsplayground", deployed -> None). - uipath-openai-agents: UiPathChatOpenAI defaults agenthub_config via the helper when the caller does not pass one (existing _build_headers already emits it). - uipath-llamaindex: inject the x-uipath-agenthub-config header into the OpenAI, Bedrock, and Vertex LLM clients' request headers. Adds uipath-platform>=0.2.18 to both packages (the release that adds the helper) and tests. Bumps uipath-openai-agents 0.1.0 -> 0.1.1 and uipath-llamaindex 0.6.0 -> 0.6.1. Co-Authored-By: Claude Opus 4.8 --- packages/uipath-llamaindex/pyproject.toml | 3 +- .../src/uipath_llamaindex/llms/_openai.py | 6 ++++ .../src/uipath_llamaindex/llms/bedrock.py | 6 ++++ .../src/uipath_llamaindex/llms/vertex.py | 5 +++ .../tests/llms/test_openai.py | 32 +++++++++++++++++++ packages/uipath-openai-agents/pyproject.toml | 3 +- .../src/uipath_openai_agents/chat/openai.py | 10 +++++- .../tests/test_chat_openai.py | 32 +++++++++++++++++++ 8 files changed, 94 insertions(+), 3 deletions(-) diff --git a/packages/uipath-llamaindex/pyproject.toml b/packages/uipath-llamaindex/pyproject.toml index 645cefd0..8653ee9e 100644 --- a/packages/uipath-llamaindex/pyproject.toml +++ b/packages/uipath-llamaindex/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-llamaindex" -version = "0.6.0" +version = "0.6.1" description = "Python SDK that enables developers to build and deploy LlamaIndex agents to the UiPath Cloud Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" @@ -12,6 +12,7 @@ dependencies = [ "llama-index-llms-azure-openai>=0.4.2", "openinference-instrumentation-llama-index>=4.3.9", "uipath>=2.13.0, <2.14.0", + "uipath-platform>=0.2.18, <0.3.0", "uipath-runtime>=0.12.1, <0.13.0", ] classifiers = [ diff --git a/packages/uipath-llamaindex/src/uipath_llamaindex/llms/_openai.py b/packages/uipath-llamaindex/src/uipath_llamaindex/llms/_openai.py index 71e841e6..e47f74a0 100644 --- a/packages/uipath-llamaindex/src/uipath_llamaindex/llms/_openai.py +++ b/packages/uipath-llamaindex/src/uipath_llamaindex/llms/_openai.py @@ -6,6 +6,8 @@ from llama_index.core.tools import ToolSelection from llama_index.llms.azure_openai import AzureOpenAI # type: ignore from uipath._utils._ssl_context import get_httpx_client_kwargs +from uipath.platform.common import resolve_coded_agenthub_config +from uipath.platform.constants import HEADER_AGENTHUB_CONFIG from uipath.utils import EndpointManager from .supported_models import OpenAIModel @@ -57,6 +59,10 @@ def __init__( "X-UiPath-LlmGateway-RequestingProduct": "uipath-python-sdk", "X-UiPath-LlmGateway-RequestingFeature": "llama-index-agent", } + # Design-time runs (local / Studio Web / debug) meter as CodedAgents.Playground; + # deployed runs send no header (-> AgentHub.LLM). + if agenthub_config := resolve_coded_agenthub_config(): + default_headers_dict[HEADER_AGENTHUB_CONFIG] = agenthub_config model_value = model.value if isinstance(model, OpenAIModel) else model base_url = os.environ.get("UIPATH_URL", "EMPTY").rstrip("/") diff --git a/packages/uipath-llamaindex/src/uipath_llamaindex/llms/bedrock.py b/packages/uipath-llamaindex/src/uipath_llamaindex/llms/bedrock.py index 48b70af5..328489de 100644 --- a/packages/uipath-llamaindex/src/uipath_llamaindex/llms/bedrock.py +++ b/packages/uipath-llamaindex/src/uipath_llamaindex/llms/bedrock.py @@ -2,6 +2,8 @@ import os from typing import Any, Optional, Sequence +from uipath.platform.common import resolve_coded_agenthub_config +from uipath.platform.constants import HEADER_AGENTHUB_CONFIG from uipath.utils import EndpointManager from .supported_models import BedrockModel @@ -130,6 +132,10 @@ def _modify_request(self, request, **kwargs): "X-UiPath-Streaming-Enabled": streaming, } + # Design-time runs meter as CodedAgents.Playground; deployed -> AgentHub.LLM. + if agenthub_config := resolve_coded_agenthub_config(): + headers[HEADER_AGENTHUB_CONFIG] = agenthub_config + job_key = os.getenv("UIPATH_JOB_KEY") process_key = os.getenv("UIPATH_PROCESS_KEY") if job_key: diff --git a/packages/uipath-llamaindex/src/uipath_llamaindex/llms/vertex.py b/packages/uipath-llamaindex/src/uipath_llamaindex/llms/vertex.py index 58b75fe3..109d78bb 100644 --- a/packages/uipath-llamaindex/src/uipath_llamaindex/llms/vertex.py +++ b/packages/uipath-llamaindex/src/uipath_llamaindex/llms/vertex.py @@ -6,6 +6,8 @@ from llama_index.core.callbacks import CallbackManager from llama_index.core.constants import DEFAULT_NUM_OUTPUTS, DEFAULT_TEMPERATURE from uipath._utils._ssl_context import get_httpx_client_kwargs +from uipath.platform.common import resolve_coded_agenthub_config +from uipath.platform.constants import HEADER_AGENTHUB_CONFIG from uipath.utils import EndpointManager from .supported_models import GeminiModel @@ -67,6 +69,9 @@ def _rewrite_request_for_gateway( headers = dict(request.headers) if is_streaming: headers["X-UiPath-Streaming-Enabled"] = "true" + # Design-time runs meter as CodedAgents.Playground; deployed -> AgentHub.LLM. + if agenthub_config := resolve_coded_agenthub_config(): + headers[HEADER_AGENTHUB_CONFIG] = agenthub_config # Update host header to match the gateway URL gateway_url_parsed = httpx.URL(gateway_url) headers["host"] = gateway_url_parsed.host diff --git a/packages/uipath-llamaindex/tests/llms/test_openai.py b/packages/uipath-llamaindex/tests/llms/test_openai.py index c917ee57..f24d3b8c 100644 --- a/packages/uipath-llamaindex/tests/llms/test_openai.py +++ b/packages/uipath-llamaindex/tests/llms/test_openai.py @@ -69,3 +69,35 @@ def test_init_builds_gateway_endpoint_from_enum_model(monkeypatch): assert llm.azure_endpoint.startswith("https://cloud.uipath.com/org/tenant/") assert "openai" in llm.azure_endpoint assert not llm.azure_endpoint.endswith("/completions") + + +def test_default_headers_include_agenthub_config_at_design_time(monkeypatch, tmp_path): + from uipath.platform.common import UiPathConfig + + monkeypatch.setenv("UIPATH_URL", "https://cloud.uipath.com/org/tenant/") + monkeypatch.setenv("UIPATH_ACCESS_TOKEN", "token") + monkeypatch.delenv("UIPATH_JOB_KEY", raising=False) + monkeypatch.delenv("UIPATH_PROJECT_ID", raising=False) + monkeypatch.chdir(tmp_path) + UiPathConfig.reset() + + llm = UiPathOpenAI(model=OpenAIModel.GPT_4O_2024_08_06) + assert ( + llm.default_headers.get("x-uipath-agenthub-config") == "codedagentsplayground" + ) + UiPathConfig.reset() + + +def test_default_headers_omit_agenthub_config_when_deployed(monkeypatch, tmp_path): + from uipath.platform.common import UiPathConfig + + monkeypatch.setenv("UIPATH_URL", "https://cloud.uipath.com/org/tenant/") + monkeypatch.setenv("UIPATH_ACCESS_TOKEN", "token") + monkeypatch.setenv("UIPATH_JOB_KEY", "deployed-job") + monkeypatch.delenv("UIPATH_PROJECT_ID", raising=False) + monkeypatch.chdir(tmp_path) + UiPathConfig.reset() + + llm = UiPathOpenAI(model=OpenAIModel.GPT_4O_2024_08_06) + assert "x-uipath-agenthub-config" not in (llm.default_headers or {}) + UiPathConfig.reset() diff --git a/packages/uipath-openai-agents/pyproject.toml b/packages/uipath-openai-agents/pyproject.toml index e9cd2bd7..116e78e5 100644 --- a/packages/uipath-openai-agents/pyproject.toml +++ b/packages/uipath-openai-agents/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-openai-agents" -version = "0.1.0" +version = "0.1.1" description = "Python SDK that enables developers to build and deploy OpenAI agents to the UiPath Cloud Platform" readme = "README.md" requires-python = ">=3.11" @@ -10,6 +10,7 @@ dependencies = [ "openai-agents>=0.6.5", "openinference-instrumentation-openai-agents>=1.4.0", "uipath>=2.13.0, <2.14.0", + "uipath-platform>=0.2.18, <0.3.0", "uipath-runtime>=0.12.1, <0.13.0", ] classifiers = [ diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/chat/openai.py b/packages/uipath-openai-agents/src/uipath_openai_agents/chat/openai.py index 181331b2..ed558d29 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/chat/openai.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/chat/openai.py @@ -6,6 +6,7 @@ import httpx from openai import AsyncOpenAI, OpenAI from uipath._utils._ssl_context import get_httpx_client_kwargs +from uipath.platform.common import resolve_coded_agenthub_config from uipath.utils import EndpointManager from .supported_models import OpenAIModels @@ -145,7 +146,14 @@ def __init__( self._model_name = model_name self._api_version = api_version self._vendor = "openai" - self._agenthub_config = agenthub_config + # Default to the coded-agent design-time/deployed marker when the caller + # did not set one, so design-time runs meter as CodedAgents.Playground and + # deployed runs fall back to AgentHub.LLM. + self._agenthub_config = ( + agenthub_config + if agenthub_config is not None + else resolve_coded_agenthub_config() + ) self._byo_connection_id = byo_connection_id self._api_flavor = api_flavor self._extra_headers = extra_headers or {} diff --git a/packages/uipath-openai-agents/tests/test_chat_openai.py b/packages/uipath-openai-agents/tests/test_chat_openai.py index af737787..a81003d0 100644 --- a/packages/uipath-openai-agents/tests/test_chat_openai.py +++ b/packages/uipath-openai-agents/tests/test_chat_openai.py @@ -64,6 +64,38 @@ def test_build_headers_includes_optional_and_respects_overrides( assert headers["X-UiPath-LlmGateway-ApiFlavor"] == "chat-completions" +def test_agenthub_config_defaults_to_coded_playground_at_design_time( + chat_env: None, monkeypatch: pytest.MonkeyPatch, tmp_path +) -> None: + """No explicit agenthub_config + no job key (design-time) -> coded playground marker.""" + from uipath.platform.common import UiPathConfig + + monkeypatch.delenv("UIPATH_JOB_KEY", raising=False) + monkeypatch.chdir(tmp_path) + UiPathConfig.reset() + + client = UiPathChatOpenAI(token="t", org_id="org", tenant_id="tn") + assert ( + client._build_headers()["X-UiPath-AgentHub-Config"] == "codedagentsplayground" + ) + UiPathConfig.reset() + + +def test_agenthub_config_omitted_when_deployed( + chat_env: None, monkeypatch: pytest.MonkeyPatch, tmp_path +) -> None: + """Deployed run (job key, not a studio project, not a debug session) -> no config header.""" + from uipath.platform.common import UiPathConfig + + monkeypatch.setenv("UIPATH_JOB_KEY", "deployed-job") + monkeypatch.chdir(tmp_path) + UiPathConfig.reset() + + client = UiPathChatOpenAI(token="t", org_id="org", tenant_id="tn") + assert "X-UiPath-AgentHub-Config" not in client._build_headers() + UiPathConfig.reset() + + def test_missing_uipath_url_raises_value_error( monkeypatch: pytest.MonkeyPatch, ) -> None: