Skip to content

Conversation

are-ces
Copy link
Contributor

@are-ces are-ces commented Oct 1, 2025

Description

When running Uvicorn with workers > 1, LCS did not work properly because each worker is a separate process, and global variables are not shared across processes.

To fix this, the code was changed to initialize necessary resources within each worker process, rather than relying on global variables.

Note: To load the configuration across processes, an environment variable was added to track the location of the config file.

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement

Related Tickets & Documents

  • Related Issue # LCORE-455
  • Closes # LCORE-455

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  • Please provide detailed steps to perform tests related to this code change.
    Change the lightspeed_stack.yaml config to increase number of workers from 1 to any number >1. Then stress test an endpoint which uses llama-stack (/query, /streaming_query)
  • How were the fix/results from this change verified? Please provide relevant screenshots or results.
    LCS was configured to have 2 / 4 workers and I tested /query and /streaming_query endpoints. A stress test would need to be performed.

Summary by CodeRabbit

  • Refactor

    • Standardized authentication handling across all endpoints for consistent, per-request resolution.
    • Migrated app startup to a FastAPI lifespan flow for smoother initialization of configuration, services, and database.
  • Bug Fixes

    • Improved reliability of configuration loading, including a safe fallback via environment settings.
    • Enhanced startup robustness across worker processes by deferring config load to each process.
  • Chores

    • Minor import and dependency cleanup across endpoints with no change to API behavior or responses.

Copy link
Contributor

coderabbitai bot commented Oct 1, 2025

Walkthrough

This PR replaces module-level auth dependency variables with inline Depends(get_auth_dependency()) across many FastAPI endpoints, adjusts related imports, and introduces lazy-loading of authentication configuration in authentication/init.py. It also refactors application startup to use FastAPI’s lifespan context and updates the CLI to set a config path environment variable for worker-local config loading.

Changes

Cohort / File(s) Summary of changes
Endpoint auth DI refactor
src/app/endpoints/authorized.py, src/app/endpoints/config.py, src/app/endpoints/conversations.py, src/app/endpoints/conversations_v2.py, src/app/endpoints/health.py, src/app/endpoints/info.py, src/app/endpoints/metrics.py, src/app/endpoints/models.py, src/app/endpoints/query.py, src/app/endpoints/root.py, src/app/endpoints/streaming_query.py
Replace Depends(auth_dependency) with Depends(get_auth_dependency()); remove module-level auth_dependency where present; minor import/order updates; no endpoint path/behavior changes beyond DI resolution.
Feedback endpoint tweaks
src/app/endpoints/feedback.py
Switch to Depends(get_auth_dependency()); add ForbiddenResponse (and json/datetime import adjustments); update response imports.
Startup lifecycle overhaul
src/app/main.py
Add lifespan async context; move startup logic (config load, Llama client/version check, MCP registration, DB init) into lifespan; wire lifespan into FastAPI app; reorganize imports.
Auth config lazy-load
src/authentication/__init__.py
Update get_auth_dependency to guard access to configuration; on missing config, load from LIGHTSPEED_STACK_CONFIG_PATH (default tests path) then proceed; reorder/expand imports; no signature changes.
CLI run changes
src/lightspeed_stack.py
Set os.environ["LIGHTSPEED_STACK_CONFIG_PATH"] = args.config_file; remove client creation/version check steps; retain start_uvicorn entry; imports reordered.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant FastAPI App
  participant Lifespan as Lifespan Context
  participant Config
  participant Llama as Llama Stack Client
  participant MCP as MCP Registry
  participant DB

  User->>FastAPI App: Start process
  activate FastAPI App
  FastAPI App->>Lifespan: enter()
  activate Lifespan
  Lifespan->>Config: Load configuration
  Lifespan->>Llama: Initialize / check version
  Lifespan->>MCP: Register servers
  Lifespan->>DB: Initialize and create tables
  Lifespan-->>FastAPI App: ready
  deactivate Lifespan
  note over FastAPI App: App serves requests
