@@ -480,14 +480,33 @@ class TestPlatformSettings:
480480 def test_build_base_url_passthrough (
481481 self , platform_env_vars , mock_platform_auth , passthrough_api_config
482482 ):
483- """Test build_base_url for passthrough mode."""
483+ """Test build_base_url for passthrough completions mode."""
484484 with patch .dict (os .environ , platform_env_vars , clear = True ):
485485 settings = PlatformSettings ()
486486 url = settings .build_base_url (
487487 model_name = "gpt-4o" ,
488488 api_config = passthrough_api_config ,
489489 )
490- assert "agenthub_/llm/raw/vendor/openai/model/gpt-4o/completions" in url
490+ assert "llm/raw/vendor/openai/model/gpt-4o/completions" in url
491+
492+ def test_build_base_url_passthrough_with_api_version (
493+ self , platform_env_vars , mock_platform_auth
494+ ):
495+ """Test build_base_url for passthrough completions with api_version (vendor endpoint ignores it)."""
496+ api_config = UiPathAPIConfig (
497+ api_type = ApiType .COMPLETIONS ,
498+ routing_mode = RoutingMode .PASSTHROUGH ,
499+ vendor_type = "openai" ,
500+ api_version = "2025-03-01" ,
501+ )
502+ with patch .dict (os .environ , platform_env_vars , clear = True ):
503+ settings = PlatformSettings ()
504+ url = settings .build_base_url (
505+ model_name = "gpt-4o" ,
506+ api_config = api_config ,
507+ )
508+ assert "llm/raw/vendor/openai/model/gpt-4o/completions" in url
509+ assert "api-version" not in url
491510
492511 def test_build_base_url_normalized (
493512 self , platform_env_vars , mock_platform_auth , normalized_api_config
@@ -532,6 +551,152 @@ def test_build_auth_pipeline_returns_auth(self, platform_env_vars, mock_platform
532551 auth = settings .build_auth_pipeline ()
533552 assert isinstance (auth , Auth )
534553
554+ def test_build_auth_pipeline_with_access_token (self , platform_env_vars , mock_platform_auth ):
555+ """Test auth pipeline uses access_token when provided."""
556+ from uipath .llm_client .settings .platform .auth import PlatformAuth
557+
558+ with patch .dict (os .environ , platform_env_vars , clear = True ):
559+ settings = PlatformSettings ()
560+ auth = settings .build_auth_pipeline ()
561+ assert isinstance (auth , PlatformAuth )
562+
563+ def test_build_base_url_passthrough_embeddings (self , platform_env_vars , mock_platform_auth ):
564+ """Test build_base_url for passthrough embeddings with api_version."""
565+ api_config = UiPathAPIConfig (
566+ api_type = ApiType .EMBEDDINGS ,
567+ routing_mode = RoutingMode .PASSTHROUGH ,
568+ vendor_type = "openai" ,
569+ api_version = "2024-02-01" ,
570+ )
571+ with patch .dict (os .environ , platform_env_vars , clear = True ):
572+ settings = PlatformSettings ()
573+ url = settings .build_base_url (
574+ model_name = "text-embedding-3-large" ,
575+ api_config = api_config ,
576+ )
577+ assert "embeddings" in url
578+ assert "text-embedding-3-large" in url
579+ assert "api-version=2024-02-01" in url
580+
581+ def test_build_base_url_passthrough_embeddings_no_api_version (
582+ self , platform_env_vars , mock_platform_auth
583+ ):
584+ """Test build_base_url for passthrough embeddings without api_version."""
585+ api_config = UiPathAPIConfig (
586+ api_type = ApiType .EMBEDDINGS ,
587+ routing_mode = RoutingMode .PASSTHROUGH ,
588+ vendor_type = "openai" ,
589+ )
590+ with patch .dict (os .environ , platform_env_vars , clear = True ):
591+ settings = PlatformSettings ()
592+ url = settings .build_base_url (
593+ model_name = "text-embedding-3-large" ,
594+ api_config = api_config ,
595+ )
596+ assert "embeddings" in url
597+ assert "text-embedding-3-large" in url
598+ assert "api-version" not in url
599+
600+ def test_build_base_url_passthrough_embeddings_non_openai_raises (
601+ self , platform_env_vars , mock_platform_auth
602+ ):
603+ """Test build_base_url raises for non-OpenAI passthrough embeddings."""
604+ api_config = UiPathAPIConfig (
605+ api_type = ApiType .EMBEDDINGS ,
606+ routing_mode = RoutingMode .PASSTHROUGH ,
607+ vendor_type = "vertexai" ,
608+ )
609+ with patch .dict (os .environ , platform_env_vars , clear = True ):
610+ settings = PlatformSettings ()
611+ with pytest .raises (ValueError , match = "only supports OpenAI-compatible models" ):
612+ settings .build_base_url (
613+ model_name = "gemini-embedding-001" ,
614+ api_config = api_config ,
615+ )
616+
617+ def test_build_base_url_normalized_embeddings_raises (
618+ self , platform_env_vars , mock_platform_auth
619+ ):
620+ """Test build_base_url raises ValueError for normalized embeddings."""
621+ normalized_embeddings_config = UiPathAPIConfig (
622+ api_type = ApiType .EMBEDDINGS ,
623+ routing_mode = RoutingMode .NORMALIZED ,
624+ )
625+ with patch .dict (os .environ , platform_env_vars , clear = True ):
626+ settings = PlatformSettings ()
627+ with pytest .raises (ValueError , match = "Normalized embeddings are not supported" ):
628+ settings .build_base_url (
629+ model_name = "text-embedding-3-large" ,
630+ api_config = normalized_embeddings_config ,
631+ )
632+
633+ def test_build_base_url_requires_model_name (
634+ self , platform_env_vars , mock_platform_auth , normalized_api_config
635+ ):
636+ """Test build_base_url asserts model_name is not None."""
637+ with patch .dict (os .environ , platform_env_vars , clear = True ):
638+ settings = PlatformSettings ()
639+ with pytest .raises (AssertionError ):
640+ settings .build_base_url (model_name = None , api_config = normalized_api_config )
641+
642+ def test_build_base_url_requires_api_config (self , platform_env_vars , mock_platform_auth ):
643+ """Test build_base_url asserts api_config is not None."""
644+ with patch .dict (os .environ , platform_env_vars , clear = True ):
645+ settings = PlatformSettings ()
646+ with pytest .raises (AssertionError ):
647+ settings .build_base_url (model_name = "gpt-4o" , api_config = None )
648+
649+ def test_build_auth_headers_empty_when_no_optional (self , platform_env_vars , mock_platform_auth ):
650+ """Test build_auth_headers with no optional tracing fields set."""
651+ env = {** platform_env_vars , "UIPATH_AGENTHUB_CONFIG" : "" }
652+ with patch .dict (os .environ , env , clear = True ):
653+ settings = PlatformSettings ()
654+ # Override to empty to test the falsy path
655+ settings .agenthub_config = ""
656+ settings .process_key = None
657+ settings .job_key = None
658+ headers = settings .build_auth_headers ()
659+ assert headers == {}
660+
661+ def test_validation_requires_all_fields (self , mock_platform_auth ):
662+ """Test validation fails without required fields."""
663+ env = {
664+ "UIPATH_ACCESS_TOKEN" : "test-access-token" ,
665+ # Missing base_url, tenant_id, organization_id
666+ }
667+ with patch .dict (os .environ , env , clear = True ):
668+ with pytest .raises (ValueError , match = "Base URL, access token, tenant ID" ):
669+ PlatformSettings ()
670+
671+ def test_validation_fails_on_expired_token (self ):
672+ """Test validation fails when access token is expired."""
673+ with (
674+ patch (
675+ "uipath.llm_client.settings.platform.settings.is_token_expired" ,
676+ return_value = True ,
677+ ),
678+ patch (
679+ "uipath.llm_client.settings.platform.settings.parse_access_token" ,
680+ return_value = {"client_id" : "test-client-id" },
681+ ),
682+ ):
683+ env = {
684+ "UIPATH_ACCESS_TOKEN" : "test-access-token" ,
685+ "UIPATH_URL" : "https://cloud.uipath.com/org/tenant" ,
686+ "UIPATH_TENANT_ID" : "test-tenant-id" ,
687+ "UIPATH_ORGANIZATION_ID" : "test-org-id" ,
688+ }
689+ with patch .dict (os .environ , env , clear = True ):
690+ with pytest .raises (ValueError , match = "Access token is expired" ):
691+ PlatformSettings ()
692+
693+ def test_validate_byo_model_is_noop (self , platform_env_vars , mock_platform_auth ):
694+ """Test validate_byo_model does nothing (no-op)."""
695+ with patch .dict (os .environ , platform_env_vars , clear = True ):
696+ settings = PlatformSettings ()
697+ result = settings .validate_byo_model ({"modelName" : "custom-model" })
698+ assert result is None
699+
535700
536701# ============================================================================
537702# Test Platform Auth Refresh Logic
0 commit comments