Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.routing import Mount, Route, WebSocketRoute
from llama_stack_client import APIConnectionError

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

await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack)
llama_stack_config = configuration.configuration.llama_stack
await AsyncLlamaStackClientHolder().load(llama_stack_config)
client = AsyncLlamaStackClientHolder().get_client()
# check if the Llama Stack version is supported by the service
await check_llama_stack_version(client)
try:
await check_llama_stack_version(client)
except APIConnectionError as e:
llama_stack_url = llama_stack_config.url
logger.error(
"Failed to connect to Llama Stack at '%s'. "
"Please verify that the 'llama_stack.url' configuration is correct "
"and that the Llama Stack service is running and accessible. "
"Original error: %s",
llama_stack_url,
e,
)
raise

logger.info("Registering MCP servers")
await register_mcp_servers_async(logger, configuration.configuration)
Expand Down
4 changes: 3 additions & 1 deletion src/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ def _load_service_client(self, config: LlamaStackConfiguration) -> None:
"Using timeout of %d seconds for Llama Stack requests", config.timeout
)
api_key = config.api_key.get_secret_value() if config.api_key else None
# Convert AnyHttpUrl to string for the client
base_url = str(config.url) if config.url else None
self._lsc = AsyncLlamaStackClient(
base_url=config.url, api_key=api_key, timeout=config.timeout
base_url=base_url, api_key=api_key, timeout=config.timeout
)

def _enrich_library_config(
Expand Down
5 changes: 3 additions & 2 deletions src/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,10 +533,11 @@ class LlamaStackConfiguration(ConfigurationBase):
- [Build AI Applications with Llama Stack](https://llamastack.github.io/)
"""

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

api_key: Optional[SecretStr] = Field(
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_loading_proper_configuration(configuration_filename: str) -> None:
# check 'llama_stack' section
ls_config = cfg.llama_stack_configuration
assert ls_config.use_as_library_client is False
assert ls_config.url == "http://localhost:8321"
assert str(ls_config.url) == "http://localhost:8321/"
assert ls_config.api_key is not None
assert ls_config.api_key.get_secret_value() == "xyzzy"

Expand Down
10 changes: 5 additions & 5 deletions tests/unit/models/config/test_authentication_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def test_authentication_configuration_in_config_noop() -> None:
llama_stack=LlamaStackConfiguration(
use_as_library_client=True,
library_client_config_path="tests/configuration/run.yaml",
url="localhost",
url="http://localhost",
api_key=SecretStr(""),
timeout=60,
),
Expand Down Expand Up @@ -346,7 +346,7 @@ def test_authentication_configuration_skip_readiness_probe() -> None:
llama_stack=LlamaStackConfiguration(
use_as_library_client=True,
library_client_config_path="tests/configuration/run.yaml",
url="localhost",
url="http://localhost",
api_key=SecretStr(""),
timeout=60,
),
Expand Down Expand Up @@ -393,7 +393,7 @@ def test_authentication_configuration_in_config_k8s() -> None:
llama_stack=LlamaStackConfiguration(
use_as_library_client=True,
library_client_config_path="tests/configuration/run.yaml",
url="localhost",
url="http://localhost",
api_key=SecretStr(""),
timeout=60,
),
Expand Down Expand Up @@ -450,7 +450,7 @@ def test_authentication_configuration_in_config_rh_identity() -> None:
llama_stack=LlamaStackConfiguration(
use_as_library_client=True,
library_client_config_path="tests/configuration/run.yaml",
url="localhost",
url="http://localhost",
api_key=SecretStr(""),
timeout=60,
),
Expand Down Expand Up @@ -497,7 +497,7 @@ def test_authentication_configuration_in_config_jwktoken() -> None:
llama_stack=LlamaStackConfiguration(
use_as_library_client=True,
library_client_config_path="tests/configuration/run.yaml",
url="localhost",
url="http://localhost",
api_key=SecretStr(""),
timeout=60,
),
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/models/config/test_llama_stack_configuration.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Unit tests for LlamaStackConfiguration model."""

import pytest
from pydantic import ValidationError

from utils.checks import InvalidConfigurationError

Expand Down Expand Up @@ -89,3 +90,37 @@ def test_llama_stack_wrong_configuration_no_config_file() -> None:
LlamaStackConfiguration(
use_as_library_client=True
) # pyright: ignore[reportCallIssue]


def test_llama_stack_configuration_valid_http_url() -> None:
"""Test that valid HTTP URLs are accepted."""
config = LlamaStackConfiguration(
url="http://localhost:8321"
) # pyright: ignore[reportCallIssue]
assert config is not None
assert str(config.url) == "http://localhost:8321/"


def test_llama_stack_configuration_valid_https_url() -> None:
"""Test that valid HTTPS URLs are accepted."""
config = LlamaStackConfiguration(
url="https://llama-stack.example.com:8321"
) # pyright: ignore[reportCallIssue]
assert config is not None
assert str(config.url) == "https://llama-stack.example.com:8321/"


def test_llama_stack_configuration_malformed_url_rejected() -> None:
"""Test that malformed URLs are rejected with a ValidationError."""
with pytest.raises(ValidationError, match="Input should be a valid URL"):
LlamaStackConfiguration(
url="not-a-valid-url"
) # pyright: ignore[reportCallIssue]


def test_llama_stack_configuration_invalid_scheme_rejected() -> None:
"""Test that URLs without http/https scheme are rejected."""
with pytest.raises(ValidationError, match="URL scheme should be 'http' or 'https'"):
LlamaStackConfiguration(
url="ftp://localhost:8321"
) # pyright: ignore[reportCallIssue]
2 changes: 1 addition & 1 deletion tests/unit/test_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def test_init_from_dict() -> None:
# check for llama_stack_configuration subsection
assert cfg.llama_stack_configuration.api_key is not None
assert cfg.llama_stack_configuration.api_key.get_secret_value() == "xyzzy"
assert cfg.llama_stack_configuration.url == "http://x.y.com:1234"
assert str(cfg.llama_stack_configuration.url) == "http://x.y.com:1234/"
assert cfg.llama_stack_configuration.use_as_library_client is False

# check for service_configuration subsection
Expand Down
Loading