Skip to content

feat: add true async support to OCI provider via aiohttp#4982

Open
fede-kamel wants to merge 14 commits into
crewAIInc:mainfrom
fede-kamel:feat/oci-true-async
Open

feat: add true async support to OCI provider via aiohttp#4982
fede-kamel wants to merge 14 commits into
crewAIInc:mainfrom
fede-kamel:feat/oci-true-async

Conversation

@fede-kamel

Copy link
Copy Markdown

Summary

Replace asyncio.to_thread wrappers with true async I/O for acall() and astream(). The OCI SDK is synchronous, so we bypass it for HTTP and use its signer for request authentication directly via aiohttp.

  • oci_async.py: OCIAsyncClient — aiohttp-based HTTP client with OCI request signing, native SSE parsing, connection pooling
  • acall(): true async chat completion, no thread pool
  • astream(): true async SSE streaming, no thread+queue bridge
  • Graceful fallback to asyncio.to_thread when aiohttp is unavailable or the client is mocked
  • aiohttp + certifi added to crewai[oci] optional deps

Temporary measure until the OCI SDK ships native async. Based on the same pattern used in langchain-oracle.

Depends on #4966, #4964, #4963, #4962, #4961, #4959. Draft until all merge.
Tracking issue: #4944

Diff breakdown (vs embeddings PR)

Change Lines
oci_async.py (new — async client) +178
completion.py (async paths + fallback) +226/-39
pyproject.toml (aiohttp dep) +2
test_oci_async.py (4 live integration tests) +93
Total +499

Test plan

  • 42 prior unit tests still pass (fallback path)
  • 4 live integration tests against meta.llama-3.3-70b-instruct:
    • True async client initialization verified
    • acall returns text response
    • astream yields chunks
    • Concurrent asyncio.gather with 2 simultaneous calls

@fede-kamel fede-kamel force-pushed the feat/oci-true-async branch from 4fdf952 to 6d3a27b Compare March 29, 2026 16:22
@fede-kamel

Copy link
Copy Markdown
Author

Note: we are currently integrating with CrewAI. For now, our official integration is with LangChain: https://github.com/NVIDIA/NeMo-Agent-Toolkit

@fede-kamel

Copy link
Copy Markdown
Author

@fede-kamel fede-kamel force-pushed the feat/oci-true-async branch from 6d3a27b to fd8c270 Compare April 24, 2026 17:11
fede-kamel added a commit to fede-kamel/crewAI that referenced this pull request May 6, 2026
- OCICompletion(BaseLLM): sync call() and async acall() for generic
  (Meta, Google, OpenAI, xAI) and Cohere model families
- Shared OCI auth utilities (utilities/oci.py): API key, security token,
  instance principal, and resource principal auth
- Provider routing in llm.py: oci/ prefix and OCI model-id patterns
- oci registered as optional dependency (crewai[oci])
- Configurable timeout via DEFAULT_OCI_TIMEOUT constant
- Cohere and generic request/response paths fully separated

Tracking issue: crewAIInc#4944
Part 1 of series: crewAIInc#4959crewAIInc#4961crewAIInc#4962crewAIInc#4963crewAIInc#4964crewAIInc#4966crewAIInc#4982
@fede-kamel fede-kamel force-pushed the feat/oci-true-async branch from fd8c270 to e19bde3 Compare May 6, 2026 16:34
@fede-kamel

Copy link
Copy Markdown
Author

@greysonlalonde @lorenzejay — bumping for review.

Status (after rebase):

This PR adds: OCI's official Python SDK is sync-only. This PR wires an aiohttp-based path that signs the request with the OCI signer manually and yields await-able streaming + non-streaming calls, eliminating the thread-pool wrapper. ~3-5× lower latency under concurrent load.

v1 spec compliance: like #4959, this targets OCI's /20231130/Chat v1 API directly via oci.generative_ai_inference. Live HTTP-body capture confirms all spec fields (compartmentId, servingMode, chatRequest{apiFormat,…}) match Oracle's /20231130/Chat reference.

