Skip to content

Commit e30221b

Browse files
authored
Merge pull request #1109 from max-svistunov/lcore-536-missing-errors
LCORE-536 Add error handling for bad llama-stack URLs; no more silent fails
2 parents 6af69e7 + b75a7ac commit e30221b

7 files changed

Lines changed: 64 additions & 12 deletions

File tree

src/app/main.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from fastapi.middleware.cors import CORSMiddleware
99
from fastapi.responses import JSONResponse
1010
from starlette.routing import Mount, Route, WebSocketRoute
11+
from llama_stack_client import APIConnectionError
1112

1213
from authorization.azure_token_manager import AzureEntraIDManager
1314
import metrics
@@ -50,10 +51,23 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
5051
"Token refresh will be retried on next Azure request."
5152
)
5253

53-
await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack)
54+
llama_stack_config = configuration.configuration.llama_stack
55+
await AsyncLlamaStackClientHolder().load(llama_stack_config)
5456
client = AsyncLlamaStackClientHolder().get_client()
5557
# check if the Llama Stack version is supported by the service
56-
await check_llama_stack_version(client)
58+
try:
59+
await check_llama_stack_version(client)
60+
except APIConnectionError as e:
61+
llama_stack_url = llama_stack_config.url
62+
logger.error(
63+
"Failed to connect to Llama Stack at '%s'. "
64+
"Please verify that the 'llama_stack.url' configuration is correct "
65+
"and that the Llama Stack service is running and accessible. "
66+
"Original error: %s",
67+
llama_stack_url,
68+
e,
69+
)
70+
raise
5771

5872
logger.info("Registering MCP servers")
5973
await register_mcp_servers_async(logger, configuration.configuration)

src/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ def _load_service_client(self, config: LlamaStackConfiguration) -> None:
7070
"Using timeout of %d seconds for Llama Stack requests", config.timeout
7171
)
7272
api_key = config.api_key.get_secret_value() if config.api_key else None
73+
# Convert AnyHttpUrl to string for the client
74+
base_url = str(config.url) if config.url else None
7375
self._lsc = AsyncLlamaStackClient(
74-
base_url=config.url, api_key=api_key, timeout=config.timeout
76+
base_url=base_url, api_key=api_key, timeout=config.timeout
7577
)
7678

7779
def _enrich_library_config(

src/models/config.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,10 +533,11 @@ class LlamaStackConfiguration(ConfigurationBase):
533533
- [Build AI Applications with Llama Stack](https://llamastack.github.io/)
534534
"""
535535

536-
url: Optional[str] = Field(
536+
url: Optional[AnyHttpUrl] = Field(
537537
None,
538538
title="Llama Stack URL",
539-
description="URL to Llama Stack service; used when library mode is disabled",
539+
description="URL to Llama Stack service; used when library mode is disabled. "
540+
"Must be a valid HTTP or HTTPS URL.",
540541
)
541542

542543
api_key: Optional[SecretStr] = Field(

tests/integration/test_configuration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_loading_proper_configuration(configuration_filename: str) -> None:
6969
# check 'llama_stack' section
7070
ls_config = cfg.llama_stack_configuration
7171
assert ls_config.use_as_library_client is False
72-
assert ls_config.url == "http://localhost:8321"
72+
assert str(ls_config.url) == "http://localhost:8321/"
7373
assert ls_config.api_key is not None
7474
assert ls_config.api_key.get_secret_value() == "xyzzy"
7575

tests/unit/models/config/test_authentication_configuration.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ def test_authentication_configuration_in_config_noop() -> None:
307307
llama_stack=LlamaStackConfiguration(
308308
use_as_library_client=True,
309309
library_client_config_path="tests/configuration/run.yaml",
310-
url="localhost",
310+
url="http://localhost",
311311
api_key=SecretStr(""),
312312
timeout=60,
313313
),
@@ -346,7 +346,7 @@ def test_authentication_configuration_skip_readiness_probe() -> None:
346346
llama_stack=LlamaStackConfiguration(
347347
use_as_library_client=True,
348348
library_client_config_path="tests/configuration/run.yaml",
349-
url="localhost",
349+
url="http://localhost",
350350
api_key=SecretStr(""),
351351
timeout=60,
352352
),
@@ -393,7 +393,7 @@ def test_authentication_configuration_in_config_k8s() -> None:
393393
llama_stack=LlamaStackConfiguration(
394394
use_as_library_client=True,
395395
library_client_config_path="tests/configuration/run.yaml",
396-
url="localhost",
396+
url="http://localhost",
397397
api_key=SecretStr(""),
398398
timeout=60,
399399
),
@@ -450,7 +450,7 @@ def test_authentication_configuration_in_config_rh_identity() -> None:
450450
llama_stack=LlamaStackConfiguration(
451451
use_as_library_client=True,
452452
library_client_config_path="tests/configuration/run.yaml",
453-
url="localhost",
453+
url="http://localhost",
454454
api_key=SecretStr(""),
455455
timeout=60,
456456
),
@@ -497,7 +497,7 @@ def test_authentication_configuration_in_config_jwktoken() -> None:
497497
llama_stack=LlamaStackConfiguration(
498498
use_as_library_client=True,
499499
library_client_config_path="tests/configuration/run.yaml",
500-
url="localhost",
500+
url="http://localhost",
501501
api_key=SecretStr(""),
502502
timeout=60,
503503
),

tests/unit/models/config/test_llama_stack_configuration.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Unit tests for LlamaStackConfiguration model."""
22

33
import pytest
4+
from pydantic import ValidationError
45

56
from utils.checks import InvalidConfigurationError
67

@@ -89,3 +90,37 @@ def test_llama_stack_wrong_configuration_no_config_file() -> None:
8990
LlamaStackConfiguration(
9091
use_as_library_client=True
9192
) # pyright: ignore[reportCallIssue]
93+
94+
95+
def test_llama_stack_configuration_valid_http_url() -> None:
96+
"""Test that valid HTTP URLs are accepted."""
97+
config = LlamaStackConfiguration(
98+
url="http://localhost:8321"
99+
) # pyright: ignore[reportCallIssue]
100+
assert config is not None
101+
assert str(config.url) == "http://localhost:8321/"
102+
103+
104+
def test_llama_stack_configuration_valid_https_url() -> None:
105+
"""Test that valid HTTPS URLs are accepted."""
106+
config = LlamaStackConfiguration(
107+
url="https://llama-stack.example.com:8321"
108+
) # pyright: ignore[reportCallIssue]
109+
assert config is not None
110+
assert str(config.url) == "https://llama-stack.example.com:8321/"
111+
112+
113+
def test_llama_stack_configuration_malformed_url_rejected() -> None:
114+
"""Test that malformed URLs are rejected with a ValidationError."""
115+
with pytest.raises(ValidationError, match="Input should be a valid URL"):
116+
LlamaStackConfiguration(
117+
url="not-a-valid-url"
118+
) # pyright: ignore[reportCallIssue]
119+
120+
121+
def test_llama_stack_configuration_invalid_scheme_rejected() -> None:
122+
"""Test that URLs without http/https scheme are rejected."""
123+
with pytest.raises(ValidationError, match="URL scheme should be 'http' or 'https'"):
124+
LlamaStackConfiguration(
125+
url="ftp://localhost:8321"
126+
) # pyright: ignore[reportCallIssue]

tests/unit/test_configuration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def test_init_from_dict() -> None:
142142
# check for llama_stack_configuration subsection
143143
assert cfg.llama_stack_configuration.api_key is not None
144144
assert cfg.llama_stack_configuration.api_key.get_secret_value() == "xyzzy"
145-
assert cfg.llama_stack_configuration.url == "http://x.y.com:1234"
145+
assert str(cfg.llama_stack_configuration.url) == "http://x.y.com:1234/"
146146
assert cfg.llama_stack_configuration.use_as_library_client is False
147147

148148
# check for service_configuration subsection

0 commit comments

Comments
 (0)