Loading
sequenceDiagram
  autonumber
  participant Client
  participant Endpoint
  participant DI as Depends(get_auth_dependency())
  participant Auth as authentication.get_auth_dependency
  participant Cfg as configuration
  participant FS as File Loader

  Client->>Endpoint: HTTP request
  Endpoint->>DI: Resolve dependency
  DI->>Auth: get_auth_dependency()
  Auth->>Cfg: Read auth module
  alt Config not loaded (LogicError)
    Auth->>FS: Load from LIGHTSPEED_STACK_CONFIG_PATH
    FS-->>Cfg: Configuration loaded
    Auth->>Cfg: Re-read auth module
  end
  Auth-->>DI: Auth dependency
  DI-->>Endpoint: Auth tuple
  Endpoint-->>Client: Response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • manstis
  • tisnik

Poem

A hop, a skip, config now in sight,
I twirl my ears at startup’s light.
Per-request auth, no globals to keep,
Lifespan wakes while workers leap.
Env-path crumbs guide every node—
Thump! The stack is good to load. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the primary change of removing global variables to enable compatibility with Uvicorn servers running multiple workers, directly reflecting the main fix outlined in the pull request objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@are-ces are-ces marked this pull request as ready for review October 1, 2025 12:54
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/app/endpoints/models.py (1)

77-79: Avoid logging secrets; redact Llama Stack configuration.

Logging the entire configuration at INFO risks leaking API keys and sensitive data. Log only non-secret fields (e.g., url, use_as_library_client) and move to DEBUG if needed.

-logger.info("Llama stack config: %s", llama_stack_configuration)
+logger.info(
+    "Llama stack configured: url=%s, library_client=%s",
+    getattr(llama_stack_configuration, "url", "<unset>"),
+    getattr(llama_stack_configuration, "use_as_library_client", "<unset>"),
+)
src/app/endpoints/query.py (3)

199-201: Avoid logging secrets; redact Llama Stack configuration.

Same concern as in models.py. Do not print the full configuration; log only safe fields.

-logger.info("Llama stack config: %s", configuration.llama_stack_configuration)
+safe_cfg = configuration.llama_stack_configuration
+logger.info(
+    "Llama stack configured: url=%s, library_client=%s",
+    getattr(safe_cfg, "url", "<unset>"),
+    getattr(safe_cfg, "use_as_library_client", "<unset>"),
+)

317-321: Bug: constructing AnyUrl at runtime will raise. Pass a str and let Pydantic validate.

pydantic.AnyUrl is a type, not a runtime constructor. AnyUrl(chunk.source) will error.

-                        doc_url=(
-                            AnyUrl(chunk.source)
-                            if chunk.source.startswith("http")
-                            else None
-                        ),
+                        doc_url=chunk.source if chunk.source.startswith("http") else None,

309-334: Referenced documents computed but not used (shadowing bug).

You build referenced_docs from rag_chunks but return the earlier referenced_documents from retrieve_response. Use the newly built list when present.

-        referenced_docs = []
+        referenced_docs = []
         doc_sources = set()
         for chunk in summary.rag_chunks:
             if chunk.source and chunk.source not in doc_sources:
                 doc_sources.add(chunk.source)
                 referenced_docs.append(
                     ReferencedDocument(
-                        doc_url=(
-                            AnyUrl(chunk.source)
-                            if chunk.source.startswith("http")
-                            else None
-                        ),
+                        doc_url=chunk.source if chunk.source.startswith("http") else None,
                         doc_title=chunk.source,
                     )
                 )
 
         logger.info("Building final response...")
+        # Prefer chunk-derived docs when available, otherwise keep those parsed from turn steps
+        referenced_documents = referenced_docs or referenced_documents
         response = QueryResponse(
             conversation_id=conversation_id,
             response=summary.llm_response,
             rag_chunks=summary.rag_chunks if summary.rag_chunks else [],
             tool_calls=tool_calls if tool_calls else None,
-            referenced_documents=referenced_documents,
+            referenced_documents=referenced_documents,
         )
src/app/main.py (2)

55-73: Fix app metadata init to avoid pre-load config access; update after startup.

Initialize with placeholders, then set title/summary/description inside lifespan after configuration is loaded.

-app = FastAPI(
-    title=f"{service_name} service - OpenAPI",
-    summary=f"{service_name} service API specification.",
-    description=f"{service_name} service API specification.",
+app = FastAPI(
+    title="Lightspeed service - OpenAPI",
+    summary="Lightspeed service API specification.",
+    description="Lightspeed service API specification.",
     version=version.__version__,
@@
-    lifespan=lifespan,
+    lifespan=lifespan,
 )

And inside lifespan, right after loading configuration:

@@
-    configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"])
+    # Load configuration and update app metadata
+    configuration.load_configuration(config_path)
+    service_name = configuration.configuration.name
+    _app.title = f"{service_name} service - OpenAPI"
+    _app.summary = f"{service_name} service API specification."
+    _app.description = f"{service_name} service API specification."

86-88: Middleware decorator must use 'http'.

@app.middleware("") is invalid; use "http" so the middleware registers.

-@app.middleware("")
+@app.middleware("http")
 async def rest_api_metrics(
🧹 Nitpick comments (3)
src/app/endpoints/models.py (2)

7-7: Use canonical FastAPI import for Depends.

Import Depends from fastapi, not fastapi.params, to match project convention and avoid confusion. As per coding guidelines.

-from fastapi.params import Depends
+from fastapi import Depends

50-51: Add response_model for better schema guarantees.

Expose the success schema via response_model to enforce and document the response type.

-@router.get("/models", responses=models_responses)
+@router.get("/models", response_model=ModelsResponse, responses=models_responses)
src/app/endpoints/query.py (1)

171-173: Add response_model to document and enforce the success payload.

This improves OpenAPI and client generation.

-@router.post("/query", responses=query_response)
+@router.post("/query", response_model=QueryResponse, responses=query_response)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98430bb and e971aab.

📒 Files selected for processing (15)
  • src/app/endpoints/authorized.py (2 hunks)
  • src/app/endpoints/config.py (2 hunks)
  • src/app/endpoints/conversations.py (4 hunks)
  • src/app/endpoints/conversations_v2.py (4 hunks)
  • src/app/endpoints/feedback.py (4 hunks)
  • src/app/endpoints/health.py (3 hunks)
  • src/app/endpoints/info.py (2 hunks)
  • src/app/endpoints/metrics.py (1 hunks)
  • src/app/endpoints/models.py (2 hunks)
  • src/app/endpoints/query.py (1 hunks)
  • src/app/endpoints/root.py (2 hunks)
  • src/app/endpoints/streaming_query.py (1 hunks)
  • src/app/main.py (2 hunks)
  • src/authentication/__init__.py (2 hunks)
  • src/lightspeed_stack.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use absolute imports for internal modules (e.g., from auth import get_auth_dependency)

Files:

  • src/app/endpoints/query.py
  • src/app/endpoints/metrics.py
  • src/app/endpoints/streaming_query.py
  • src/app/endpoints/conversations_v2.py
  • src/lightspeed_stack.py
  • src/app/main.py
  • src/app/endpoints/authorized.py
  • src/app/endpoints/conversations.py
  • src/authentication/__init__.py
  • src/app/endpoints/health.py
  • src/app/endpoints/feedback.py
  • src/app/endpoints/models.py
  • src/app/endpoints/config.py
  • src/app/endpoints/info.py
  • src/app/endpoints/root.py
src/app/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use standard FastAPI imports (from fastapi import APIRouter, HTTPException, Request, status, Depends) in FastAPI app code

Files:

  • src/app/endpoints/query.py
  • src/app/endpoints/metrics.py
  • src/app/endpoints/streaming_query.py
  • src/app/endpoints/conversations_v2.py
  • src/app/main.py
  • src/app/endpoints/authorized.py
  • src/app/endpoints/conversations.py
  • src/app/endpoints/health.py
  • src/app/endpoints/feedback.py
  • src/app/endpoints/models.py
  • src/app/endpoints/config.py
  • src/app/endpoints/info.py
  • src/app/endpoints/root.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: All modules start with descriptive module-level docstrings explaining purpose
Use logger = logging.getLogger(name) for module logging after import logging
Define type aliases at module level for clarity
All functions require docstrings with brief descriptions
Provide complete type annotations for all function parameters and return types
Use typing_extensions.Self in model validators where appropriate
Use modern union syntax (str | int) and Optional[T] or T | None consistently
Function names use snake_case with descriptive, action-oriented prefixes (get_, validate_, check_)
Avoid in-place parameter modification; return new data structures instead of mutating arguments
Use appropriate logging levels: debug, info, warning, error with clear messages
All classes require descriptive docstrings explaining purpose
Class names use PascalCase with conventional suffixes (Configuration, Error/Exception, Resolver, Interface)
Abstract base classes should use abc.ABC and @AbstractMethod for interfaces
Provide complete type annotations for all class attributes
Follow Google Python docstring style for modules, classes, and functions, including Args, Returns, Raises, Attributes sections as needed

Files:

  • src/app/endpoints/query.py
  • src/app/endpoints/metrics.py
  • src/app/endpoints/streaming_query.py
  • src/app/endpoints/conversations_v2.py
  • src/lightspeed_stack.py
  • src/app/main.py
  • src/app/endpoints/authorized.py
  • src/app/endpoints/conversations.py
  • src/authentication/__init__.py
  • src/app/endpoints/health.py
  • src/app/endpoints/feedback.py
  • src/app/endpoints/models.py
  • src/app/endpoints/config.py
  • src/app/endpoints/info.py
  • src/app/endpoints/root.py
src/{app/**/*.py,client.py}

📄 CodeRabbit inference engine (CLAUDE.md)

Use async def for I/O-bound operations and external API calls

Files:

  • src/app/endpoints/query.py
  • src/app/endpoints/metrics.py
  • src/app/endpoints/streaming_query.py
  • src/app/endpoints/conversations_v2.py
  • src/app/main.py
  • src/app/endpoints/authorized.py
  • src/app/endpoints/conversations.py
  • src/app/endpoints/health.py
  • src/app/endpoints/feedback.py
  • src/app/endpoints/models.py
  • src/app/endpoints/config.py
  • src/app/endpoints/info.py
  • src/app/endpoints/root.py
src/app/endpoints/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

In API endpoints, raise FastAPI HTTPException with appropriate status codes for error handling

Files:

  • src/app/endpoints/query.py
  • src/app/endpoints/metrics.py
  • src/app/endpoints/streaming_query.py
  • src/app/endpoints/conversations_v2.py
  • src/app/endpoints/authorized.py
  • src/app/endpoints/conversations.py
  • src/app/endpoints/health.py
  • src/app/endpoints/feedback.py
  • src/app/endpoints/models.py
  • src/app/endpoints/config.py
  • src/app/endpoints/info.py
  • src/app/endpoints/root.py
**/__init__.py

📄 CodeRabbit inference engine (CLAUDE.md)

Package init.py files contain brief package descriptions

Files:

  • src/authentication/__init__.py
🧬 Code graph analysis (15)
src/app/endpoints/query.py (1)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/app/endpoints/metrics.py (4)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/authorization/middleware.py (1)
  • authorize (111-122)
src/metrics/utils.py (1)
  • setup_model_metrics (20-57)
src/models/config.py (2)
  • config (138-144)
  • Action (311-350)
src/app/endpoints/streaming_query.py (1)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/app/endpoints/conversations_v2.py (4)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/authorization/middleware.py (1)
  • authorize (111-122)
src/models/config.py (2)
  • config (138-144)
  • Action (311-350)
src/models/responses.py (3)
  • ConversationDeleteResponse (513-546)
  • ConversationResponse (463-510)
  • ConversationsListResponseV2 (670-677)
src/lightspeed_stack.py (2)
src/configuration.py (1)
  • configuration (65-69)
src/runners/uvicorn.py (1)
  • start_uvicorn (12-31)
src/app/main.py (6)
src/app/database.py (2)
  • create_tables (29-31)
  • initialize_database (101-129)
src/client.py (3)
  • AsyncLlamaStackClientHolder (18-55)
  • load (23-47)
  • get_client (49-55)
src/configuration.py (2)
  • configuration (65-69)
  • load_configuration (52-58)
src/log.py (1)
  • get_logger (7-13)
src/utils/common.py (1)
  • register_mcp_servers_async (15-34)
src/utils/llama_stack_version.py (1)
  • check_llama_stack_version (22-39)
src/app/endpoints/authorized.py (1)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/app/endpoints/conversations.py (2)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/authorization/middleware.py (1)
  • authorize (111-122)
src/authentication/__init__.py (1)
src/configuration.py (4)
  • configuration (65-69)
  • LogicError (32-33)
  • authentication_configuration (100-105)
  • load_configuration (52-58)
src/app/endpoints/health.py (3)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/authorization/middleware.py (1)
  • authorize (111-122)
src/models/responses.py (2)
  • ProviderHealthStatus (199-219)
  • ReadinessResponse (222-275)
src/app/endpoints/feedback.py (2)
src/models/responses.py (1)
  • ForbiddenResponse (448-460)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/app/endpoints/models.py (2)
src/authorization/middleware.py (1)
  • authorize (111-122)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/app/endpoints/config.py (1)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/app/endpoints/info.py (4)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
src/authorization/middleware.py (1)
  • authorize (111-122)
src/client.py (1)
  • AsyncLlamaStackClientHolder (18-55)
src/configuration.py (1)
  • configuration (65-69)
src/app/endpoints/root.py (1)
src/authentication/__init__.py (1)
  • get_auth_dependency (14-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-pr
  • GitHub Check: e2e_tests
🔇 Additional comments (26)
src/lightspeed_stack.py (2)

77-79: LGTM! Environment variable pattern enables worker-local config loading.

The env var approach correctly addresses the process isolation issue with uvicorn workers > 1. Each worker can now independently load configuration via get_auth_dependency().


60-60: Configuration validation is consistent
Both the main process (at line 60) and each worker in get_auth_dependency invoke the same configuration.load_configuration, so all schema and permission errors are validated identically in both contexts.

src/app/endpoints/authorized.py (1)

40-40: LGTM! Dependency injection pattern correctly migrated.

The change from module-level auth_dependency to inline Depends(get_auth_dependency()) enables per-worker authentication setup, fixing the multi-worker compatibility issue.

src/app/endpoints/conversations_v2.py (1)

95-95: LGTM! Consistent dependency injection pattern across all conversation endpoints.

All three endpoints (get_conversations_list, get_conversation, delete_conversation) correctly migrated from module-level auth dependency to per-request resolution.

Also applies to: 125-125, 161-161

src/app/endpoints/streaming_query.py (1)

572-572: LGTM! Streaming endpoint correctly migrated to per-request auth dependency.

The change ensures each worker process can independently authenticate streaming query requests, which is critical for the high-concurrency streaming endpoint.

src/app/endpoints/config.py (1)

64-64: LGTM! Config endpoint correctly uses per-request auth dependency.

The migration is consistent with other endpoints. Note that get_auth_dependency() may load configuration as a side effect (lines 19-24 in src/authentication/init.py), and this endpoint also returns configuration—this is acceptable since the lazy loading ensures config is available before this handler executes.

src/app/endpoints/models.py (1)

54-55: Auth DI change looks good.

Per-request Depends(get_auth_dependency()) aligns with worker-per-process model and removes globals.

src/app/endpoints/query.py (1)

176-178: Auth DI change looks good.

Switch to Depends(get_auth_dependency()) is correct and consistent.

src/app/endpoints/root.py (1)

781-783: Auth DI change looks good.

Per-request dependency is consistent with the rest of the API.

src/app/endpoints/health.py (2)

81-83: Auth DI change looks good.

Readiness handler now uses per-request auth dependency; consistent with PR goals.


127-129: Auth DI change looks good.

Liveness handler updated consistently.

src/authentication/__init__.py (2)

4-4: Import added for environment variable access.

The os import is appropriately added to support reading LIGHTSPEED_STACK_CONFIG_PATH from the environment.


7-9: Imports reorganized for explicit dependency access.

The imports now explicitly reference AuthInterface and LogicError with configuration as a module import, supporting the new lazy-loading pattern. This follows the coding guideline for absolute imports.

src/app/endpoints/info.py (2)

6-6: FastAPI imports follow standard order.

The import statement correctly includes Depends alongside other FastAPI components as per coding guidelines.


40-40: Dependency injection updated for per-request resolution.

The endpoint now uses Depends(get_auth_dependency()) instead of a precomputed auth_dependency, aligning with the PR's goal to support multiple Uvicorn workers by eliminating shared global state. Each request will now obtain a fresh authentication dependency.

src/app/endpoints/metrics.py (2)

5-5: FastAPI dependencies consolidated.

Import statement appropriately groups FastAPI components including Depends, following standard import conventions.


24-24: Authentication dependency updated for multi-worker compatibility.

The parameter now uses Depends(get_auth_dependency()) for per-request dependency resolution, consistent with the PR's objective to eliminate global state for Uvicorn multi-worker deployments.

src/app/endpoints/feedback.py (5)

3-3: Import added for JSON serialization.

The json import supports json.dump() usage at line 156 in store_feedback(). This import is necessary and correctly placed.


6-6: Datetime imports consolidated.

UTC and datetime are now imported together in a single statement, improving import hygiene.


22-22: ForbiddenResponse imported for endpoint responses.

The import is correctly added to support the response model at line 44 in the feedback_response dictionary.


90-90: Feedback endpoint updated for per-request dependency resolution.

The auth parameter now uses Depends(get_auth_dependency()), aligning with the multi-worker compatibility goal by removing reliance on module-level state.


186-186: Status update endpoint updated for per-request dependency resolution.

Consistent with the other endpoint, the auth parameter uses Depends(get_auth_dependency()) to support worker-isolated authentication.

src/app/endpoints/conversations.py (4)

6-6: FastAPI imports standardized.

Depends is now imported alongside other FastAPI components, following the coding guideline for standard FastAPI imports.


181-181: List endpoint updated for multi-worker compatibility.

The get_conversations_list_endpoint_handler now uses Depends(get_auth_dependency()) for per-request authentication, eliminating shared module-level state.


243-243: Get endpoint updated for multi-worker compatibility.

The get_conversation_endpoint_handler now uses Depends(get_auth_dependency()) for per-request authentication, consistent with the PR's objective.


371-371: Delete endpoint updated for multi-worker compatibility.

The delete_conversation_endpoint_handler now uses Depends(get_auth_dependency()) for per-request authentication, completing the migration away from module-level dependency instances.

logger.info("Initializing app")


service_name = configuration.configuration.name
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 | 🔴 Critical

Critical: configuration accessed before it’s loaded.

service_name = configuration.configuration.name will raise LogicError at import time because configuration is loaded later in lifespan. Move name resolution into lifespan and update app metadata after loading.

-service_name = configuration.configuration.name
+# Defer service name resolution until configuration is loaded in lifespan

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/app/main.py around line 26, the code reads
configuration.configuration.name at import time which raises LogicError because
the configuration is loaded later in the app lifespan; move the service name
resolution into the app lifespan/startup routine where configuration is loaded,
remove the top-level access, and after loading configuration assign the name to
the app metadata (for example app.title or app.state.metadata['name']) so the
application metadata is updated only after configuration is available.

Comment on lines +38 to +41
configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"])
await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack)
client = AsyncLlamaStackClientHolder().get_client()
# check if the Llama Stack version is supported by the service
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 | 🟠 Major

Handle missing LIGHTSPEED_STACK_CONFIG_PATH gracefully.

Direct indexing os.environ[...] will crash with KeyError. Validate and fail with a clear message (or provide a safe default if desired).

-    configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"])
+    config_path = os.getenv("LIGHTSPEED_STACK_CONFIG_PATH")
+    if not config_path:
+        logger.error("LIGHTSPEED_STACK_CONFIG_PATH is not set; cannot load configuration")
+        raise RuntimeError("Required env var LIGHTSPEED_STACK_CONFIG_PATH is not set")
+    configuration.load_configuration(config_path)
📝 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
configuration.load_configuration(os.environ["LIGHTSPEED_STACK_CONFIG_PATH"])
await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack)
client = AsyncLlamaStackClientHolder().get_client()
# check if the Llama Stack version is supported by the service
config_path = os.getenv("LIGHTSPEED_STACK_CONFIG_PATH")
if not config_path:
logger.error("LIGHTSPEED_STACK_CONFIG_PATH is not set; cannot load configuration")
raise RuntimeError("Required env var LIGHTSPEED_STACK_CONFIG_PATH is not set")
configuration.load_configuration(config_path)
await AsyncLlamaStackClientHolder().load(configuration.configuration.llama_stack)
client = AsyncLlamaStackClientHolder().get_client()
# check if the Llama Stack version is supported by the service

Comment on lines +18 to +27
try:
module = configuration.authentication_configuration.module
except LogicError:
# Only load once if not already loaded
config_path = os.getenv(
"LIGHTSPEED_STACK_CONFIG_PATH",
"tests/configuration/lightspeed-stack.yaml",
)
configuration.load_configuration(config_path)
module = configuration.authentication_configuration.module
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 | 🔴 Critical

🧩 Analysis chain

Review the default configuration path and add concurrency protection.

Three concerns with the lazy-loading implementation:

  1. Test path as production default: Line 24 defaults to "tests/configuration/lightspeed-stack.yaml", which is a test fixture path. In a production multi-worker deployment, this path may not exist or contain inappropriate test configuration. Consider using a production-appropriate default or making the environment variable mandatory.

  2. Misleading comment: Line 21 states "Only load once if not already loaded", but the code does not prevent multiple concurrent workers or threads from simultaneously calling configuration.load_configuration(config_path) when they all encounter LogicError. If multiple requests hit this code path concurrently before configuration loads, race conditions may occur.

  3. Repeated loading on exception: Each time LogicError is raised (configuration not loaded), the code will call load_configuration. While this ensures eventual loading, the comment implies single execution which is not guaranteed.

Verify if concurrent initialization is protected elsewhere:


🏁 Script executed:

#!/bin/bash
# Check if configuration.load_configuration has thread-safety mechanisms
ast-grep --pattern $'def load_configuration($$$) -> $RET:
  $$$'

Length of output: 539


🏁 Script executed:

#!/bin/bash
# show the lazy‐load block in authentication module
sed -n '1,50p' src/authentication/__init__.py
# search for explicit locks
rg -n 'threading\.Lock' -C2 .

Length of output: 2104


Enforce safe, production-ready configuration loading

  • Replace the test-fixture default ("tests/configuration/lightspeed-stack.yaml") with a production-appropriate path or make LIGHTSPEED_STACK_CONFIG_PATH mandatory.
  • Surround configuration.load_configuration(config_path) with a synchronization primitive (e.g. module-level threading.Lock) to prevent concurrent races.
  • Update or remove the “Only load once if not already loaded” comment to reflect the actual single-load guarantee under concurrency. [src/authentication/init.py:18–27]
🤖 Prompt for AI Agents
In src/authentication/__init__.py around lines 18 to 27, the code uses a
test-fixture default config path and loads configuration without
synchronization; change this to require LIGHTSPEED_STACK_CONFIG_PATH to be set
(remove the "tests/..." default) or replace it with a production path, wrap the
call to configuration.load_configuration(config_path) in a module-level
threading.Lock to prevent concurrent races during lazy load, and update the
comment to reflect that the lock enforces a single-load guarantee under
concurrency.

Copy link
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

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

Nice one, LGTM

@tisnik tisnik merged commit 714ce2b into lightspeed-core:main Oct 1, 2025
18 of 19 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Oct 15, 2025
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants