Repository: openlit/openlit
Pull request #720: [FIX]: ollama instrumentation failed when using symbol imports before…
Author: login: minimAluminiumalism
Labels: name: openlit-python
description: Issue related to OpenLIT Python SDK
Created: 2025-05-13T06:43:30Z | Merged: 2025-05-15T08:42:23Z

… openlit init

### Overview:
<!-- What does this PR do? Link issues here -->

Solve https://github.com/openlit/openlit/issues/712

### Visuals (If applicable):
<!-- Attach screenshots/screen recordings here to show the changes -->


### Checklist:
- [x] PR name follows conventional commit format: `[Feat]: ...` or `[Fix]: ....`
- [x] Added visuals for changes (If applicable)
- [x] Checked OpenLIT [contribution guidelines](https://github.com/openlit/openlit/blob/main/CONTRIBUTING.md)

## Summary by Sourcery

Fix ollama instrumentation to properly patch methods imported before OpenLIT initialization by recording pre-imported modules and applying wrappers during and after init.

Bug Fixes:
- Ensure ollama instrumentation applies to modules imported before OpenLIT initialization, including direct symbol imports for chat and embeddings

Enhancements:
- Refactor instrumentation logic into a unified `_apply_instrumentation` function that wraps sync and async chat/embeddings methods
- Add import hooks to record future ollama imports and apply instrumentation dynamically


Diff:
diff --git a/.gitignore b/.gitignore
index 633ebe64f..98be4062f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,6 @@ venv/
 path/
 __pycache__/
 **bun.lockb
-logs/
\ No newline at end of file
+logs/
+
+.vscode/
diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml
index 24e09aa2e..042557ceb 100644
--- a/sdk/python/pyproject.toml
+++ b/sdk/python/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "openlit"
-version = "1.33.20"
+version = "1.33.21"
 description = "OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications and GPUs, facilitating the integration of observability into your GenAI-driven projects"
 authors = ["OpenLIT"]
 license = "Apache-2.0"
diff --git a/sdk/python/src/openlit/instrumentation/ollama/__init__.py b/sdk/python/src/openlit/instrumentation/ollama/__init__.py
index 85468c8fd..08352255e 100644
--- a/sdk/python/src/openlit/instrumentation/ollama/__init__.py
+++ b/sdk/python/src/openlit/instrumentation/ollama/__init__.py
@@ -16,6 +16,29 @@
 
 _instruments = ("ollama >= 0.2.0",)
 
+# Dispatch wrapper to route instrumentation to chat or embeddings based on path
+def _dispatch(sync_chat_wrap, sync_emb_wrap):
+    def wrapper(wrapped, instance, args, kwargs):
+        if len(args) > 2 and isinstance(args[2], str):
+            op = args[2].rstrip("/").split("/")[-1]
+            if op == "chat":
+                return sync_chat_wrap(wrapped, instance, args, kwargs)
+            if op == "embeddings":
+                return sync_emb_wrap(wrapped, instance, args, kwargs)
+        return wrapped(*args, **kwargs)
+    return wrapper
+
+def _dispatch_async(async_chat_wrap, async_emb_wrap):
+    async def wrapper(wrapped, instance, args, kwargs):
+        if len(args) > 2 and isinstance(args[2], str):
+            op = args[2].rstrip("/").split("/")[-1]
+            if op == "chat":
+                return await async_chat_wrap(wrapped, instance, args, kwargs)
+            if op == "embeddings":
+                return await async_emb_wrap(wrapped, instance, args, kwargs)
+        return await wrapped(*args, **kwargs)
+    return wrapper
+
 class OllamaInstrumentor(BaseInstrumentor):
     """
     An instrumentor for Ollama's client library.
@@ -35,48 +58,38 @@ def _instrument(self, **kwargs):
         disable_metrics = kwargs.get("disable_metrics")
         version = importlib.metadata.version("ollama")
 
-        # sync chat
-        wrap_function_wrapper(
-            "ollama",
-            "chat",
-            chat(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
+        # Build wrapper factories for chat and embeddings
+        sync_chat_wrap = chat(
+            version, environment, application_name,
+            tracer, event_provider, pricing_info,
+            capture_message_content, metrics, disable_metrics
         )
-        wrap_function_wrapper(
-            "ollama",
-            "Client.chat",
-            chat(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
+        sync_emb_wrap = embeddings(
+            version, environment, application_name,
+            tracer, event_provider, pricing_info,
+            capture_message_content, metrics, disable_metrics
         )
-
-        # sync embeddings
-        wrap_function_wrapper(
-            "ollama",
-            "embeddings",
-            embeddings(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
+        async_chat_wrap = async_chat(
+            version, environment, application_name,
+            tracer, event_provider, pricing_info,
+            capture_message_content, metrics, disable_metrics
         )
-        wrap_function_wrapper(
-            "ollama",
-            "Client.embeddings",
-            embeddings(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
+        async_emb_wrap = async_embeddings(
+            version, environment, application_name,
+            tracer, event_provider, pricing_info,
+            capture_message_content, metrics, disable_metrics
         )
 
-        # async chat
+        # Patch underlying request methods to ensure instrumentation regardless of import order
         wrap_function_wrapper(
-            "ollama",
-            "AsyncClient.chat",
-            async_chat(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
+            "ollama._client",
+            "Client._request",
+            _dispatch(sync_chat_wrap, sync_emb_wrap),
         )
-
-        # async embeddings
         wrap_function_wrapper(
-            "ollama",
-            "AsyncClient.embeddings",
-            async_embeddings(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
+            "ollama._client",
+            "AsyncClient._request",
+            _dispatch_async(async_chat_wrap, async_emb_wrap),
         )
 
     def _uninstrument(self, **kwargs):
diff --git a/sdk/python/src/openlit/instrumentation/ollama/async_ollama.py b/sdk/python/src/openlit/instrumentation/ollama/async_ollama.py
index 9d8af0928..3ba315c82 100644
--- a/sdk/python/src/openlit/instrumentation/ollama/async_ollama.py
+++ b/sdk/python/src/openlit/instrumentation/ollama/async_ollama.py
@@ -106,7 +106,8 @@ async def wrapper(wrapped, instance, args, kwargs):
         streaming = kwargs.get("stream", False)
 
         server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 11434)
-        request_model = kwargs.get("model", "gpt-4o")
+        json_body = kwargs.get("json", {}) or {}
+        request_model = json_body.get("model") or kwargs.get("model")
 
         span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT} {request_model}"
 
@@ -154,7 +155,8 @@ async def wrapper(wrapped, instance, args, kwargs):
         """
 
         server_address, server_port = set_server_address_and_port(instance, '127.0.0.1', 11434)
-        request_model = kwargs.get('model', 'all-minilm')
+        json_body = kwargs.get('json', {}) or {}
+        request_model = json_body.get('model') or kwargs.get('model')
 
         span_name = f'{SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING} {request_model}'
 
diff --git a/sdk/python/src/openlit/instrumentation/ollama/ollama.py b/sdk/python/src/openlit/instrumentation/ollama/ollama.py
index 5864a1861..a740e141a 100644
--- a/sdk/python/src/openlit/instrumentation/ollama/ollama.py
+++ b/sdk/python/src/openlit/instrumentation/ollama/ollama.py
@@ -106,7 +106,8 @@ def wrapper(wrapped, instance, args, kwargs):
         streaming = kwargs.get("stream", False)
 
         server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 11434)
-        request_model = kwargs.get("model", "gpt-4o")
+        json_body = kwargs.get("json", {}) or {}
+        request_model = json_body.get("model") or kwargs.get("model")
 
         span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT} {request_model}"
 
@@ -154,7 +155,8 @@ def wrapper(wrapped, instance, args, kwargs):
         """
 
         server_address, server_port = set_server_address_and_port(instance, '127.0.0.1', 11434)
-        request_model = kwargs.get('model', 'all-minilm')
+        json_body = kwargs.get('json', {}) or {}
+        request_model = json_body.get('model') or kwargs.get('model')
 
         span_name = f'{SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING} {request_model}'
 
diff --git a/sdk/python/src/openlit/instrumentation/ollama/utils.py b/sdk/python/src/openlit/instrumentation/ollama/utils.py
index c5606b053..654b5e792 100644
--- a/sdk/python/src/openlit/instrumentation/ollama/utils.py
+++ b/sdk/python/src/openlit/instrumentation/ollama/utils.py
@@ -57,8 +57,10 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
     if len(scope._timestamps) > 1:
         scope._tbt = calculate_tbt(scope._timestamps)
 
-    formatted_messages = extract_and_format_input(scope._kwargs.get("messages", ""))
-    request_model = scope._kwargs.get("model", "gpt-4o")
+    json_body = scope._kwargs.get("json", {}) or {}
+    request_model = json_body.get("model") or scope._kwargs.get("model")
+    messages = json_body.get("messages", scope._kwargs.get("messages", ""))
+    formatted_messages = extract_and_format_input(messages)
 
     cost = get_chat_model_cost(request_model, pricing_info, scope._input_tokens, scope._output_tokens)
 
@@ -252,7 +254,9 @@ def process_embedding_response(response, request_model, pricing_info, server_por
     end_time = time.time()
 
     try:
-        input_tokens = general_tokens(str(kwargs.get('prompt')))
+        json_body = kwargs.get("json", {}) or {}
+        prompt_val = json_body.get('prompt', kwargs.get('prompt', ''))
+        input_tokens = general_tokens(str(prompt_val))
 
         # Calculate cost of the operation
         cost = get_embed_model_cost(request_model,
@@ -293,7 +297,7 @@ def process_embedding_response(response, request_model, pricing_info, server_por
                 SemanticConvention.GEN_AI_SYSTEM: SemanticConvention.GEN_AI_SYSTEM_OLLAMA
             },
             body={
-                **({"content": kwargs.get('prompt', '')} if capture_message_content else {}),
+                **({"content": prompt_val} if capture_message_content else {}),
                 "role":  'user'
             }
         )


Reviews:
user: login: sourcery-ai[bot]
body: Hey @minimAluminiumalism - I've reviewed your changes and they look great!

<details>
<summary>Here's what I looked at during the review</summary>

- 🟡 **General issues**: 3 issues found
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T06:48:26Z
user: login: amanagarwal042
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T08:42:14Z

Review comments:
diff_hunk: @@ -16,6 +17,18 @@
 
 _instruments = ("ollama >= 0.2.0",)
 
+# Record modules imported before initialization to apply patches during init
+_pending_ollama = []
path: sdk/python/src/openlit/instrumentation/ollama/__init__.py
user: login: sourcery-ai[bot]
body: **suggestion (bug_risk):** Global pending modules list isn’t protected for concurrency

Wrap `_pending_ollama` in a thread-safe container or add locking to prevent race conditions during concurrent imports or instrumentation.
created_at: 2025-05-13T06:48:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,49 +48,72 @@
         disable_metrics = kwargs.get("disable_metrics")
         version = importlib.metadata.version("ollama")
 
-        # sync chat
-        wrap_function_wrapper(
-            "ollama",
-            "chat",
-            chat(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
-        wrap_function_wrapper(
-            "ollama",
-            "Client.chat",
-            chat(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
+        # Define a function to apply wrapt patches based on initialization configuration
+        def _apply_instrumentation(module):
+            wrap_function_wrapper(
+                module, "chat",
+                chat(version, environment, application_name,
+                     tracer, event_provider, pricing_info,
+                     capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "Client.chat",
+                chat(version, environment, application_name,
+                     tracer, event_provider, pricing_info,
+                     capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "embeddings",
+                embeddings(version, environment, application_name,
+                           tracer, event_provider, pricing_info,
+                           capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "Client.embeddings",
+                embeddings(version, environment, application_name,
+                           tracer, event_provider, pricing_info,
+                           capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "AsyncClient.chat",
+                async_chat(version, environment, application_name,
+                           tracer, event_provider, pricing_info,
+                           capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "AsyncClient.embeddings",
+                async_embeddings(version, environment, application_name,
+                                 tracer, event_provider, pricing_info,
+                                 capture_message_content, metrics, disable_metrics),
+            )
 
-        # sync embeddings
-        wrap_function_wrapper(
-            "ollama",
-            "embeddings",
-            embeddings(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
-        wrap_function_wrapper(
-            "ollama",
-            "Client.embeddings",
-            embeddings(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
+            # Patch any modules that did `from ollama import chat` before init
+            try:
+                wrapped_chat = getattr(module, 'chat')
+                original_chat = getattr(wrapped_chat, '__wrapped__', None)
+                if original_chat:
+                    for m in list(sys.modules.values()):
path: sdk/python/src/openlit/instrumentation/ollama/__init__.py
user: login: sourcery-ai[bot]
body: **suggestion (performance):** Scanning all loaded modules on each patch may be costly

Consider caching the module lookup or limiting scans to only modules that imported chat or embeddings to avoid iterating over all of sys.modules.
created_at: 2025-05-13T06:48:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,49 +48,72 @@
         disable_metrics = kwargs.get("disable_metrics")
         version = importlib.metadata.version("ollama")
 
-        # sync chat
-        wrap_function_wrapper(
-            "ollama",
-            "chat",
-            chat(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
-        wrap_function_wrapper(
-            "ollama",
-            "Client.chat",
-            chat(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
+        # Define a function to apply wrapt patches based on initialization configuration
+        def _apply_instrumentation(module):
+            wrap_function_wrapper(
+                module, "chat",
+                chat(version, environment, application_name,
+                     tracer, event_provider, pricing_info,
+                     capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "Client.chat",
+                chat(version, environment, application_name,
+                     tracer, event_provider, pricing_info,
+                     capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "embeddings",
+                embeddings(version, environment, application_name,
+                           tracer, event_provider, pricing_info,
+                           capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "Client.embeddings",
+                embeddings(version, environment, application_name,
+                           tracer, event_provider, pricing_info,
+                           capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "AsyncClient.chat",
+                async_chat(version, environment, application_name,
+                           tracer, event_provider, pricing_info,
+                           capture_message_content, metrics, disable_metrics),
+            )
+            wrap_function_wrapper(
+                module, "AsyncClient.embeddings",
+                async_embeddings(version, environment, application_name,
+                                 tracer, event_provider, pricing_info,
+                                 capture_message_content, metrics, disable_metrics),
+            )
 
-        # sync embeddings
-        wrap_function_wrapper(
-            "ollama",
-            "embeddings",
-            embeddings(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
-        wrap_function_wrapper(
-            "ollama",
-            "Client.embeddings",
-            embeddings(version, environment, application_name,
-                  tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
-        )
+            # Patch any modules that did `from ollama import chat` before init
+            try:
+                wrapped_chat = getattr(module, 'chat')
+                original_chat = getattr(wrapped_chat, '__wrapped__', None)
+                if original_chat:
+                    for m in list(sys.modules.values()):
+                        if getattr(m, 'chat', None) is original_chat:
+                            setattr(m, 'chat', wrapped_chat)
+            except Exception:
+                pass
path: sdk/python/src/openlit/instrumentation/ollama/__init__.py
user: login: sourcery-ai[bot]
body: **suggestion:** Avoid broad exception catch

Restrict the catch to specific exceptions (e.g., AttributeError) to avoid hiding unexpected errors.

```suggestion
            except AttributeError:
                pass
```
created_at: 2025-05-13T06:48:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -16,6 +17,18 @@
 
 _instruments = ("ollama >= 0.2.0",)
 
+# Record modules imported before initialization to apply patches during init
+_pending_ollama = []
+
+def _record_module(module):
+    _pending_ollama.append(module)
+
+# Record the module whenever ollama is imported
+when_imported("ollama")(_record_module)
+_existing_mod = sys.modules.get("ollama")
+if _existing_mod:
path: sdk/python/src/openlit/instrumentation/ollama/__init__.py
user: login: sourcery-ai[bot]
body: **suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))

```suggestion
if _existing_mod := sys.modules.get("ollama"):
```
created_at: 2025-05-13T06:48:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: sourcery-ai[bot]
created_at: 2025-05-13T06:43:35Z
body: <!-- Generated by sourcery-ai[bot]: start review_guide -->

## Reviewer's Guide

This PR enhances the Ollama instrumentor to reliably patch both sync and async chat/embeddings methods even when modules are imported before initialization by recording pending imports, refactoring wrap logic into a helper, and retroactively replacing symbol-imported functions.

#### Sequence Diagram: Ollama Instrumentation for Pre-Initialization Imports

```mermaid
sequenceDiagram
    actor UserCode
    participant System as "Python Environment / sys.modules"
    participant OpenLitHook as "OpenLit Import Hook (when_imported)"
    participant PendingList as "_pending_ollama_list"
    participant Instrumentor as "OllamaInstrumentor"
    participant OllamaModule as "ollama_module instance"

    Note over UserCode, OllamaModule: Scenario: 'ollama' is imported before 'openlit.init()'

    UserCode ->> System: import ollama (or from ollama import chat)
    activate System
    System -->> OpenLitHook: 'ollama' module loaded, hook triggered
    deactivate System
    activate OpenLitHook
    OpenLitHook ->> PendingList: _record_module(OllamaModule): Add module
    deactivate OpenLitHook

    UserCode ->> Instrumentor: openlit.init() calls OllamaInstrumentor._instrument()
    activate Instrumentor
    Instrumentor ->> Instrumentor: Define nested _apply_instrumentation(module) helper
    Instrumentor ->> PendingList: For each 'mod' in _pending_ollama:
    activate PendingList
    PendingList -->> Instrumentor: Get OllamaModule
    deactivate PendingList

    Instrumentor ->> OllamaModule: Call _apply_instrumentation(OllamaModule)
    activate OllamaModule
    OllamaModule ->> OllamaModule: Wrap functions (chat, embeddings, etc.)
    OllamaModule ->> System: Retroactively patch symbol imports in other modules
    deactivate OllamaModule

    Instrumentor ->> PendingList: _pending_ollama.clear()
    Instrumentor ->> OpenLitHook: Register _apply_instrumentation for future 'ollama' imports
    deactivate Instrumentor
```

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Record and defer instrumentation for modules imported before init | <ul><li>Introduced _pending_ollama list to store modules</li><li>Added _record_module callback</li><li>Hooked when_imported('ollama') to record new imports</li><li>Captured already-loaded ollama module from sys.modules</li></ul> | `sdk/python/src/openlit/instrumentation/ollama/__init__.py` |
| Refactor and extend wrapt instrumentation into a helper | <ul><li>Consolidated repeated wrap_function_wrapper calls into _apply_instrumentation</li><li>Added wrappers for embeddings and async chat/embeddings</li><li>Parameterized wrapper arguments with version, environment, tracer, etc.</li></ul> | `sdk/python/src/openlit/instrumentation/ollama/__init__.py` |
| Retroactively patch symbol-imported functions | <ul><li>Detect wrapped functions via __wrapped__ attribute</li><li>Iterate over sys.modules to replace original chat/embeddings</li><li>Wrap replacements in try/except to avoid import errors</li></ul> | `sdk/python/src/openlit/instrumentation/ollama/__init__.py` |
| Apply and clear pending instrumentation, register future imports | <ul><li>Loop over _pending_ollama to apply instrumentation and then clear it</li><li>Re-register when_imported hook for post-init imports</li></ul> | `sdk/python/src/openlit/instrumentation/ollama/__init__.py` |

---

<details>
<summary>Tips and commands</summary>

#### Interacting with Sourcery

- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.
- **Continue discussions:** Reply directly to Sourcery's review comments.
- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an
  issue from a review comment by replying to it. You can also reply to a
  review comment with `@sourcery-ai issue` to create an issue from it.
- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull
  request title to generate a title at any time. You can also comment
  `@sourcery-ai title` on the pull request to (re-)generate the title at any time.
- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in
  the pull request body to generate a PR summary at any time exactly where you
  want it. You can also comment `@sourcery-ai summary` on the pull request to
  (re-)generate the summary at any time.
- **Generate reviewer's guide:** Comment `@sourcery-ai guide` on the pull
  request to (re-)generate the reviewer's guide at any time.
- **Resolve all Sourcery comments:** Comment `@sourcery-ai resolve` on the
  pull request to resolve all Sourcery comments. Useful if you've already
  addressed all the comments and don't want to see them anymore.
- **Dismiss all Sourcery reviews:** Comment `@sourcery-ai dismiss` on the pull
  request to dismiss all existing Sourcery reviews. Especially useful if you
  want to start fresh with a new review - don't forget to comment
  `@sourcery-ai review` to trigger a new review!

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) to:
- Enable or disable review features such as the Sourcery-generated pull request
  summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.

#### Getting Help

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
author_association: CONTRIBUTOR
user: login: minimAluminiumalism
created_at: 2025-05-13T11:29:17Z
body: @patcher9 

I've changed the hook function to correctly instrument ollama even when imported funcs(`chat`, `embeddings`) prior to `openlit.init()`.

It seems that the CI/CD pipeline failure is unrelated to my changes. Can you check that?
author_association: CONTRIBUTOR