Skip to content

Conversation

@avirajsingh7
Copy link
Collaborator

@avirajsingh7 avirajsingh7 commented Nov 24, 2025

Summary

Target issue is #439
Introduce support for referencing stored configurations using an identifier and version, allowing for more flexible configuration management. This change addresses the need for users to either provide a stored configuration or an ad-hoc configuration blob, ensuring that only one method is used at a time.

Checklist

Before submitting a pull request, please ensure that you mark these task.

  • Ran fastapi run --reload app/main.py or docker compose up in the repository root and test.
  • If you've fixed a bug or added code that is tested and has test cases.

Notes

Please add here if any other information is required for the reviewer.

Summary by CodeRabbit

  • New Features

    • Requests can use either a stored reusable LLM config (id + version) or an ad-hoc inline config blob.
    • New request options to include provider raw responses and attach request metadata.
  • Improvements

    • Validation enforces a single config path and clearer errors for resolution failures.
    • Runtime now resolves/normalizes config blobs and centralizes error handling.
  • Models

    • Introduced typed config blob and re-exported config-related models for use across the app.
  • Tests

    • Updated tests to the nested blob shape and added coverage for stored-config resolution and failure paths.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

Adds a typed ConfigBlob and updates LLMCallConfig to support either a stored config (id+version) or an ad‑hoc blob; introduces resolve_config_blob and adjusts execute_job (signature and flow) to resolve and use the blob for provider selection; models, CRUD, and tests updated to the nested completion shape.

Changes

Cohort / File(s) Summary
LLM request models
backend/app/models/llm/request.py
Added ConfigBlob(SQLModel) (holds completion), extended LLMCallConfig with id, version, blob, validate_config_logic validator, and is_stored_config property; updated LLMCallRequest with include_provider_raw_response, request_metadata, and adjusted config description.
LLM job service
backend/app/services/llm/jobs.py
Added resolve_config_blob(config_crud, config) to fetch/deserialize stored config versions; updated execute_job(...) signature (adds task_instance) and logic to branch on config.is_stored_config, use resolved ConfigBlob for provider init/execution, centralize error handling via APIResponse, and return unified callback responses.
Config CRUD / versioning
backend/app/crud/config/config.py, backend/app/crud/config/version.py
When creating ConfigVersion, serialize config_blob via .model_dump() before persisting; commit/flow unchanged.
Config models
backend/app/models/config/config.py, backend/app/models/config/version.py
config_blob typed as ConfigBlob (replacing dict[str, Any]); added validate_blob_not_empty field validator and updated field descriptions to reflect nested completion.
Models package exports
backend/app/models/__init__.py, backend/app/models/llm/__init__.py
Re-exported ConfigBlob (and CompletionConfig) at package/llm module level to expose new types via app.models.
Tests & test utilities
backend/app/tests/**, backend/app/tests/utils/test_data.py
Tests updated to use ConfigBlob/CompletionConfig and nested completion.params; added example_config_blob fixture; updated helpers create_test_config/create_test_version signatures to accept ConfigBlob; added stored-config resolution tests and assertions comparing .model_dump().

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/API
    participant Job as execute_job
    participant ConfigCrud as ConfigVersionCrud
    participant Provider as LLM Provider

    Note over Job,ConfigCrud: Resolve stored vs ad-hoc config into a ConfigBlob

    Client->>Job: submit LLMCallRequest (LLMCallConfig)
    Job->>Job: validate_config_logic(config)
    alt stored config (is_stored_config)
        Job->>ConfigCrud: fetch by id+version
        ConfigCrud-->>Job: ConfigVersion / error
        Job->>Job: parse ConfigVersion.config_blob -> ConfigBlob
        alt parse success
            Job->>Provider: init(provider=config_blob.completion.provider)
            Job->>Provider: execute(completion=config_blob.completion, task_instance)
        else fetch/parse failure
            Job-->>Client: emit failure callback (resolve/parse error)
        end
    else ad-hoc blob
        Job->>Provider: init(provider=config.blob.completion.provider)
        Job->>Provider: execute(completion=config.blob.completion, task_instance)
    end
    Provider-->>Job: result
    Job-->>Client: success/failure callback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus areas:
    • validate_config_logic correctness (mutual exclusivity and id/version pairing).
    • resolve_config_blob DB fetch, deserialization, and error/logging paths.
    • All call sites and tests impacted by execute_job signature change (task_instance).
    • Tests asserting persisted blobs vs .model_dump() and the new fixtures.

Possibly related PRs

Suggested reviewers

  • AkhileshNegi
  • kartpop
  • Prajna1999

Poem

🐇 I nibbled the schema, tucked a blob in my paw,
Stored or ad‑hoc — now both paths I saw.
Validate, resolve, then send to the LLM hill,
Hop, dispatch, callback — tidy, swift, and chill. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Integrate Config Management with Unified Api' is vague and overly broad—it doesn't clearly describe the specific change (dual-path config support: stored references or ad-hoc blobs). Clarify the title to specifically mention the main change, e.g., 'Support stored config references alongside ad-hoc blobs in LLMCallConfig' or 'Add dual-path configuration support to LLMCallConfig'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch llm_call_config_support

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.

Copy link

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
backend/app/models/config/config.py (1)

67-71: Remove or replace the ineffective validator check on ConfigBlob.

The field_validator runs after Pydantic's internal validation, receiving a ConfigBlob instance. Since Pydantic models do not implement bool by default, model instances are always truthy. The check if not value will never evaluate to True for a valid ConfigBlob object—it's dead code. The actual validation relies on ConfigBlob's required completion field (CompletionConfig); invalid input dicts fail earlier during Pydantic validation, never reaching this validator. Either remove the validator or replace it with an explicit check like if not value.completion if additional validation is needed.

🧹 Nitpick comments (5)
backend/app/services/llm/jobs.py (1)

122-122: Unused parameter task_instance.

The task_instance parameter is added to the function signature but is not used anywhere in the function body. If this parameter is required by the Celery task framework or for future use, consider adding a comment explaining its purpose. Otherwise, consider removing it or prefixing with an underscore (_task_instance) to indicate it's intentionally unused.

backend/app/models/config/version.py (1)

11-69: ConfigBlob-on-create design looks sound; consider typing the validator

Using ConfigBlob only on ConfigVersionCreate while keeping the stored/public models on dict[str, Any] is a clean separation of validated input vs JSONB storage and matches the inline comment here. The existing validate_blob_not_empty validator still runs on both dicts and ConfigBlob instances, so behavior is preserved.

For a small clarity win and to match the “use type hints” guideline, you could add annotations to validate_blob_not_empty (e.g. cls: type["ConfigVersionBase"], value: dict[str, Any] | ConfigBlob, and an explicit return type), without changing logic.

As per coding guidelines, ...

backend/app/tests/api/routes/configs/test_version.py (1)

29-36: ConfigBlob-shaped payloads look correct; consider DRYing and normalizing imports

The updated config_blob JSON bodies in these tests now follow the ConfigBlob/CompletionConfig structure (completion → provider/params), and the empty-blob case still correctly yields a 422 via validation, so behavior is consistent with the new model.

Two small refinements you might consider:

  • Move the from app.models.llm.request import ConfigBlob, CompletionConfig import in test_get_version_by_number up to the module top to keep import style uniform.
  • The repeated literal config_blob payloads could be produced by a small factory/fixture (e.g. make_config_blob_payload(model=..., **overrides)) so schema or default changes only need to be updated in one place.

Based on learnings, centralizing test payload construction via factories/fixtures improves maintainability.

Also applies to: 91-96, 121-127, 154-159, 301-312, 451-456

backend/app/tests/utils/test_data.py (1)

3-24: Typed ConfigBlob integration in test helpers looks good; consider sharing the default blob

Updating create_test_config/create_test_version to accept ConfigBlob | None and constructing a default ConfigBlob(CompletionConfig(...)) when missing brings these helpers in line with the new config model and avoids leaking untyped dicts into call sites. That’s a solid improvement.

If these defaults evolve, there’s a bit of duplication between the two ConfigBlob constructions (same provider and almost-identical params). A small factory (e.g. def make_default_config_blob(...) -> ConfigBlob) or fixture both helpers call would keep them perfectly in sync.

Based on learnings, centralizing shared test objects via factories/fixtures reduces future maintenance.

Also applies to: 239-269, 283-305

backend/app/tests/crud/config/test_version.py (1)

6-27: Good use of a shared ConfigBlob fixture; minor consistency and type-hint nits

The example_config_blob fixture and its reuse across these CRUD tests nicely centralize the configuration shape and align the tests with the ConfigBlob/CompletionConfig model. The checks that fetched_version.config_blob == example_config_blob.model_dump() give a clear contract that the CRUD layer stores raw JSON, not model instances.

Two optional cleanups:

  • In test_create_version you currently model_dump() the fixture and pass that dict into ConfigVersionCreate, whereas other tests pass the ConfigBlob directly. For consistency (and to avoid an extra parse/serialize round), you could pass example_config_blob into ConfigVersionCreate everywhere and only use model_dump() for expected-value assertions.
  • Adding a return type annotation to the example_config_blob fixture (def example_config_blob() -> ConfigBlob) would keep type hints complete in this module.

Based on learnings, shared fixtures plus consistent usage patterns make the tests easier to evolve.

Also applies to: 29-49, 52-80, 98-120, 376-400

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2e05cb and 9884fff.

📒 Files selected for processing (12)
  • backend/app/crud/config/config.py (1 hunks)
  • backend/app/crud/config/version.py (1 hunks)
  • backend/app/models/__init__.py (1 hunks)
  • backend/app/models/config/config.py (2 hunks)
  • backend/app/models/config/version.py (2 hunks)
  • backend/app/models/llm/__init__.py (1 hunks)
  • backend/app/services/llm/jobs.py (3 hunks)
  • backend/app/tests/api/routes/configs/test_config.py (3 hunks)
  • backend/app/tests/api/routes/configs/test_version.py (6 hunks)
  • backend/app/tests/crud/config/test_config.py (5 hunks)
  • backend/app/tests/crud/config/test_version.py (9 hunks)
  • backend/app/tests/utils/test_data.py (6 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use type hints in Python code (Python 3.11+ project)

Files:

  • backend/app/crud/config/config.py
  • backend/app/tests/api/routes/configs/test_config.py
  • backend/app/models/config/config.py
  • backend/app/crud/config/version.py
  • backend/app/models/config/version.py
  • backend/app/tests/api/routes/configs/test_version.py
  • backend/app/models/llm/__init__.py
  • backend/app/services/llm/jobs.py
  • backend/app/tests/crud/config/test_config.py
  • backend/app/models/__init__.py
  • backend/app/tests/crud/config/test_version.py
  • backend/app/tests/utils/test_data.py
backend/app/crud/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Implement database access operations in backend/app/crud/

Files:

  • backend/app/crud/config/config.py
  • backend/app/crud/config/version.py
backend/app/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Define SQLModel entities (database tables and domain objects) in backend/app/models/

Files:

  • backend/app/models/config/config.py
  • backend/app/models/config/version.py
  • backend/app/models/llm/__init__.py
  • backend/app/models/__init__.py
backend/app/services/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Implement business logic services under backend/app/services/

Files:

  • backend/app/services/llm/jobs.py
🧠 Learnings (1)
📚 Learning: 2025-10-08T12:05:01.317Z
Learnt from: CR
Repo: ProjectTech4DevAI/ai-platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-08T12:05:01.317Z
Learning: Applies to app/tests/**/*.py : Use the factory pattern for test fixtures