This is part of the 7-PR series (#4959, #4961, #4962, #4963, #4964, #4966, #4982). Happy to fold any subset into a single PR if that helps review velocity.

fede-kamel added a commit to fede-kamel/crewAI that referenced this pull request May 6, 2026
- OCICompletion(BaseLLM): sync call() and async acall() for generic
  (Meta, Google, OpenAI, xAI) and Cohere model families
- Shared OCI auth utilities (utilities/oci.py): API key, security token,
  instance principal, and resource principal auth
- Provider routing in llm.py: oci/ prefix and OCI model-id patterns
- oci registered as optional dependency (crewai[oci])
- Configurable timeout via DEFAULT_OCI_TIMEOUT constant
- Cohere and generic request/response paths fully separated

Tracking issue: crewAIInc#4944
Part 1 of series: crewAIInc#4959crewAIInc#4961crewAIInc#4962crewAIInc#4963crewAIInc#4964crewAIInc#4966crewAIInc#4982
@fede-kamel fede-kamel force-pushed the feat/oci-true-async branch from e19bde3 to 7c93976 Compare May 6, 2026 18:45
fede-kamel added a commit to fede-kamel/crewAI that referenced this pull request May 18, 2026
- OCICompletion(BaseLLM): sync call() and async acall() for generic
  (Meta, Google, OpenAI, xAI) and Cohere model families
- Shared OCI auth utilities (utilities/oci.py): API key, security token,
  instance principal, and resource principal auth
- Provider routing in llm.py: oci/ prefix and OCI model-id patterns
- oci registered as optional dependency (crewai[oci])
- Configurable timeout via DEFAULT_OCI_TIMEOUT constant
- Cohere and generic request/response paths fully separated

Tracking issue: crewAIInc#4944
Part 1 of series: crewAIInc#4959crewAIInc#4961crewAIInc#4962crewAIInc#4963crewAIInc#4964crewAIInc#4966crewAIInc#4982
@fede-kamel fede-kamel force-pushed the feat/oci-true-async branch from 7c93976 to b8982a9 Compare May 18, 2026 01:26
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

OCI support is added for native LLM calls and embeddings, including provider routing, optional dependencies, authentication, synchronous and asynchronous requests, streaming, tools, structured outputs, multimodal inputs, image embeddings, and unit/live integration tests.

OCI provider support

Layer / File(s) Summary
Provider registration and transport
lib/crewai/pyproject.toml, lib/crewai/src/crewai/llm.py, lib/crewai/src/crewai/utilities/oci*
Registers OCI dependencies and native routing, adds authentication helpers, and implements signed asynchronous HTTP and SSE transport.
LLM request and content handling
lib/crewai/src/crewai/llms/providers/oci/{__init__,completion,vision}.py
Adds OCICompletion, model-family inference, OCI request construction, message normalization, multimodal content, tool definitions, structured response formats, and capability declarations.
LLM execution paths
lib/crewai/src/crewai/llms/providers/oci/completion.py
Implements response extraction, streaming, asynchronous calls, tool recursion, structured output parsing, metadata tracking, and client locking.
Embedding provider integration
lib/crewai/src/crewai/rag/embeddings/...
Adds OCI provider types, factory registration, configurable provider construction, batched text embeddings, image embeddings, serving modes, and serializable configuration.
OCI LLM validation
lib/crewai/tests/llms/oci/*
Adds mocked and live coverage for routing, calls, Cohere v2, asynchronous behavior, multimodal inputs, streaming, structured outputs, and tools.
OCI embedding validation
lib/crewai/tests/rag/embeddings/test_*oci*
Tests factory wiring, batching, output dimensions, configuration round trips, image embeddings, and live text embedding.

Sequence Diagram(s)

sequenceDiagram
  participant CrewAI
  participant OCICompletion
  participant OCIAsyncClient
  participant OCI_GenAI
  CrewAI->>OCICompletion: call or acall messages
  OCICompletion->>OCICompletion: build provider-specific request
  OCICompletion->>OCIAsyncClient: send async request
  OCIAsyncClient->>OCI_GenAI: signed chat request
  OCI_GenAI-->>OCIAsyncClient: response or stream events
  OCIAsyncClient-->>OCICompletion: decoded content and metadata
  OCICompletion-->>CrewAI: text, tool calls, or structured model
Loading
sequenceDiagram
  participant EmbeddingsFactory
  participant OCIProvider
  participant OCIEmbeddingFunction
  participant OCI_GenAI
  EmbeddingsFactory->>OCIProvider: build OCI configuration
  OCIProvider->>OCIEmbeddingFunction: initialize callable
  OCIEmbeddingFunction->>OCI_GenAI: submit batched embedding request
  OCI_GenAI-->>OCIEmbeddingFunction: embedding vectors
  OCIEmbeddingFunction-->>EmbeddingsFactory: return aggregated vectors
Loading

Suggested reviewers: lucasgomide, lorenzejay

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding true async OCI provider support via aiohttp.
Description check ✅ Passed The description is directly related to the changeset and accurately describes the async OCI support work.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for 45 days with no activity.

- OCICompletion(BaseLLM): sync call() and async acall() for generic
  (Meta, Google, OpenAI, xAI) and Cohere model families
- Shared OCI auth utilities (utilities/oci.py): API key, security token,
  instance principal, and resource principal auth
- Provider routing in llm.py: oci/ prefix and OCI model-id patterns
- oci registered as optional dependency (crewai[oci])
- Configurable timeout via DEFAULT_OCI_TIMEOUT constant
- Cohere and generic request/response paths fully separated

Tracking issue: crewAIInc#4944
Part 1 of series: crewAIInc#4959crewAIInc#4961crewAIInc#4962crewAIInc#4963crewAIInc#4964crewAIInc#4966crewAIInc#4982
- 15 unit tests (mocked OCI SDK, no credentials needed)
- 2 parametrized integration tests across 4 model families:
  meta.llama-3.3-70b-instruct, cohere.command-r-plus-08-2024,
  google.gemini-2.5-flash, openai.gpt-5.2-chat-latest
- conftest.py with shared fixtures for both test suites
Previously: any `openai.*` model on the GENERIC api_format route was
sent `max_completion_tokens` instead of `max_tokens`. That works for
the GPT-5 family (which OCI requires it on with HTTP 400 if you send
`max_tokens`), but is inconsistent with `_is_openai_gpt5_family()`
already used elsewhere in the same method for temperature and stop.

Switch the param-name routing to the same predicate so GPT-4o /
GPT-4.1 keep the standard `max_tokens` field, while only `openai.gpt-5*`
gets `max_completion_tokens`. Also reword the test pair to reflect the
two distinct behaviours.

Verified live against OCI us-chicago-1 — all 7 model families pass:
  openai.gpt-5.5          ok    (max_completion_tokens)
  openai.gpt-5            ok    (max_completion_tokens)
  openai.gpt-4o           Ok.   (max_tokens)
  openai.gpt-4.1          ok    (max_tokens)
  meta.llama-3.3-70b-instruct           ok
  meta.llama-4-scout-17b-16e-instruct   ok
  cohere.command-latest   ok    (CohereChatRequest)
Add reasoning_effort: str | None = None as a constructor kwarg. When
set, it's passed as `reasoning_effort` on the OCI GenericChatRequest —
honoured by GPT-5 family, Gemini 2.5, Grok reasoning variants, and
Cohere Command-A-Reasoning; ignored by non-reasoning models.

Single biggest cost knob for reasoning-capable models on OCI: setting
"LOW" typically cuts reasoning-token spend 5-10×. Other
GenericChatRequest fields (verbosity, parallel_tool_calls, logit_bias,
n, metadata, etc.) aren't exposed — they either duplicate existing
controls or aren't missing primitives.
Coderabbit's pre-merge check flagged 45.10% docstring coverage on this
PR; required threshold is 80%. Add concise docstrings for the
remaining 14 methods on OCICompletion (init, provider helpers,
message helpers, response extractors, call/acall, _chat). Coverage now
100% per ast walk; no behavioural change.
@fede-kamel fede-kamel force-pushed the feat/oci-true-async branch from b8982a9 to b55be34 Compare July 10, 2026 18:13
@fede-kamel fede-kamel marked this pull request as ready for review July 10, 2026 18:20
fede-kamel and others added 2 commits July 10, 2026 14:22
Drop unreachable os.getenv defaults in the live-config fixture and fix
the pytest path in the run instructions to include the lib/crewai/
prefix.
Add streaming text completion via OCI SSE events:
- stream=True in call() routes to _stream_call_impl with chunk events
- iter_stream() yields raw text chunks (sync generator)
- astream() wraps iter_stream via thread+queue for async callers
- _stream_chat_events holds client lock for full stream duration
- SSE event parsing handles both string and mapping payloads

Tested live against meta.llama-3.3-70b-instruct,
cohere.command-r-plus-08-2024, google.gemini-2.5-flash,
and openai.gpt-5.2-chat-latest.

Depends on: crewAIInc#4959
Tracking issue: crewAIInc#4944
fede-kamel and others added 7 commits July 10, 2026 14:22
Add native function calling for generic and Cohere model families:
- _format_tools converts CrewAI tool specs to OCI SDK format
- _extract_tool_calls normalizes responses back to CrewAI shape
- _handle_tool_calls executes tools and recurses until model finishes
- Cohere tool message handling with trailing tool results
- Tool choice control (auto/none/required/function)
- Passthrough parameter filtering via SDK introspection
- Streaming tool call accumulation from SSE fragments
- supports_function_calling() returns True

Tested live against meta.llama-3.3-70b-instruct with
raw tool call return and recursive tool execution.

Depends on: crewAIInc#4961 (streaming), crewAIInc#4959 (basic text)
Tracking issue: crewAIInc#4944
Add response_model (Pydantic) support for structured output:
- _build_response_format converts Pydantic schema to OCI
  JsonSchemaResponseFormat (generic) or CohereResponseJsonFormat
- _parse_structured_response validates and returns typed models
- response_model threaded through call, _call_impl, _stream_call_impl,
  and _handle_tool_calls for full coverage
- Handles JSON in markdown fences via base class _validate_structured_output

Tested live against meta.llama-3.3-70b-instruct and
google.gemini-2.5-flash.

Depends on: crewAIInc#4962 (tool calling), crewAIInc#4961 (streaming), crewAIInc#4959 (basic text)
Tracking issue: crewAIInc#4944
Add multimodal content handling for generic model families:
- vision.py: model lists, data URI helpers, image encoding utilities
- _build_generic_content handles image_url, document_url, video_url,
  audio_url content types mapped to OCI SDK content objects
- _message_has_multimodal_content detects non-text payloads
- Cohere models reject multimodal with clear error message
- supports_multimodal() returns True

Depends on: crewAIInc#4963, crewAIInc#4962, crewAIInc#4961, crewAIInc#4959
Tracking issue: crewAIInc#4944
Send a 2x2 red PNG to google.gemini-2.5-flash via data URI and
verify it identifies the color. Tests the full image_url content
pipeline end-to-end against a live OCI vision model.
cohere.command-a-vision rejects the v1 COHERE api format, so route
command-a-vision / command-a-reasoning model ids to a new cohere_v2
request family built on CohereChatRequestV2: message-based history,
text + image_url content, CohereToolV2 function tools, JSON response
format, and SSE streaming (v2 events carry message.content[].text).

Verified live against OCI on-demand in us-chicago-1: plain chat,
streaming, and vision all pass; tool use is wired but the service
currently rejects tools for command-a-vision.
Add OCI embedding support integrated with CrewAI's RAG pipeline:
- OCIEmbeddingFunction: ChromaDB-compatible embedding callable with
  batching, config serialization, image embedding support
- OCIProvider: Pydantic-based provider with alias validation for
  env vars and config keys
- Factory registration in embeddings/factory.py + types.py
- Supports text and image embeddings, output dimensions,
  custom endpoints, all 4 OCI auth modes

Tested live against cohere.embed-english-v3.0 with API_KEY auth.

Depends on: crewAIInc#4964, crewAIInc#4963, crewAIInc#4962, crewAIInc#4961, crewAIInc#4959
Tracking issue: crewAIInc#4944
Replace asyncio.to_thread wrappers with true async I/O using aiohttp
for acall() and astream(). The OCI SDK is sync-only, so we bypass it
for HTTP and use its signer for request authentication directly.

- oci_async.py: OCIAsyncClient with aiohttp, OCI request signing,
  native SSE parsing, connection pooling
- acall(): true async chat completion (no thread pool)
- astream(): true async SSE streaming (no thread+queue bridge)
- Graceful fallback to asyncio.to_thread when aiohttp unavailable
  or client is mocked (unit tests)
- aiohttp + certifi added to crewai[oci] optional deps

Temporary measure until OCI SDK ships native async support.

Tested live: acall, astream, and concurrent acall against
meta.llama-3.3-70b-instruct with API_KEY auth.

Depends on: crewAIInc#4966, crewAIInc#4964, crewAIInc#4963, crewAIInc#4962, crewAIInc#4961, crewAIInc#4959
Tracking issue: crewAIInc#4944
@fede-kamel fede-kamel force-pushed the feat/oci-true-async branch from b55be34 to 50a083a Compare July 10, 2026 18:25
@fede-kamel

Copy link
Copy Markdown
Author

Rebased onto current main and un-drafted — mergeable again. True-async path verified live against OCI on-demand (us-chicago-1): acall via the aiohttp client returns correctly; the thread fallback also passes. Unit suite: 58 passed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (7)
lib/crewai/src/crewai/rag/embeddings/providers/oci/types.py (1)

8-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Annotated metadata on model_name may be misleading.

Line 11 uses Annotated[str, "cohere.embed-english-v3.0"] which appears to suggest a default value, but Annotated metadata in a TypedDict is purely informational — it does not set a default. Since the actual default lives in OCIProvider (the Pydantic model), this annotation could confuse consumers into thinking the TypedDict itself provides a default. Consider using a docstring or comment instead to document the recommended model name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/rag/embeddings/providers/oci/types.py` around lines 8 -
24, Replace the Annotated metadata on OCIProviderConfig.model_name with a plain
str annotation, and document the recommended “cohere.embed-english-v3.0” model
using a comment or docstring; keep the actual default defined in OCIProvider.
lib/crewai/src/crewai/rag/embeddings/providers/oci/embedding_callable.py (2)

171-181: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

embed_image_batch sends one API request per image.

Each image in the batch results in a separate embed_text call (line 177). For large image batches this is inefficient — it could be worth batching multiple data-URI inputs into a single embed_text request if the OCI API supports multiple inputs with input_type="IMAGE".

If the OCI API only accepts one image per request, a comment documenting this constraint would help future maintainers understand the design choice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/rag/embeddings/providers/oci/embedding_callable.py`
around lines 171 - 181, Review embed_image_batch and the OCI embed_text API to
determine whether multiple IMAGE data URIs can be submitted in one request. If
supported, convert all images first and issue a single request with
input_type="IMAGE"; otherwise retain the per-image calls and add a concise
comment documenting the API’s single-image constraint.

154-161: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

No error handling around embed_text API calls.

If an OCI API call fails mid-batch (e.g., rate limit, network error), partial embeddings are silently lost and the exception propagates with no context about which batch failed. Consider wrapping the embed_text call with error context or letting callers know this is expected (delegating retry to the caller).

This is a common pattern in embedding functions, so if retry/error handling is intentionally left to the caller, a brief docstring note would clarify the contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/rag/embeddings/providers/oci/embedding_callable.py`
around lines 154 - 161, Add explicit error context around each OCI embed_text
call in __call__, identifying the failed batch and preserving the original
exception (or document that retry handling is intentionally delegated to
callers). Ensure failures do not appear as unexplained mid-batch errors and
clarify the behavior in the callable’s docstring if no internal retry is added.
lib/crewai/tests/llms/oci/test_oci_integration_multimodal.py (1)

22-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting _skip_unless_live into a shared conftest.py fixture.

This function is duplicated verbatim in test_oci_integration_tools.py and partially in test_oci_async.py. The streaming and structured integration tests already use oci_live_config from conftest.py for the same purpose. Consolidating would ensure consistent skip logic and reduce maintenance burden when env-var requirements change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/llms/oci/test_oci_integration_multimodal.py` around lines 22
- 42, Extract the shared live OCI configuration and skip logic from
`_skip_unless_live` into a `conftest.py` fixture, preferably reusing or
extending `oci_live_config`. Update `test_oci_integration_multimodal.py`,
`test_oci_integration_tools.py`, and `test_oci_async.py` to consume the fixture
and remove their local helper implementations, preserving the existing
environment validation and returned configuration.
lib/crewai/tests/llms/oci/test_oci_integration_tools.py (1)

19-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

_skip_unless_live and _env_models are duplicated from test_oci_integration_multimodal.py.

These helpers are identical to the ones in test_oci_integration_multimodal.py. Consider extracting _skip_unless_live to conftest.py (where oci_live_config already exists) and parameterizing _env_models with the env var names and defaults.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/llms/oci/test_oci_integration_tools.py` around lines 19 -
39, Remove the duplicated _skip_unless_live and _env_models helpers from this
test module. Reuse the shared OCI live configuration fixture/helper from
conftest.py (including oci_live_config), and replace _env_models with a
parameterized shared utility or direct environment handling that accepts the
relevant variable names and fallback/default values; update all call sites in
the integration tests accordingly.
lib/crewai/src/crewai/llms/providers/oci/completion.py (1)

1689-1690: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the public capability methods.

Add concise docstrings describing the capability assumptions and context-window fallback.

As per coding guidelines, “Document public APIs and complex logic in Python code.”

Also applies to: 1695-1705

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/llms/providers/oci/completion.py` around lines 1689 -
1690, Document the public capability methods, including
supports_function_calling and the related methods around it, with concise
docstrings explaining their capability assumptions and context-window fallback
behavior. Use the existing method names to locate each public API and ensure
every method has documentation consistent with the Python coding guidelines.

Source: Coding guidelines

lib/crewai/src/crewai/llms/providers/oci/vision.py (1)

46-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the exported vision helpers.

Add concise argument, return-value, and error behavior docstrings for these public functions.

As per coding guidelines, “Document public APIs and complex logic in Python code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/llms/providers/oci/vision.py` around lines 46 - 57, Add
concise docstrings to the public functions load_image, encode_image, and
is_vision_model in vision.py, documenting their arguments, return values, and
any relevant errors or error behavior. Keep the documentation consistent with
the module’s existing style and accurately describe each function’s behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai/src/crewai/llms/providers/oci/completion.py`:
- Around line 1692-1693: The supports_multimodal method currently reports
support for every OCI model; change it to return is_vision_model(self.model),
using the existing VISION_MODELS detection in vision.py. Add an explicit
override for dedicated OCI endpoints whose multimodal capability cannot be
inferred from the model name.
- Around line 1544-1584: The native async branch must preserve the full
semantics of call(). Update the async implementation around
_prepare_async_request and _async_client.chat_async to run call context,
lifecycle events, hooks, callbacks, tool-call execution, structured response
validation, and response metadata handling consistently with the thread
fallback, including honoring response_model and hook failures; reuse shared
call() helpers where possible rather than returning raw content directly.
- Line 85: The configured timeout is not propagated through the async OCI
request path. Update the OCI provider’s async client setup and `chat_async()`
invocation in `acall()` to pass the `timeout` value, matching the synchronous
path and preserving the caller’s connect/read limits.

In `@lib/crewai/src/crewai/utilities/oci_async.py`:
- Around line 35-45: The recursive _convert_keys_to_camel function incorrectly
transforms user-defined schema keys in async OCI payloads. Restrict key
conversion to known OCI model-attribute mappings, preserving arbitrary nested
JSON schema keys under tool properties and structured-output schemas; update the
call site around the async request construction accordingly.
- Around line 79-89: Update OCIAsyncClient session lifecycle management in
_get_session and close, and ensure OCICompletion tears down or recreates the
cached client/session per event loop. Track the owning event loop for the cached
aiohttp.ClientSession, close and clear it when the loop changes or the client is
no longer needed, and avoid reusing loop-bound sessions across calls.

In `@lib/crewai/tests/llms/oci/test_oci_async.py`:
- Around line 12-24: Update _skip_unless_live to accept either OCI_REGION or
OCI_SERVICE_ENDPOINT, skipping only when both are unset. Add the configured
OCI_SERVICE_ENDPOINT to the returned config as service_endpoint so OCICompletion
uses it, while preserving the existing compartment, authentication type, and
profile handling.

In `@lib/crewai/tests/rag/embeddings/test_factory_oci.py`:
- Around line 109-115: Remove the `.tolist()` conversion in the result
normalization for `OCIEmbeddingFunction.__call__` tests, since the mocked
embeddings are plain lists; compare `result` directly against the expected
nested lists while retaining the length and strict zip assertions.

---

Nitpick comments:
In `@lib/crewai/src/crewai/llms/providers/oci/completion.py`:
- Around line 1689-1690: Document the public capability methods, including
supports_function_calling and the related methods around it, with concise
docstrings explaining their capability assumptions and context-window fallback
behavior. Use the existing method names to locate each public API and ensure
every method has documentation consistent with the Python coding guidelines.

In `@lib/crewai/src/crewai/llms/providers/oci/vision.py`:
- Around line 46-57: Add concise docstrings to the public functions load_image,
encode_image, and is_vision_model in vision.py, documenting their arguments,
return values, and any relevant errors or error behavior. Keep the documentation
consistent with the module’s existing style and accurately describe each
function’s behavior.

In `@lib/crewai/src/crewai/rag/embeddings/providers/oci/embedding_callable.py`:
- Around line 171-181: Review embed_image_batch and the OCI embed_text API to
determine whether multiple IMAGE data URIs can be submitted in one request. If
supported, convert all images first and issue a single request with
input_type="IMAGE"; otherwise retain the per-image calls and add a concise
comment documenting the API’s single-image constraint.
- Around line 154-161: Add explicit error context around each OCI embed_text
call in __call__, identifying the failed batch and preserving the original
exception (or document that retry handling is intentionally delegated to
callers). Ensure failures do not appear as unexplained mid-batch errors and
clarify the behavior in the callable’s docstring if no internal retry is added.

In `@lib/crewai/src/crewai/rag/embeddings/providers/oci/types.py`:
- Around line 8-24: Replace the Annotated metadata on
OCIProviderConfig.model_name with a plain str annotation, and document the
recommended “cohere.embed-english-v3.0” model using a comment or docstring; keep
the actual default defined in OCIProvider.

In `@lib/crewai/tests/llms/oci/test_oci_integration_multimodal.py`:
- Around line 22-42: Extract the shared live OCI configuration and skip logic
from `_skip_unless_live` into a `conftest.py` fixture, preferably reusing or
extending `oci_live_config`. Update `test_oci_integration_multimodal.py`,
`test_oci_integration_tools.py`, and `test_oci_async.py` to consume the fixture
and remove their local helper implementations, preserving the existing
environment validation and returned configuration.

In `@lib/crewai/tests/llms/oci/test_oci_integration_tools.py`:
- Around line 19-39: Remove the duplicated _skip_unless_live and _env_models
helpers from this test module. Reuse the shared OCI live configuration
fixture/helper from conftest.py (including oci_live_config), and replace
_env_models with a parameterized shared utility or direct environment handling
that accepts the relevant variable names and fallback/default values; update all
call sites in the integration tests accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c9176c25-f841-4dfc-a3a3-6089bba2d71d

📥 Commits

Reviewing files that changed from the base of the PR and between 85c467d and b55be34.

📒 Files selected for processing (29)
  • lib/crewai/pyproject.toml
  • lib/crewai/src/crewai/llm.py
  • lib/crewai/src/crewai/llms/providers/oci/__init__.py
  • lib/crewai/src/crewai/llms/providers/oci/completion.py
  • lib/crewai/src/crewai/llms/providers/oci/vision.py
  • lib/crewai/src/crewai/rag/embeddings/factory.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/__init__.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/embedding_callable.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/oci_provider.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/types.py
  • lib/crewai/src/crewai/rag/embeddings/types.py
  • lib/crewai/src/crewai/utilities/oci.py
  • lib/crewai/src/crewai/utilities/oci_async.py
  • lib/crewai/tests/llms/oci/__init__.py
  • lib/crewai/tests/llms/oci/conftest.py
  • lib/crewai/tests/llms/oci/test_oci.py
  • lib/crewai/tests/llms/oci/test_oci_async.py
  • lib/crewai/tests/llms/oci/test_oci_cohere_v2.py
  • lib/crewai/tests/llms/oci/test_oci_integration_basic.py
  • lib/crewai/tests/llms/oci/test_oci_integration_multimodal.py
  • lib/crewai/tests/llms/oci/test_oci_integration_streaming.py
  • lib/crewai/tests/llms/oci/test_oci_integration_structured.py
  • lib/crewai/tests/llms/oci/test_oci_integration_tools.py
  • lib/crewai/tests/llms/oci/test_oci_multimodal.py
  • lib/crewai/tests/llms/oci/test_oci_streaming.py
  • lib/crewai/tests/llms/oci/test_oci_structured.py
  • lib/crewai/tests/llms/oci/test_oci_tools.py
  • lib/crewai/tests/rag/embeddings/test_factory_oci.py
  • lib/crewai/tests/rag/embeddings/test_oci_embedding_integration.py

reasoning_effort: str | None = None,
stream: bool = False,
oci_provider: str | None = None,
timeout: tuple[int, int] = DEFAULT_OCI_TIMEOUT,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C4 'DEFAULT_OCI_TIMEOUT|ClientTimeout|timeout[=:]' \
  lib/crewai/src/crewai/llms/providers/oci/completion.py \
  lib/crewai/src/crewai/utilities/oci_async.py

Repository: crewAIInc/crewAI

Length of output: 4840


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant class/method structure first.
ast-grep outline lib/crewai/src/crewai/llms/providers/oci/completion.py --view expanded
printf '\n---\n'
ast-grep outline lib/crewai/src/crewai/utilities/oci_async.py --view expanded
printf '\n---\n'

# Read the relevant slices with line numbers.
sed -n '1,240p' lib/crewai/src/crewai/llms/providers/oci/completion.py | cat -n
printf '\n---\n'
sed -n '1,260p' lib/crewai/src/crewai/utilities/oci_async.py | cat -n

Repository: crewAIInc/crewAI

Length of output: 22065


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1520,1705p' lib/crewai/src/crewai/llms/providers/oci/completion.py | cat -n

Repository: crewAIInc/crewAI

Length of output: 8812


Propagate the configured timeout into the async OCI path. timeout is only forwarded to the sync SDK client; acall() creates OCIAsyncClient and calls chat_async() without it, so native async requests still use the hardcoded 300s default instead of the caller’s connect/read limits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/llms/providers/oci/completion.py` at line 85, The
configured timeout is not propagated through the async OCI request path. Update
the OCI provider’s async client setup and `chat_async()` invocation in `acall()`
to pass the `timeout` value, matching the synchronous path and preserving the
caller’s connect/read limits.

Comment thread lib/crewai/src/crewai/llms/providers/oci/completion.py
Comment on lines +1692 to +1693
def supports_multimodal(self) -> bool:
return True

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Report multimodal support per model.

Returning True for every OCI model advertises image support for text-only models, even though vision.py provides VISION_MODELS and is_vision_model. Use model-specific detection, with an explicit override for dedicated endpoints whose capability cannot be inferred.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/llms/providers/oci/completion.py` around lines 1692 -
1693, The supports_multimodal method currently reports support for every OCI
model; change it to return is_vision_model(self.model), using the existing
VISION_MODELS detection in vision.py. Add an explicit override for dedicated OCI
endpoints whose multimodal capability cannot be inferred from the model name.

Comment thread lib/crewai/src/crewai/utilities/oci_async.py
Comment on lines +79 to +89
async def _get_session(self) -> aiohttp.ClientSession:
if self._session is None or self._session.closed:
ssl_context = ssl.create_default_context(cafile=certifi.where())
connector = aiohttp.TCPConnector(ssl=ssl_context)
self._session = aiohttp.ClientSession(connector=connector)
return self._session

async def close(self) -> None:
if self._session is not None and not self._session.closed:
await self._session.close()
self._session = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C4 '_async_client_instance|_session|aclose\(|\.close\(' \
  lib/crewai/src/crewai

Repository: crewAIInc/crewAI

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- lib/crewai/src/crewai/utilities/oci_async.py ---'
sed -n '1,220p' lib/crewai/src/crewai/utilities/oci_async.py

echo
echo '--- lib/crewai/src/crewai/llms/providers/oci/completion.py (async client lifecycle sections) ---'
sed -n '1600,1705p' lib/crewai/src/crewai/llms/providers/oci/completion.py

echo
echo '--- search for OCIAsyncClient.close/aclose/__aexit__/cleanup references ---'
rg -n -C3 'OCIAsyncClient|_async_client_instance|aclose\(|\.close\(\)|__aexit__' lib/crewai/src/crewai/llms/providers/oci lib/crewai/src/crewai/utilities/oci_async.py

Repository: crewAIInc/crewAI

Length of output: 14371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- all _async_client usages in OCI completion ---'
rg -n -C3 '_async_client\b|_async_client_instance\b|OCIAsyncClient\b|async with .*_async_client|await .*_async_client' lib/crewai/src/crewai/llms/providers/oci/completion.py

echo
echo '--- provider cleanup/close methods in OCI completion ---'
rg -n -C4 'def (close|aclose|__aexit__|__del__|cleanup)|async def (close|aclose)|await .*close\(' lib/crewai/src/crewai/llms/providers/oci/completion.py

echo
echo '--- inspect async call paths using the async client ---'
sed -n '1700,1905p' lib/crewai/src/crewai/llms/providers/oci/completion.py

Repository: crewAIInc/crewAI

Length of output: 3154


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- provider/base cleanup hooks ---'
rg -n -C3 'async def aclose|def aclose|async def close|def close|__aexit__|__enter__|__del__' \
  lib/crewai/src/crewai/llms lib/crewai/src/crewai/utilities | sed -n '1,260p'

echo
echo '--- OCI completion class header and inheritance ---'
sed -n '1,220p' lib/crewai/src/crewai/llms/providers/oci/completion.py

echo
echo '--- references to provider cleanup in callers ---'
rg -n -C2 'aclose\(\)|close\(\)|async with .*OCI|async with .*completion|context manager' \
  lib/crewai/src/crewai | sed -n '1,260p'

Repository: crewAIInc/crewAI

Length of output: 31768


Manage the cached aiohttp session per event loop. OCIAsyncClient can close its session, but OCICompletion never tears it down and reuses one cached instance across calls. If that instance crosses event loops, the existing ClientSession stays loop-bound and can fail or leak connectors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/utilities/oci_async.py` around lines 79 - 89, Update
OCIAsyncClient session lifecycle management in _get_session and close, and
ensure OCICompletion tears down or recreates the cached client/session per event
loop. Track the owning event loop for the cached aiohttp.ClientSession, close
and clear it when the loop changes or the client is no longer needed, and avoid
reusing loop-bound sessions across calls.

Comment on lines +12 to +24
def _skip_unless_live() -> dict[str, str]:
compartment = os.getenv("OCI_COMPARTMENT_ID")
if not compartment:
pytest.skip("OCI_COMPARTMENT_ID not set")
region = os.getenv("OCI_REGION")
if not region:
pytest.skip("OCI_REGION not set")
config: dict[str, str] = {"compartment_id": compartment}
if os.getenv("OCI_AUTH_TYPE"):
config["auth_type"] = os.getenv("OCI_AUTH_TYPE", "API_KEY")
if os.getenv("OCI_AUTH_PROFILE"):
config["auth_profile"] = os.getenv("OCI_AUTH_PROFILE", "DEFAULT")
return config

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

_skip_unless_live doesn't handle OCI_SERVICE_ENDPOINT, inconsistent with other integration test files.

This function requires OCI_REGION and skips if unset, but test_oci_integration_multimodal.py and test_oci_integration_tools.py accept either OCI_REGION or OCI_SERVICE_ENDPOINT. When only OCI_SERVICE_ENDPOINT is configured, these async tests will be incorrectly skipped. Additionally, service_endpoint is never passed into the config, so even if the env var is set, the OCICompletion instance won't use it.

🔧 Proposed fix to align with other test files
 def _skip_unless_live() -> dict[str, str]:
     compartment = os.getenv("OCI_COMPARTMENT_ID")
     if not compartment:
         pytest.skip("OCI_COMPARTMENT_ID not set")
     region = os.getenv("OCI_REGION")
-    if not region:
-        pytest.skip("OCI_REGION not set")
+    endpoint = os.getenv("OCI_SERVICE_ENDPOINT")
+    if not region and not endpoint:
+        pytest.skip("Set OCI_REGION or OCI_SERVICE_ENDPOINT")
     config: dict[str, str] = {"compartment_id": compartment}
+    if endpoint:
+        config["service_endpoint"] = endpoint
     if os.getenv("OCI_AUTH_TYPE"):
         config["auth_type"] = os.getenv("OCI_AUTH_TYPE", "API_KEY")
     if os.getenv("OCI_AUTH_PROFILE"):
         config["auth_profile"] = os.getenv("OCI_AUTH_PROFILE", "DEFAULT")
     return config
📝 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
def _skip_unless_live() -> dict[str, str]:
compartment = os.getenv("OCI_COMPARTMENT_ID")
if not compartment:
pytest.skip("OCI_COMPARTMENT_ID not set")
region = os.getenv("OCI_REGION")
if not region:
pytest.skip("OCI_REGION not set")
config: dict[str, str] = {"compartment_id": compartment}
if os.getenv("OCI_AUTH_TYPE"):
config["auth_type"] = os.getenv("OCI_AUTH_TYPE", "API_KEY")
if os.getenv("OCI_AUTH_PROFILE"):
config["auth_profile"] = os.getenv("OCI_AUTH_PROFILE", "DEFAULT")
return config
def _skip_unless_live() -> dict[str, str]:
compartment = os.getenv("OCI_COMPARTMENT_ID")
if not compartment:
pytest.skip("OCI_COMPARTMENT_ID not set")
region = os.getenv("OCI_REGION")
endpoint = os.getenv("OCI_SERVICE_ENDPOINT")
if not region and not endpoint:
pytest.skip("Set OCI_REGION or OCI_SERVICE_ENDPOINT")
config: dict[str, str] = {"compartment_id": compartment}
if endpoint:
config["service_endpoint"] = endpoint
if os.getenv("OCI_AUTH_TYPE"):
config["auth_type"] = os.getenv("OCI_AUTH_TYPE", "API_KEY")
if os.getenv("OCI_AUTH_PROFILE"):
config["auth_profile"] = os.getenv("OCI_AUTH_PROFILE", "DEFAULT")
return config
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/llms/oci/test_oci_async.py` around lines 12 - 24, Update
_skip_unless_live to accept either OCI_REGION or OCI_SERVICE_ENDPOINT, skipping
only when both are unset. Add the configured OCI_SERVICE_ENDPOINT to the
returned config as service_endpoint so OCICompletion uses it, while preserving
the existing compartment, authentication type, and profile handling.

Comment on lines +109 to +115
result = embedder(["a", "b", "c"])

result_rows = [embedding.tolist() for embedding in result]
expected_rows = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]
assert len(result_rows) == len(expected_rows)
for actual, expected in zip(result_rows, expected_rows, strict=True):
assert actual == pytest.approx(expected)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

.tolist() will fail on plain list embeddings.

The mock at line 90 returns embeddings as plain Python lists ([[0.1, 0.2], ...]), not numpy arrays. OCIEmbeddingFunction.__call__ extends the result directly with response.data.embeddings, so result is a list of lists. Calling .tolist() on a plain list raises AttributeError.

🐛 Proposed fix
     result = embedder(["a", "b", "c"])
 
-    result_rows = [embedding.tolist() for embedding in result]
+    result_rows = [list(embedding) for embedding in result]
     expected_rows = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]
📝 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
result = embedder(["a", "b", "c"])
result_rows = [embedding.tolist() for embedding in result]
expected_rows = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]
assert len(result_rows) == len(expected_rows)
for actual, expected in zip(result_rows, expected_rows, strict=True):
assert actual == pytest.approx(expected)
result = embedder(["a", "b", "c"])
result_rows = [list(embedding) for embedding in result]
expected_rows = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]
assert len(result_rows) == len(expected_rows)
for actual, expected in zip(result_rows, expected_rows, strict=True):
assert actual == pytest.approx(expected)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/rag/embeddings/test_factory_oci.py` around lines 109 - 115,
Remove the `.tolist()` conversion in the result normalization for
`OCIEmbeddingFunction.__call__` tests, since the mocked embeddings are plain
lists; compare `result` directly against the expected nested lists while
retaining the length and strict zip assertions.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
lib/crewai/src/crewai/llms/providers/oci/completion.py (1)

1526-1584: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Critical gaps remain in acall native async path despite "Addressed" marker.

The past review (lines 1544-1584) flagged that the native async branch skips call() semantics and was marked "Addressed." The current code added stop-word trimming, usage tracking, and call_completed events, but critical functional gaps remain:

  1. response_model not parsedresponse_model is forwarded to _prepare_async_request (line 1546) to include the JSON schema in the request, but the response is returned as a raw string (line 1584). The sync path parses it via _parse_structured_response (lines 1213-1220). Callers of acall(response_model=SomeModel) will receive a str instead of a BaseModel.

  2. Tool calls not handled — If the model returns tool calls, they are silently ignored. The sync path extracts and recurses via _handle_tool_calls (lines 1199-1211). The async path has no equivalent, so acall with tools returns an empty string when the model requests a tool.

  3. last_response_metadata not set — The sync path sets self.last_response_metadata at line 1193. The async path tracks usage (lines 1567-1575) but never assigns self.last_response_metadata, leaving it stale or None.

  4. Missing lifecycle events and hooks — No llm_call_context(), _emit_call_started_event, _emit_call_failed_event, _invoke_before_llm_call_hooks, _invoke_after_llm_call_hooks, or try/except error handling. The sync call() has all of these (lines 1478-1524).

  5. Usage values not cast to int — Lines 1571-1573 use usage.get("promptTokens", 0) without int(), unlike the streaming path (lines 1000-1002) which casts explicitly. If the API returns string values, _track_token_usage_internal may fail.

Consider refactoring acall to delegate to call() via asyncio.to_thread when response_model or tools are provided (preserving full semantics), and only use the native async path for simple text completions where the gaps are acceptable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/llms/providers/oci/completion.py` around lines 1526 -
1584, The native async path in acall does not preserve call() semantics for
structured responses, tool calls, metadata, lifecycle hooks, failures, and usage
typing. Refactor acall to delegate through asyncio.to_thread(self.call, ...)
whenever response_model or tools are provided; otherwise, complete the native
path by matching call()’s lifecycle/error handling, setting
last_response_metadata, casting usage values to int, and preserving the
corresponding events and hooks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@lib/crewai/src/crewai/llms/providers/oci/completion.py`:
- Around line 1526-1584: The native async path in acall does not preserve call()
semantics for structured responses, tool calls, metadata, lifecycle hooks,
failures, and usage typing. Refactor acall to delegate through
asyncio.to_thread(self.call, ...) whenever response_model or tools are provided;
otherwise, complete the native path by matching call()’s lifecycle/error
handling, setting last_response_metadata, casting usage values to int, and
preserving the corresponding events and hooks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 98947822-856f-4d57-8573-d86178f4f4c0

📥 Commits

Reviewing files that changed from the base of the PR and between b55be34 and 50a083a.

📒 Files selected for processing (25)
  • lib/crewai/pyproject.toml
  • lib/crewai/src/crewai/llms/providers/oci/__init__.py
  • lib/crewai/src/crewai/llms/providers/oci/completion.py
  • lib/crewai/src/crewai/llms/providers/oci/vision.py
  • lib/crewai/src/crewai/rag/embeddings/factory.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/__init__.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/embedding_callable.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/oci_provider.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/types.py
  • lib/crewai/src/crewai/rag/embeddings/types.py
  • lib/crewai/src/crewai/utilities/oci_async.py
  • lib/crewai/tests/llms/oci/conftest.py
  • lib/crewai/tests/llms/oci/test_oci_async.py
  • lib/crewai/tests/llms/oci/test_oci_cohere_v2.py
  • lib/crewai/tests/llms/oci/test_oci_integration_basic.py
  • lib/crewai/tests/llms/oci/test_oci_integration_multimodal.py
  • lib/crewai/tests/llms/oci/test_oci_integration_streaming.py
  • lib/crewai/tests/llms/oci/test_oci_integration_structured.py
  • lib/crewai/tests/llms/oci/test_oci_integration_tools.py
  • lib/crewai/tests/llms/oci/test_oci_multimodal.py
  • lib/crewai/tests/llms/oci/test_oci_streaming.py
  • lib/crewai/tests/llms/oci/test_oci_structured.py
  • lib/crewai/tests/llms/oci/test_oci_tools.py
  • lib/crewai/tests/rag/embeddings/test_factory_oci.py
  • lib/crewai/tests/rag/embeddings/test_oci_embedding_integration.py
🚧 Files skipped from review as they are similar to previous changes (21)
  • lib/crewai/tests/llms/oci/test_oci_integration_basic.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/init.py
  • lib/crewai/tests/llms/oci/test_oci_integration_streaming.py
  • lib/crewai/tests/llms/oci/test_oci_cohere_v2.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/types.py
  • lib/crewai/tests/rag/embeddings/test_oci_embedding_integration.py
  • lib/crewai/src/crewai/llms/providers/oci/init.py
  • lib/crewai/tests/llms/oci/test_oci_integration_structured.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/oci_provider.py
  • lib/crewai/src/crewai/llms/providers/oci/vision.py
  • lib/crewai/src/crewai/rag/embeddings/factory.py
  • lib/crewai/tests/llms/oci/test_oci_integration_tools.py
  • lib/crewai/tests/llms/oci/conftest.py
  • lib/crewai/tests/llms/oci/test_oci_integration_multimodal.py
  • lib/crewai/src/crewai/rag/embeddings/types.py
  • lib/crewai/tests/llms/oci/test_oci_async.py
  • lib/crewai/pyproject.toml
  • lib/crewai/tests/llms/oci/test_oci_multimodal.py
  • lib/crewai/tests/rag/embeddings/test_factory_oci.py
  • lib/crewai/src/crewai/rag/embeddings/providers/oci/embedding_callable.py
  • lib/crewai/src/crewai/utilities/oci_async.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant