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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The service includes comprehensive user data collection capabilities for various
* [Llama Stack project and configuration](#llama-stack-project-and-configuration)
* [Check connection to Llama Stack](#check-connection-to-llama-stack)
* [Llama Stack as client library](#llama-stack-as-client-library)
* [Llama Stack version check](#llama-stack-version-check)
* [User data collection](#user-data-collection)
* [System prompt](#system-prompt)
* [Safety Shields](#safety-shields)
Expand Down Expand Up @@ -243,6 +244,12 @@ user_data_collection:
transcripts_storage: "/tmp/data/transcripts"
```

## Llama Stack version check

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.


Comment on lines +247 to +251
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Documented version check is good; update earlier example to avoid guaranteed startup failure

The new section clearly explains the startup guard. However, the earlier “Llama Stack project and configuration” example pins llama-stack==0.2.14. With MIN/MAX set to 0.2.17, following that example will make lightspeed-core refuse to start. Please update the example to 0.2.17 (or expand the allowed range in constants if you intend broader compatibility).

Suggested change to the example block (shown here for clarity):

# In the earlier example section
dependencies = [
-    "llama-stack==0.2.14",
+    "llama-stack==0.2.17",
     "fastapi>=0.115.12",
     # ...
]
🤖 Prompt for AI Agents
In README.md around lines 247 to 251, the earlier "Llama Stack project and
configuration" example pins llama-stack to 0.2.14 which will conflict with the
documented MINIMAL_SUPPORTED_LLAMA_STACK_VERSION (0.2.17) and cause startup
failure; update that example to use "llama-stack==0.2.17" (or alternatively
broaden the MIN/MAX constants in src/constants.py if wider compatibility is
intended) so the example and the version guard are consistent.


## User data collection

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.
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies = [
"email-validator>=2.2.0",
"openai==1.99.9",
"sqlalchemy>=2.0.42",
"semver<4.0.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Pin semver to >=3,<4 to avoid runtime ImportError

utils/llama_stack_version.py imports from semver import Version, which is only available in semver v3+. With the current constraint semver<4.0.0, environments can resolve to v2.x, causing ImportError at startup/tests.

Apply this diff:

-    "semver<4.0.0",
+    "semver>=3,<4.0.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"semver<4.0.0",
"semver>=3,<4.0.0",
🤖 Prompt for AI Agents
In pyproject.toml around line 39, the dependency constraint "semver<4.0.0" is
too loose and can resolve to semver v2.x which lacks `Version`; change the
constraint to "semver>=3,<4" to ensure semver v3+ is installed; after updating
pyproject.toml, regenerate/update your lock file (e.g., poetry.lock or
equivalent) and run tests/startup to verify the ImportError is resolved.

]


Expand Down Expand Up @@ -91,6 +92,7 @@ dev = [
"build>=1.2.2.post1",
"twine>=6.1.0",
"openapi-to-md>=0.1.0b2",
"pytest-subtests>=0.14.2",
]
llslibdev = [
# To check llama-stack API provider dependecies:
Expand Down
4 changes: 4 additions & 0 deletions src/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"""Constants used in business logic."""

# Minimal and maximal supported Llama Stack version
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION = "0.2.17"
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION = "0.2.17"

UNABLE_TO_PROCESS_RESPONSE = "Unable to process this request"

# Supported attachment types
Expand Down
3 changes: 3 additions & 0 deletions src/lightspeed_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from runners.uvicorn import start_uvicorn
from configuration import configuration
from client import AsyncLlamaStackClientHolder
from utils.llama_stack_version import check_llama_stack_version

FORMAT = "%(message)s"
logging.basicConfig(
Expand Down Expand Up @@ -66,6 +67,8 @@ def main() -> None:
asyncio.run(
AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack)
)
client = AsyncLlamaStackClientHolder().get_client()
asyncio.run(check_llama_stack_version(client))

if args.dump_configuration:
configuration.configuration.dump()
Expand Down
51 changes: 51 additions & 0 deletions src/utils/llama_stack_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Check if the Llama Stack version is supported by the LCS."""

import logging

from semver import Version

from llama_stack_client._client import AsyncLlamaStackClient


from constants import (
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION,
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION,
)

logger = logging.getLogger("utils.llama_stack_version")


class InvalidLlamaStackVersionException(Exception):
"""Llama Stack version is not valid."""


async def check_llama_stack_version(
client: AsyncLlamaStackClient,
) -> None:
"""Check if the Llama Stack version is supported by the LCS."""
version_info = await client.inspect.version()
compare_versions(
version_info.version,
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION,
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION,
)


def compare_versions(version_info: str, minimal: str, maximal: str) -> None:
"""Compare current Llama Stack version with minimal and maximal allowed versions."""
current_version = Version.parse(version_info)
minimal_version = Version.parse(minimal)
maximal_version = Version.parse(maximal)
logger.debug("Current version: %s", current_version)
logger.debug("Minimal version: %s", minimal_version)
logger.debug("Maximal version: %s", maximal_version)

if current_version < minimal_version:
raise InvalidLlamaStackVersionException(
f"Llama Stack version >= {minimal_version} is required, but {current_version} is used"
)
if current_version > maximal_version:
raise InvalidLlamaStackVersionException(
f"Llama Stack version <= {maximal_version} is required, but {current_version} is used"
)
logger.info("Correct Llama Stack version : %s", current_version)
101 changes: 101 additions & 0 deletions tests/unit/utils/test_llama_stack_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""Unit tests for utility function to check Llama Stack version."""

import pytest
from semver import Version
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

semver API mismatch risk: Version is only guaranteed in semver>=3

Using from semver import Version assumes semver v3+. The PR pins semver<4.0.0, which allows v2.x to be resolved in some environments, breaking the import. Make the tests resilient across 2.x/3.x or bump the lower bound.

Two fix options (pick one):

  1. Make tests compatible with both semver 2.x and 3.x by falling back to VersionInfo:
-from semver import Version
+try:
+    # semver >= 3
+    from semver import Version
+except ImportError:  # semver < 3
+    from semver import VersionInfo as Version
  1. Alternatively, tighten dependency lower bound in pyproject.toml:
# pyproject.toml
semver = ">=3.0.0,<4.0.0"

Run this to confirm the declared semver constraint and catch any 2.x resolution:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Semver dependency line(s):"
rg -n -C1 -i '^\s*semver\s*=' pyproject.toml || true

Length of output: 136


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) All occurrences of 'semver' in the repo:"
rg -n "semver" || true

echo
echo "2) 'semver =' in TOML files:"
rg -nP "semver\s*=" --glob "*.toml" || true

echo
echo "3) 'semver' in setup.py / setup.cfg:"
grep -R "semver" setup.py setup.cfg || true

echo
echo "4) 'semver' in Pipfile / requirements*.txt:"
grep -R "semver" Pipfile requirements*.txt || true

Length of output: 1648


Tighten semver lower bound to ≥3.0.0

The project currently pins only semver<4.0.0 (pyproject.toml line 39), which allows semver 2.x—where Version doesn’t exist. Both runtime (src/utils/llama_stack_version.py:5) and tests (tests/unit/utils/test_llama_stack_version.py:4) import Version, so installing semver 2.x will break.

Please update your dependency declaration:

pyproject.toml (inside [project] or [tool.poetry.dependencies]):

- "semver<4.0.0",
+ "semver>=3.0.0,<4.0.0",

If you really need to support semver 2.x, apply a fallback import in both code and tests:

-from semver import Version
+try:
+    from semver import Version
+except ImportError:
+    from semver import VersionInfo as Version
🤖 Prompt for AI Agents
In tests/unit/utils/test_llama_stack_version.py around line 4, the test imports
Version from semver but the project allows semver 2.x (which lacks Version);
update pyproject.toml to require semver>=3.0.0,<4.0.0 to ensure Version exists,
and/or add a defensive fallback import in both src/utils/llama_stack_version.py
and this test so they first try to import Version and, if not present, import
the compatible type or construct from the older semver API; make the dependency
change the primary fix and add the fallback only if you must support semver 2.x.


from llama_stack_client.types import VersionInfo

from utils.llama_stack_version import (
check_llama_stack_version,
InvalidLlamaStackVersionException,
)

from constants import (
MINIMAL_SUPPORTED_LLAMA_STACK_VERSION,
MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION,
)


@pytest.mark.asyncio
async def test_check_llama_stack_version_minimal_supported_version(mocker):
"""Test the check_llama_stack_version function."""

# mock the Llama Stack client
mock_client = mocker.AsyncMock()
mock_client.inspect.version.return_value = VersionInfo(
version=MINIMAL_SUPPORTED_LLAMA_STACK_VERSION
)

# test if the version is checked
await check_llama_stack_version(mock_client)


@pytest.mark.asyncio
async def test_check_llama_stack_version_maximal_supported_version(mocker):
"""Test the check_llama_stack_version function."""

# mock the Llama Stack client
mock_client = mocker.AsyncMock()
mock_client.inspect.version.return_value = VersionInfo(
version=MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION
)

# test if the version is checked
await check_llama_stack_version(mock_client)


@pytest.mark.asyncio
async def test_check_llama_stack_version_too_small_version(mocker):
"""Test the check_llama_stack_version function."""

# mock the Llama Stack client
mock_client = mocker.AsyncMock()

# that is surely out of range
mock_client.inspect.version.return_value = VersionInfo(version="0.0.0")

expected_exception_msg = (
f"Llama Stack version >= {MINIMAL_SUPPORTED_LLAMA_STACK_VERSION} "
+ "is required, but 0.0.0 is used"
)
# test if the version is checked
with pytest.raises(InvalidLlamaStackVersionException, match=expected_exception_msg):
await check_llama_stack_version(mock_client)


async def _check_version_must_fail(mock_client, bigger_version):
mock_client.inspect.version.return_value = VersionInfo(version=str(bigger_version))

expected_exception_msg = (
f"Llama Stack version <= {MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION} is required, "
+ f"but {bigger_version} is used"
)
# test if the version is checked
with pytest.raises(InvalidLlamaStackVersionException, match=expected_exception_msg):
await check_llama_stack_version(mock_client)


@pytest.mark.asyncio
async def test_check_llama_stack_version_too_big_version(mocker, subtests):
"""Test the check_llama_stack_version function."""

# mock the Llama Stack client
mock_client = mocker.AsyncMock()

max_version = Version.parse(MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION)

with subtests.test(msg="Increased patch number"):
bigger_version = max_version.bump_patch()
await _check_version_must_fail(mock_client, bigger_version)

with subtests.test(msg="Increased minor number"):
bigger_version = max_version.bump_minor()
await _check_version_must_fail(mock_client, bigger_version)

with subtests.test(msg="Increased major number"):
bigger_version = max_version.bump_major()
await _check_version_must_fail(mock_client, bigger_version)

with subtests.test(msg="Increased all numbers"):
bigger_version = max_version.bump_major().bump_minor().bump_patch()
await _check_version_must_fail(mock_client, bigger_version)
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.