Applied to files:

  • backend/app/tests/crud/config/test_config.py
  • backend/app/tests/crud/config/test_version.py
🧬 Code graph analysis (7)
backend/app/models/config/config.py (2)
backend/app/core/util.py (1)
  • now (13-14)
backend/app/models/llm/request.py (1)
  • ConfigBlob (61-67)
backend/app/models/config/version.py (1)
backend/app/models/llm/request.py (1)
  • ConfigBlob (61-67)
backend/app/tests/api/routes/configs/test_version.py (2)
backend/app/models/llm/request.py (2)
  • ConfigBlob (61-67)
  • CompletionConfig (49-58)
backend/app/tests/utils/test_data.py (1)
  • create_test_version (283-317)
backend/app/models/llm/__init__.py (1)
backend/app/models/llm/request.py (4)
  • LLMCallRequest (129-148)
  • CompletionConfig (49-58)
  • QueryParams (35-46)
  • ConfigBlob (61-67)
backend/app/tests/crud/config/test_config.py (4)
backend/app/models/llm/request.py (2)
  • ConfigBlob (61-67)
  • CompletionConfig (49-58)
backend/app/tests/crud/config/test_version.py (1)
  • example_config_blob (16-26)
backend/app/crud/config/config.py (1)
  • ConfigCrud (19-159)
