From bfb474c004d932556c340d8009134865dba62520 Mon Sep 17 00:00:00 2001 From: giuliastf Date: Fri, 8 Aug 2025 19:58:23 +0300 Subject: [PATCH 1/2] fix(error_license): var 2 --- src/uipath_llamaindex/llms/_openai_llms.py | 59 ++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src/uipath_llamaindex/llms/_openai_llms.py b/src/uipath_llamaindex/llms/_openai_llms.py index d38fd6f..b1a8607 100644 --- a/src/uipath_llamaindex/llms/_openai_llms.py +++ b/src/uipath_llamaindex/llms/_openai_llms.py @@ -1,9 +1,16 @@ import os from enum import Enum from typing import Any, Union - +import logging +import json from llama_index.llms.azure_openai import AzureOpenAI # type: ignore -from uipath.utils import EndpointManager +from openai import PermissionDeniedError +from uipath._cli._runtime._contracts import UiPathErrorCategory + +from .._cli._runtime._exception import UiPathLlamaIndexRuntimeError +from llama_index.core.base.llms.types import CompletionResponse + +logger = logging.getLogger(__name__) class OpenAIModel(Enum): @@ -43,11 +50,57 @@ def __init__( defaults = { "model": model_value, "deployment_name": model_value, - "azure_endpoint": f"{base_url}/{EndpointManager.get_passthrough_endpoint().format(model=model, api_version=api_version)}", + # "azure_endpoint": f"{base_url}/{EndpointManager.get_passthrough_endpoint().format(model=model, api_version=api_version)}", + "azure_endpoint": f"{base_url}/llm/openai/deployments/{model_value}/chat/completions?api-version={api_version}", "api_key": os.environ.get("UIPATH_ACCESS_TOKEN"), "api_version": api_version, "is_chat_model": True, "default_headers": default_headers_dict, } + print("endpoint", defaults["azure_endpoint"]) final_kwargs = {**defaults, **kwargs} super().__init__(**final_kwargs) + + def _is_license_error(self, e: PermissionDeniedError) -> bool: + """Check if the error is a license-related 403 error.""" + if e.status_code == 403 and e.response: + try: + response_body = e.response.json() + if isinstance(response_body, dict): + title = response_body.get("title", "").lower() + return title == "license not available" + except Exception: + pass + return False + + def _create_license_fallback(self) -> CompletionResponse: + """Create a fallback response for license errors.""" + default_message = "I apologize, but I'm currently unable to process your request due to licensing limitations. Please contact your UiPath administrator to configure LLM licensing for this Agent Hub instance." + + return CompletionResponse( + text=default_message, + raw={"id": "license-fallback", "object": "text_completion", "model": self.model} + ) + + async def acomplete(self, prompt, **kwargs): + """Override acomplete to handle license errors universally.""" + try: + return await super().acomplete(prompt, **kwargs) + except PermissionDeniedError as e: + if self._is_license_error(e): + logger.warning("UiPath Agent Hub license not available - returning fallback response") + return self._create_license_fallback() + raise + + async def _achat(self, messages, **kwargs): + try: + return await super()._achat(messages, **kwargs) + except PermissionDeniedError as e: + if self._is_license_error(e): + raise UiPathLlamaIndexRuntimeError( + code="LICENSE_NOT_AVAILABLE", + title="License Not Available", + detail="License not available for LLM usage", + category=UiPathErrorCategory.DEPLOYMENT, + ) from e + raise \ No newline at end of file From 813166be10222f80bc63eb26c8d45adaa236f6b7 Mon Sep 17 00:00:00 2001 From: giuliastf Date: Fri, 8 Aug 2025 20:03:07 +0300 Subject: [PATCH 2/2] fix(error_license): added helper func for license error --- pyproject.toml | 2 +- src/uipath_llamaindex/llms/_openai_llms.py | 62 +++++++++------------- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2729a5c..4021cb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-llamaindex" -version = "0.0.32" +version = "0.0.33" description = "UiPath LlamaIndex SDK" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" diff --git a/src/uipath_llamaindex/llms/_openai_llms.py b/src/uipath_llamaindex/llms/_openai_llms.py index b1a8607..ed9a1ae 100644 --- a/src/uipath_llamaindex/llms/_openai_llms.py +++ b/src/uipath_llamaindex/llms/_openai_llms.py @@ -1,14 +1,14 @@ +import logging import os from enum import Enum from typing import Any, Union -import logging -import json + from llama_index.llms.azure_openai import AzureOpenAI # type: ignore from openai import PermissionDeniedError from uipath._cli._runtime._contracts import UiPathErrorCategory +from uipath.utils import EndpointManager from .._cli._runtime._exception import UiPathLlamaIndexRuntimeError -from llama_index.core.base.llms.types import CompletionResponse logger = logging.getLogger(__name__) @@ -25,7 +25,6 @@ class OpenAIModel(Enum): TEXT_DAVINCI_003 = "text-davinci-003" -# Define your custom AzureOpenAI class with default settings class UiPathOpenAI(AzureOpenAI): def __init__( self, @@ -50,57 +49,44 @@ def __init__( defaults = { "model": model_value, "deployment_name": model_value, - # "azure_endpoint": f"{base_url}/{EndpointManager.get_passthrough_endpoint().format(model=model, api_version=api_version)}", - "azure_endpoint": f"{base_url}/llm/openai/deployments/{model_value}/chat/completions?api-version={api_version}", + "azure_endpoint": f"{base_url}/{EndpointManager.get_passthrough_endpoint().format(model=model, api_version=api_version)}", "api_key": os.environ.get("UIPATH_ACCESS_TOKEN"), "api_version": api_version, "is_chat_model": True, "default_headers": default_headers_dict, } - print("endpoint", defaults["azure_endpoint"]) final_kwargs = {**defaults, **kwargs} super().__init__(**final_kwargs) - def _is_license_error(self, e: PermissionDeniedError) -> bool: - """Check if the error is a license-related 403 error.""" + def _handle_permission_denied_error(self, e: PermissionDeniedError) -> None: + """Handle PermissionDeniedError and convert license errors to UiPathLlamaIndexRuntimeError.""" if e.status_code == 403 and e.response: try: response_body = e.response.json() if isinstance(response_body, dict): title = response_body.get("title", "").lower() - return title == "license not available" - except Exception: - pass - return False + if title == "license not available": + raise UiPathLlamaIndexRuntimeError( + code="LICENSE_NOT_AVAILABLE", + title=response_body.get("title", "License Not Available"), + detail=response_body.get( + "detail", "License not available for LLM usage" + ), + category=UiPathErrorCategory.DEPLOYMENT, + ) from e + except Exception as parse_error: + logger.warning(f"Failed to parse 403 response JSON: {parse_error}") - def _create_license_fallback(self) -> CompletionResponse: - """Create a fallback response for license errors.""" - default_message = "I apologize, but I'm currently unable to process your request due to licensing limitations. Please contact your UiPath administrator to configure LLM licensing for this Agent Hub instance." - - return CompletionResponse( - text=default_message, - raw={"id": "license-fallback", "object": "text_completion", "model": self.model} - ) + raise e - async def acomplete(self, prompt, **kwargs): - """Override acomplete to handle license errors universally.""" + async def _achat(self, messages, **kwargs): try: - return await super().acomplete(prompt, **kwargs) + return await super()._achat(messages, **kwargs) except PermissionDeniedError as e: - if self._is_license_error(e): - logger.warning("UiPath Agent Hub license not available - returning fallback response") - return self._create_license_fallback() - raise + self._handle_permission_denied_error(e) - async def _achat(self, messages, **kwargs): + def _chat(self, messages, **kwargs): try: - return await super()._achat(messages, **kwargs) + return super()._chat(messages, **kwargs) except PermissionDeniedError as e: - if self._is_license_error(e): - raise UiPathLlamaIndexRuntimeError( - code="LICENSE_NOT_AVAILABLE", - title="License Not Available", - detail="License not available for LLM usage", - category=UiPathErrorCategory.DEPLOYMENT, - ) from e - raise \ No newline at end of file + self._handle_permission_denied_error(e) \ No newline at end of file