Skip to content

Commit 19cc14d

Browse files
authored
Merge pull request lightspeed-core#422 from tisnik/lcore-437-check-llama-stack-version-on-startup--
LCORE-437: check Llama Stack version on startup
2 parents ea8141c + c285340 commit 19cc14d

7 files changed

Lines changed: 194 additions & 0 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ The service includes comprehensive user data collection capabilities for various
2727
* [Llama Stack project and configuration](#llama-stack-project-and-configuration)
2828
* [Check connection to Llama Stack](#check-connection-to-llama-stack)
2929
* [Llama Stack as client library](#llama-stack-as-client-library)
30+
* [Llama Stack version check](#llama-stack-version-check)
3031
* [User data collection](#user-data-collection)
3132
* [System prompt](#system-prompt)
3233
* [Safety Shields](#safety-shields)
@@ -243,6 +244,12 @@ user_data_collection:
243244
transcripts_storage: "/tmp/data/transcripts"
244245
```
245246

247+
## Llama Stack version check
248+
249+
During Lightspeed Core Stack service startup, the Llama Stack version is retrieved. The version is tested against two constants `MINIMAL_SUPPORTED_LLAMA_STACK_VERSION` and `MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION` which are defined in `src/constants.py`. If the actual Llama Stack version is outside the range defined by these two constants, the service won't start and administrator will be informed about this problem.
250+
251+
252+
246253
## User data collection
247254

248255
The Lightspeed Core Stack includes comprehensive user data collection capabilities to gather various types of user interaction data for analysis and improvement. This includes feedback, conversation transcripts, and other user interaction data.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies = [
3636
"email-validator>=2.2.0",
3737
"openai==1.99.9",
3838
"sqlalchemy>=2.0.42",
39+
"semver<4.0.0",
3940
]
4041

4142

@@ -91,6 +92,7 @@ dev = [
9192
"build>=1.2.2.post1",
9293
"twine>=6.1.0",
9394
"openapi-to-md>=0.1.0b2",
95+
"pytest-subtests>=0.14.2",
9496
]
9597
llslibdev = [
9698
# To check llama-stack API provider dependecies:

src/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
"""Constants used in business logic."""
22

3+
# Minimal and maximal supported Llama Stack version
4+
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION = "0.2.17"
5+
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION = "0.2.17"
6+
37
UNABLE_TO_PROCESS_RESPONSE = "Unable to process this request"
48

59
# Supported attachment types

src/lightspeed_stack.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from runners.uvicorn import start_uvicorn
1313
from configuration import configuration
1414
from client import AsyncLlamaStackClientHolder
15+
from utils.llama_stack_version import check_llama_stack_version
1516

1617
FORMAT = "%(message)s"
1718
logging.basicConfig(
@@ -66,6 +67,8 @@ def main() -> None:
6667
asyncio.run(
6768
AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack)
6869
)
70+
client = AsyncLlamaStackClientHolder().get_client()
71+
asyncio.run(check_llama_stack_version(client))
6972

7073
if args.dump_configuration:
7174
configuration.configuration.dump()

src/utils/llama_stack_version.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Check if the Llama Stack version is supported by the LCS."""
2+
3+
import logging
4+
5+
from semver import Version
6+
7+
from llama_stack_client._client import AsyncLlamaStackClient
8+
9+
10+
from constants import (
11+
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION,
12+
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION,
13+
)
14+
15+
logger = logging.getLogger("utils.llama_stack_version")
16+
17+
18+
class InvalidLlamaStackVersionException(Exception):
19+
"""Llama Stack version is not valid."""
20+
21+
22+
async def check_llama_stack_version(
23+
client: AsyncLlamaStackClient,
24+
) -> None:
25+
"""Check if the Llama Stack version is supported by the LCS."""
26+
version_info = await client.inspect.version()
27+
compare_versions(
28+
version_info.version,
29+
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION,
30+
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION,
31+
)
32+
33+
34+
def compare_versions(version_info: str, minimal: str, maximal: str) -> None:
35+
"""Compare current Llama Stack version with minimal and maximal allowed versions."""
36+
current_version = Version.parse(version_info)
37+
minimal_version = Version.parse(minimal)
38+
maximal_version = Version.parse(maximal)
39+
logger.debug("Current version: %s", current_version)
40+
logger.debug("Minimal version: %s", minimal_version)
41+
logger.debug("Maximal version: %s", maximal_version)
42+
43+
if current_version < minimal_version:
44+
raise InvalidLlamaStackVersionException(
45+
f"Llama Stack version >= {minimal_version} is required, but {current_version} is used"
46+
)
47+
if current_version > maximal_version:
48+
raise InvalidLlamaStackVersionException(
49+
f"Llama Stack version <= {maximal_version} is required, but {current_version} is used"
50+
)
51+
logger.info("Correct Llama Stack version : %s", current_version)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""Unit tests for utility function to check Llama Stack version."""
2+
3+
import pytest
4+
from semver import Version
5+
6+
from llama_stack_client.types import VersionInfo
7+
8+
from utils.llama_stack_version import (
9+
check_llama_stack_version,
10+
InvalidLlamaStackVersionException,
11+
)
12+
13+
from constants import (
14+
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION,
15+
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION,
16+
)
17+
18+
19+
@pytest.mark.asyncio
20+
async def test_check_llama_stack_version_minimal_supported_version(mocker):
21+
"""Test the check_llama_stack_version function."""
22+
23+
# mock the Llama Stack client
24+
mock_client = mocker.AsyncMock()
25+
mock_client.inspect.version.return_value = VersionInfo(
26+
version=MINIMAL_SUPPORTED_LLAMA_STACK_VERSION
27+
)
28+
29+
# test if the version is checked
30+
await check_llama_stack_version(mock_client)
31+
32+
33+
@pytest.mark.asyncio
34+
async def test_check_llama_stack_version_maximal_supported_version(mocker):
35+
"""Test the check_llama_stack_version function."""
36+
37+
# mock the Llama Stack client
38+
mock_client = mocker.AsyncMock()
39+
mock_client.inspect.version.return_value = VersionInfo(
40+
version=MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION
41+
)
42+
43+
# test if the version is checked
44+
await check_llama_stack_version(mock_client)
45+
46+
47+
@pytest.mark.asyncio
48+
async def test_check_llama_stack_version_too_small_version(mocker):
49+
"""Test the check_llama_stack_version function."""
50+
51+
# mock the Llama Stack client
52+
mock_client = mocker.AsyncMock()
53+
54+
# that is surely out of range
55+
mock_client.inspect.version.return_value = VersionInfo(version="0.0.0")
56+
57+
expected_exception_msg = (
58+
f"Llama Stack version >= {MINIMAL_SUPPORTED_LLAMA_STACK_VERSION} "
59+
+ "is required, but 0.0.0 is used"
60+
)
61+
# test if the version is checked
62+
with pytest.raises(InvalidLlamaStackVersionException, match=expected_exception_msg):
63+
await check_llama_stack_version(mock_client)
64+
65+
66+
async def _check_version_must_fail(mock_client, bigger_version):
67+
mock_client.inspect.version.return_value = VersionInfo(version=str(bigger_version))
68+
69+
expected_exception_msg = (
70+
f"Llama Stack version <= {MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION} is required, "
71+
+ f"but {bigger_version} is used"
72+
)
73+
# test if the version is checked
74+
with pytest.raises(InvalidLlamaStackVersionException, match=expected_exception_msg):
75+
await check_llama_stack_version(mock_client)
76+
77+
78+
@pytest.mark.asyncio
79+
async def test_check_llama_stack_version_too_big_version(mocker, subtests):
80+
"""Test the check_llama_stack_version function."""
81+
82+
# mock the Llama Stack client
83+
mock_client = mocker.AsyncMock()
84+
85+
max_version = Version.parse(MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION)
86+
87+
with subtests.test(msg="Increased patch number"):
88+
bigger_version = max_version.bump_patch()
89+
await _check_version_must_fail(mock_client, bigger_version)
90+
91+
with subtests.test(msg="Increased minor number"):
92+
bigger_version = max_version.bump_minor()
93+
await _check_version_must_fail(mock_client, bigger_version)
94+
95+
with subtests.test(msg="Increased major number"):
96+
bigger_version = max_version.bump_major()
97+
await _check_version_must_fail(mock_client, bigger_version)
98+
99+
with subtests.test(msg="Increased all numbers"):
100+
bigger_version = max_version.bump_major().bump_minor().bump_patch()
101+
await _check_version_must_fail(mock_client, bigger_version)

uv.lock

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)