backend/app/models/config/config.py (1)
  • ConfigCreate (56-71)
backend/app/models/__init__.py (1)
backend/app/models/llm/request.py (2)
  • ConfigBlob (61-67)
  • CompletionConfig (49-58)
backend/app/tests/utils/test_data.py (3)
backend/app/models/llm/request.py (2)
  • ConfigBlob (61-67)
  • CompletionConfig (49-58)
backend/app/models/config/config.py (1)
  • ConfigBase (13-19)
backend/app/tests/services/llm/providers/test_openai.py (1)
  • provider (27-29)
⏰ 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). (1)
  • GitHub Check: checks (3.11.7, 6)
🔇 Additional comments (10)
backend/app/services/llm/jobs.py (2)

81-114: LGTM! Well-structured error handling.

The resolve_config_blob function provides robust error handling with distinct paths for expected (HTTPException) and unexpected exceptions, along with appropriate logging. The tuple return pattern clearly communicates success vs failure states.


133-180: Validation logic confirms code is safe—the review comment concern is unfounded.

The validate_config_logic method enforces mutual exclusivity (raises error if both stored config and blob provided), requires both id and version for stored config, and requires blob for ad-hoc config. This guarantees that when config.is_stored_config is False, config.blob is non-None (line 166), and when the stored path succeeds, config_blob is non-None. The error handling at line 159 prevents entry into line 168 when config_blob would be None, ensuring config_blob is never None when accessed.

Likely an incorrect or invalid review comment.

backend/app/models/llm/__init__.py (1)

1-7: LGTM! Public exports updated correctly.

The addition of ConfigBlob to the public imports aligns with the new configuration management functionality and enables downstream modules to access this type.

backend/app/crud/config/config.py (1)

50-50: LGTM! Correct serialization of ConfigBlob.

The use of config_blob.model_dump() properly serializes the typed ConfigBlob object to a dictionary for database storage, aligning with the new typed configuration structure.

backend/app/crud/config/version.py (1)

37-37: LGTM! Consistent serialization pattern.

The serialization of config_blob using model_dump() is consistent with the approach in backend/app/crud/config/config.py and correctly handles the typed ConfigBlob structure.

backend/app/models/__init__.py (1)

90-91: LGTM! Public API surface expanded appropriately.

Adding ConfigBlob and CompletionConfig to the package-level exports enables consistent imports across the codebase and supports the new configuration management functionality.

backend/app/tests/api/routes/configs/test_config.py (1)

20-29: LGTM! Tests updated to use nested configuration structure.

The tests correctly use the new nested config_blob structure with completion.provider and completion.params, aligning with the ConfigBlob/CompletionConfig schema. This ensures the API accepts and validates the new configuration format.

Also applies to: 89-94, 419-424

backend/app/models/config/config.py (1)

5-5: LGTM! Type safety improved with ConfigBlob.

The change from dict[str, Any] to ConfigBlob provides better type safety and validation. The import of ConfigBlob is correctly added, and the removal of UniqueConstraint appears to be a cleanup (it wasn't used in the file).

Also applies to: 9-9, 60-60

backend/app/tests/crud/config/test_config.py (2)

18-29: LGTM! Good use of fixture pattern.

The example_config_blob fixture follows the factory pattern for test fixtures and provides a reusable ConfigBlob instance with properly structured nested configuration. This improves test maintainability and consistency.

Based on learnings, this aligns with the recommended approach for test fixtures.


32-57: LGTM! Tests correctly use fixture and verify serialization.

The tests properly use the example_config_blob fixture and verify that the stored blob matches example_config_blob.model_dump() (line 57), which correctly validates the serialization behavior.

Copy link

@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: 0

🧹 Nitpick comments (3)
backend/app/tests/api/routes/test_llm.py (1)

17-31: The payload structure correctly reflects the new API.

The test successfully validates the ad-hoc config blob path. Consider adding tests for:

  • Stored config path using id + version
  • Validation scenarios: both methods provided, neither provided, or partial stored config (id without version or vice versa)

These additional tests would ensure the validation logic in LLMCallConfig works correctly at the API level.

backend/app/tests/services/llm/test_jobs.py (2)

393-439: Consider reusing job_env fixture to reduce duplication.

The stored config tests duplicate the mock setup pattern from the job_env fixture. Consider parameterizing or extending the fixture to support stored config scenarios.

That said, the test logic is correct and provides good coverage for the stored config success path.


615-618: Move import to module level.

The ConfigVersionCreate import should be at the top of the file with other imports for consistency and to avoid runtime import overhead on each test run.

 from app.models.llm.request import ConfigBlob, LLMCallConfig
+from app.models.config import ConfigVersionCreate
 from app.services.llm.jobs import (

Then remove line 617.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9884fff and 82707a2.

📒 Files selected for processing (2)
  • backend/app/tests/api/routes/test_llm.py (2 hunks)
  • backend/app/tests/services/llm/test_jobs.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use type hints in Python code (Python 3.11+ project)

Files:

  • backend/app/tests/api/routes/test_llm.py
  • backend/app/tests/services/llm/test_jobs.py
🧬 Code graph analysis (1)
backend/app/tests/api/routes/test_llm.py (1)
backend/app/models/llm/request.py (3)
  • LLMCallConfig (70-126)
  • CompletionConfig (49-58)
  • ConfigBlob (61-67)
⏰ 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). (1)
  • GitHub Check: checks (3.11.7, 6)
🔇 Additional comments (8)
backend/app/tests/api/routes/test_llm.py (1)

4-9: LGTM!

The imports correctly include all necessary types for the new configuration structure.

backend/app/tests/services/llm/test_jobs.py (7)

8-31: LGTM!

The import additions are appropriate for the new stored config resolution tests. All imported symbols are utilized in the test code below.


41-49: LGTM!

The fixture properly wraps CompletionConfig inside ConfigBlob, aligning with the new dual-path LLMCallConfig semantics.


226-233: LGTM!

The request data structure correctly reflects the new blob-wrapped completion config path.


491-524: LGTM!

Good error handling coverage for the version-not-found scenario. The test correctly verifies that the job fails gracefully and returns an appropriate error message.


580-613: LGTM!

Good defensive test that validates error handling for malformed configuration blobs. Manually corrupting the database record is an appropriate technique for testing this edge case.


650-670: LGTM!

Thorough test coverage for multi-version scenarios. The test correctly verifies that each version resolves to its specific configuration values.


530-554: LGTM!

Clean unit test that verifies the happy path for resolve_config_blob, with thorough assertions on the resolved configuration values.

@codecov
Copy link

codecov bot commented Nov 25, 2025

Codecov Report

❌ Patch coverage is 95.67308% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
backend/app/services/llm/jobs.py 82.85% 6 Missing ⚠️
backend/app/models/llm/request.py 86.95% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avirajsingh7 avirajsingh7 self-assigned this Nov 25, 2025
@avirajsingh7 avirajsingh7 added ready-for-review enhancement New feature or request labels Nov 25, 2025
@avirajsingh7 avirajsingh7 linked an issue Nov 25, 2025 that may be closed by this pull request
@avirajsingh7 avirajsingh7 requested a review from kartpop November 25, 2025 10:03
else:
config_blob = config.blob

if callback is None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

why this condition if callback is None?

if callback is present, should we not fetch the provider_instance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thats the response to callback if something fails in config retrieval,
this might be confusing changed callback to callback_response

Copy link

@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: 0

♻️ Duplicate comments (1)
backend/app/services/llm/jobs.py (1)

133-183: Guard against config_blob being None before accessing config_blob.completion.provider

Right now the provider setup runs whenever callback_response is None, and it unconditionally dereferences config_blob.completion.provider. If upstream validation or future changes ever allow a case where config_blob is still None while callback_response is also None (e.g., malformed or missing inline blob), this will raise an AttributeError and fall into the generic “Unexpected error occurred” path instead of returning a clear configuration error.

It’s safer to:

  • Only call get_llm_provider when both callback_response is None and config_blob is not None.
  • If callback_response is still None but config_blob is None, synthesize a failure response so you never dereference a missing blob.

For example:

-            if callback_response is None:
-                try:
-                    provider_instance = get_llm_provider(
-                        session=session,
-                        provider_type=config_blob.completion.provider,
-                        project_id=project_id,
-                        organization_id=organization_id,
-                    )
-                except ValueError as ve:
-                    callback_response = APIResponse.failure_response(
-                        error=str(ve),
-                        metadata=request.request_metadata,
-                    )
+            if callback_response is None and config_blob is not None:
+                try:
+                    provider_instance = get_llm_provider(
+                        session=session,
+                        provider_type=config_blob.completion.provider,
+                        project_id=project_id,
+                        organization_id=organization_id,
+                    )
+                except ValueError as ve:
+                    callback_response = APIResponse.failure_response(
+                        error=str(ve),
+                        metadata=request.request_metadata,
+                    )
+            elif callback_response is None and config_blob is None:
+                callback_response = APIResponse.failure_response(
+                    error="Resolved configuration blob is empty",
+                    metadata=request.request_metadata,
+                )

This aligns the control flow with the intent (“one of stored config or blob is guaranteed”) while making the failure mode explicit and avoiding any chance of dereferencing a None blob.

🧹 Nitpick comments (1)
backend/app/services/llm/jobs.py (1)

219-223: Remove unnecessary f-string prefix in unexpected-error message

"Unexpected error occurred" doesn’t interpolate any variables, so the f prefix is redundant and Ruff flags it (F541). A plain string avoids the warning:

-        callback_response = APIResponse.failure_response(
-            error=f"Unexpected error occurred",
-            metadata=request.request_metadata,
-        )
+        callback_response = APIResponse.failure_response(
+            error="Unexpected error occurred",
+            metadata=request.request_metadata,
+        )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07aa683 and 63b3cc1.

📒 Files selected for processing (1)
  • backend/app/services/llm/jobs.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use type hints in Python code (Python 3.11+ project)

Files:

  • backend/app/services/llm/jobs.py
backend/app/services/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Implement business logic services under backend/app/services/

Files:

  • backend/app/services/llm/jobs.py
🪛 Ruff (0.14.6)
backend/app/services/llm/jobs.py

221-221: f-string without any placeholders

Remove extraneous f prefix

(F541)

⏰ 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). (1)
  • GitHub Check: checks (3.11.7, 6)
🔇 Additional comments (1)
backend/app/services/llm/jobs.py (1)

81-114: Config resolution helper looks clean and well-factored

The separation between fetching the stored config version and parsing it into ConfigBlob, with (config_blob, error_message) as the contract, keeps execute_job straightforward and centralizes error handling and logging. This helper looks good as-is.

Copy link

@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: 0

🧹 Nitpick comments (2)
backend/app/models/llm/request.py (1)

91-97: Minor: Missing space in description string.

There's a missing space between the two sentences in the description.

     blob: ConfigBlob | None = Field(
         default=None,
         description=(
-            "Raw JSON blob of the full configuration. Used for ad-hoc configurations without storing."
-            "Either this or (id + version) must be provided."
+            "Raw JSON blob of the full configuration. Used for ad-hoc configurations without storing. "
+            "Either this or (id + version) must be provided."
         ),
     )
backend/app/services/llm/jobs.py (1)

219-228: Remove extraneous f-prefix from string literal.

The f-string at line 221 has no placeholders and should be a regular string.

     except Exception as e:
         callback_response = APIResponse.failure_response(
-            error=f"Unexpected error occurred",
+            error="Unexpected error occurred",
             metadata=request.request_metadata,
         )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63b3cc1 and 3715671.

📒 Files selected for processing (2)
  • backend/app/models/llm/request.py (2 hunks)
  • backend/app/services/llm/jobs.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use type hints in Python code (Python 3.11+ project)

Files:

  • backend/app/models/llm/request.py
  • backend/app/services/llm/jobs.py
backend/app/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Define SQLModel entities (database tables and domain objects) in backend/app/models/

Files:

  • backend/app/models/llm/request.py
backend/app/services/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Implement business logic services under backend/app/services/

Files:

  • backend/app/services/llm/jobs.py
🪛 Ruff (0.14.6)
backend/app/services/llm/jobs.py

221-221: f-string without any placeholders

Remove extraneous f prefix

(F541)

⏰ 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). (1)
  • GitHub Check: checks (3.11.7, 6)
🔇 Additional comments (6)
backend/app/models/llm/request.py (3)

61-68: LGTM!

Clean model definition with appropriate type hints and placeholder comments for future extensions.


99-127: LGTM!

The validation logic correctly enforces mutual exclusivity between stored config (id+version) and ad-hoc blob, while ensuring both id and version are provided together when using stored config. The is_stored_config property provides a clean interface for checking the configuration path.


129-163: LGTM!

The updated docstring and field descriptions clearly document the dual-path configuration approach (stored vs blob) and provide appropriate production guidance. This addresses the prior review feedback about swagger documentation, as Field descriptions are exposed in the OpenAPI schema.

backend/app/services/llm/jobs.py (3)

81-114: LGTM!

The function correctly handles both expected errors (HTTPException, validation errors) and unexpected exceptions with appropriate logging. The tuple return pattern for (result, error) is clear and facilitates clean error handling at the call site.


150-183: Config resolution flow looks correct; prior null-pointer concern is addressed.

The early return at lines 164-166 now correctly handles the stored-config failure case, preventing null access on config_blob. The else branch (line 168-169) assigns config.blob which is guaranteed non-None by LLMCallConfig validation.


185-217: LGTM!

The success and failure paths correctly construct responses using callback_response and handle callback delivery and job status updates appropriately.

Copy link

@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: 0

🧹 Nitpick comments (2)
backend/app/services/llm/jobs.py (2)

133-170: Config resolution and provider invocation correctly routed through ConfigBlob

The updated execute_job flow looks sound:

  • Relies on LLMCallConfig validation, then branches on config.is_stored_config, ensuring only one of stored config or blob is used.
  • For stored configs, you resolve via ConfigVersionCrud and resolve_config_blob, and on any retrieval/parsing error you immediately build an APIResponse.failure_response and delegate to handle_job_error, so config_blob is never dereferenced when invalid.
  • For ad‑hoc configs, you directly assign config_blob = config.blob, which is guaranteed non‑None by the model validator.
  • provider_type and completion_config are consistently sourced from config_blob.completion, unifying both config paths.

Given the model invariants and early‑return error handling, config_blob will always be non‑None where accessed here. If you want to appease stricter type checkers, you could optionally add an assert config_blob is not None before using it, but functionally this is correct.

-            try:
-                provider_instance = get_llm_provider(
-                    session=session,
-                    provider_type=config_blob.completion.provider,
-                    project_id=project_id,
-                    organization_id=organization_id,
-                )
+            assert config_blob is not None
+            try:
+                provider_instance = get_llm_provider(
+                    session=session,
+                    provider_type=config_blob.completion.provider,
+                    project_id=project_id,
+                    organization_id=organization_id,
+                )

Also applies to: 174-174, 186-186


220-228: Remove redundant f‑string in generic error response (linter F541)

At line 221, error=f"Unexpected error occurred" is an f‑string without any interpolation. While it works, it triggers a linter warning and is unnecessary.

You can simplify it as:

-        callback_response = APIResponse.failure_response(
-            error=f"Unexpected error occurred",
-            metadata=request.request_metadata,
-        )
+        callback_response = APIResponse.failure_response(
+            error="Unexpected error occurred",
+            metadata=request.request_metadata,
+        )

Everything else in this generic error handler (logging str(e) and delegating to handle_job_error) looks good.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3715671 and 591f202.

📒 Files selected for processing (1)
  • backend/app/services/llm/jobs.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use type hints in Python code (Python 3.11+ project)

Files:

  • backend/app/services/llm/jobs.py
backend/app/services/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Implement business logic services under backend/app/services/

Files:

  • backend/app/services/llm/jobs.py
🧬 Code graph analysis (1)
backend/app/services/llm/jobs.py (6)
backend/app/crud/config/version.py (2)
  • ConfigVersionCrud (15-142)
  • exists_or_raise (113-123)
backend/app/crud/jobs.py (1)
  • JobCrud (11-42)
backend/app/models/job.py (3)
  • JobStatus (9-13)
  • JobType (16-18)
  • JobUpdate (47-50)
backend/app/models/llm/request.py (3)
  • ConfigBlob (61-67)
  • LLMCallConfig (70-126)
  • is_stored_config (124-126)
backend/app/crud/config/config.py (1)
  • exists_or_raise (134-142)
backend/app/utils.py (3)
  • APIResponse (30-54)
  • failure_response (43-54)
  • success_response (37-40)
🪛 Ruff (0.14.6)
backend/app/services/llm/jobs.py

221-221: f-string without any placeholders

Remove extraneous f prefix

(F541)

⏰ 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). (1)
  • GitHub Check: checks (3.11.7, 6)
🔇 Additional comments (3)
backend/app/services/llm/jobs.py (3)

9-12: Imports correctly updated for config CRUD and typed LLM config models

The added imports for ConfigVersionCrud, ConfigBlob, and LLMCallConfig match their new usage in resolve_config_blob and execute_job, and keep the types explicit. No issues here.


81-115: resolve_config_blob helper is well‑structured; error handling looks robust

The helper cleanly separates retrieval and parsing concerns:

  • Uses ConfigVersionCrud.exists_or_raise for the stored version and converts the HTTPException.detail into a user‑facing error string.
  • Catches parse issues when constructing ConfigBlob(**config_version.config_blob) and returns a clear validation error.
  • Logs unexpected exceptions with config id/version context while returning generic, human‑safe error messages.

The type hints and return convention (ConfigBlob | None, str | None) are clear and align with how the caller handles errors.


192-217: Success and failure response paths are consistent with centralized error handling

The post‑execution handling is coherent:

  • On non‑empty response, you build an APIResponse.success_response, optionally send a callback, mark the job as SUCCESS, log, and return the serialized response directly.
  • When response is falsy, you derive a failure APIResponse using error or "Unknown error occurred" and route it through handle_job_error, which standardizes callback sending and job status updates.

This keeps success and error paths explicit while centralizing the failure side effects in handle_job_error.

@avirajsingh7 avirajsingh7 merged commit 1821b84 into main Nov 27, 2025
3 checks passed
@avirajsingh7 avirajsingh7 deleted the llm_call_config_support branch November 27, 2025 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ready-for-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Config Management with Unified LLM API

4 participants