feat: add tool calling support to OCI Generative AI provider#4962
feat: add tool calling support to OCI Generative AI provider#4962fede-kamel wants to merge 8 commits into
Conversation
9e9f7e4 to
a594397
Compare
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 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
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
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
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
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
|
Note: we are currently integrating with CrewAI. For now, our official integration is with LangChain: https://github.com/NVIDIA/NeMo-Agent-Toolkit |
|
OCI GenAI PR series (tracking issue: #4944)
Depends on #4961. |
a594397 to
aff1773
Compare
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 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
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
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
- 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#4959 → crewAIInc#4961 → crewAIInc#4962 → crewAIInc#4963 → crewAIInc#4964 → crewAIInc#4966 → crewAIInc#4982
02551db to
958c10a
Compare
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 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
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
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAdds OCI as a native CrewAI LLM provider with configurable authentication, generic and Cohere request handling, tool calling, streaming, lifecycle integration, optional SDK installation, and unit/live integration tests. ChangesOCI provider integration
Sequence Diagram(s)sequenceDiagram
participant CrewAI
participant OCICompletion
participant OCI SDK
participant ToolFunction
CrewAI->>OCICompletion: call messages and tools
OCICompletion->>OCI SDK: submit OCI chat request
OCI SDK-->>OCICompletion: response or stream events
OCICompletion->>ToolFunction: execute returned tool call
ToolFunction-->>OCICompletion: tool result
OCICompletion->>OCI SDK: submit follow-up chat request
OCI SDK-->>OCICompletion: final response
OCICompletion-->>CrewAI: text, chunks, or normalized tool calls
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
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
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
|
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#4959 → crewAIInc#4961 → crewAIInc#4962 → crewAIInc#4963 → crewAIInc#4964 → crewAIInc#4966 → crewAIInc#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.
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
958c10a to
afbe463
Compare
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
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
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
afbe463 to
c14ef1e
Compare
|
Rebased onto current main and un-drafted — mergeable again. Tool calling verified live against OCI on-demand (us-chicago-1): full call → tool execution → final-answer loop on |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
lib/crewai/tests/llms/oci/test_oci_streaming.py (1)
50-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd
StreamOptionsto conftest's fake OCI module to eliminate repeated setup.The
StreamOptionsmock is manually created in three streaming tests. Adding it to_make_fake_oci_moduleinconftest.pywould remove this duplication and prevent tests from silently breaking if the mock shape changes.♻️ Proposed refactor
In
conftest.py, addStreamOptionsto_make_fake_oci_module:# ChatDetails models.ChatDetails = MagicMock(side_effect=lambda **kw: MagicMock(**kw)) + # Stream options + models.StreamOptions = MagicMock(side_effect=lambda **kw: MagicMock(**kw)) + # Auth helpersThen remove the manual setup from all three streaming tests:
- # StreamOptions mock - patch_oci_module.generative_ai_inference.models.StreamOptions = MagicMock( - side_effect=lambda **kw: MagicMock(**kw) - )Also applies to: 84-86, 114-116
🤖 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_streaming.py` around lines 50 - 53, Move the shared StreamOptions mock setup into the _make_fake_oci_module fixture factory in conftest.py, defining it with the same keyword-preserving MagicMock behavior. Remove the duplicated patch_oci_module.generative_ai_inference.models.StreamOptions assignments from all three streaming tests, including the referenced sections.lib/crewai/tests/llms/oci/test_oci_integration_tools.py (1)
19-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsolidate duplicated live-config utilities into conftest fixtures.
_env_modelsand_skip_unless_liveduplicate logic fromconftest.py(_env_modelsat line 152,_skip_unless_live_configat line 158). More importantly,_skip_unless_liveis missingOCI_AUTH_FILE_LOCATIONhandling that exists in the conftest version, so live tool tests cannot run with custom auth file locations while basic and streaming tests can.Consider adding
oci_tool_modelandoci_tool_configfixtures toconftest.pythat reuse the existing_env_modelsand_skip_unless_live_configfunctions, then remove the duplicates here.♻️ Proposed refactor
In
conftest.py, add:`@pytest.fixture`( params=_env_models("OCI_TEST_TOOL_MODELS", "OCI_TEST_TOOL_MODEL", "meta.llama-3.3-70b-instruct"), ids=lambda m: m, ) def oci_tool_model(request: pytest.FixtureRequest) -> str: return request.param `@pytest.fixture`() def oci_tool_config() -> dict[str, str]: return _skip_unless_live_config()Then in
test_oci_integration_tools.py, remove_env_models,_skip_unless_live,oci_tool_model, andoci_tool_config, and use the conftest fixtures directly:-from __future__ import annotations - -import os - -import pytest - -from crewai.llms.providers.oci.completion import OCICompletion - - -def _env_models(env_var: str, fallback: str, default: str) -> list[str]: - raw = os.getenv(env_var) or os.getenv(fallback) or default - return [m.strip() for m in raw.split(",") if m.strip()] - - -def _skip_unless_live(): - 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 - - TOOL_SPEC = [-@pytest.fixture( - params=_env_models("OCI_TEST_TOOL_MODELS", "OCI_TEST_TOOL_MODEL", "meta.llama-3.3-70b-instruct"), - ids=lambda m: m, -) -def oci_tool_model(request): - return request.param - - -@pytest.fixture() -def oci_tool_config(): - return _skip_unless_live() - - def test_oci_live_tool_call_returns_raw(oci_tool_model: str, oci_tool_config: dict):🤖 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, Consolidate the duplicated OCI live-test configuration helpers into shared fixtures. In conftest.py, add the proposed oci_tool_model and oci_tool_config fixtures using _env_models and _skip_unless_live_config, preserving support for OCI_AUTH_FILE_LOCATION; then remove the local _env_models, _skip_unless_live, oci_tool_model, and oci_tool_config definitions from test_oci_integration_tools.py and update tests to consume the shared fixtures.lib/crewai/src/crewai/llms/providers/oci/completion.py (1)
475-488: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPassthrough filtering depends on OCI SDK internal/undocumented attributes.
_allowed_passthrough_request_keysintrospectsattribute_map/swagger_types(generated Swagger client internals) or falls back toinspect.signatureto decide whichadditional_paramsare safe to forward toGenericChatRequest/CohereChatRequest. These aren't part of the OCI SDK's documented public contract and could change across SDK releases (e.g.,oci>=2.168.0pin has no upper bound), silently changing which passthrough params are accepted or dropped.Worth verifying this behavior holds across the supported
ociversion range, since a future SDK release altering these generated model internals would fail silently (params dropped) rather than raising a clear error.🤖 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 475 - 488, Replace the undocumented introspection in _allowed_passthrough_request_keys with an explicit, version-stable allowlist of supported GenericChatRequest and CohereChatRequest fields, or validate against a documented OCI SDK API. Ensure unsupported additional_params are rejected or reported clearly rather than silently dropped, and add coverage for the supported OCI version range and both request classes.
🤖 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 279-345: Update _build_cohere_chat_history so mid-history tool
messages reconstruct CohereToolResult.call using the corresponding
previous_tool_calls entry keyed by tool_call_id, preserving the original name
and parameters instead of hardcoding an empty parameter map. Reuse or centralize
the existing lookup logic used by the trailing tool-result branch, and remove
any resulting duplication.
- Around line 1042-1078: The iter_stream method currently consumes
response.data.events() outside the OCI client lock. Replace the direct _chat
call and event iteration with _stream_chat_events(chat_details), ensuring the
entire SSE stream is consumed through the helper under _client_lock while
preserving text, usage, metadata handling, and final tracking behavior.
---
Nitpick comments:
In `@lib/crewai/src/crewai/llms/providers/oci/completion.py`:
- Around line 475-488: Replace the undocumented introspection in
_allowed_passthrough_request_keys with an explicit, version-stable allowlist of
supported GenericChatRequest and CohereChatRequest fields, or validate against a
documented OCI SDK API. Ensure unsupported additional_params are rejected or
reported clearly rather than silently dropped, and add coverage for the
supported OCI version range and both request classes.
In `@lib/crewai/tests/llms/oci/test_oci_integration_tools.py`:
- Around line 19-39: Consolidate the duplicated OCI live-test configuration
helpers into shared fixtures. In conftest.py, add the proposed oci_tool_model
and oci_tool_config fixtures using _env_models and _skip_unless_live_config,
preserving support for OCI_AUTH_FILE_LOCATION; then remove the local
_env_models, _skip_unless_live, oci_tool_model, and oci_tool_config definitions
from test_oci_integration_tools.py and update tests to consume the shared
fixtures.
In `@lib/crewai/tests/llms/oci/test_oci_streaming.py`:
- Around line 50-53: Move the shared StreamOptions mock setup into the
_make_fake_oci_module fixture factory in conftest.py, defining it with the same
keyword-preserving MagicMock behavior. Remove the duplicated
patch_oci_module.generative_ai_inference.models.StreamOptions assignments from
all three streaming tests, including the referenced sections.
🪄 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: e26f3745-3bab-4d23-bcf4-2b7cbcd6769c
📒 Files selected for processing (13)
lib/crewai/pyproject.tomllib/crewai/src/crewai/llm.pylib/crewai/src/crewai/llms/providers/oci/__init__.pylib/crewai/src/crewai/llms/providers/oci/completion.pylib/crewai/src/crewai/utilities/oci.pylib/crewai/tests/llms/oci/__init__.pylib/crewai/tests/llms/oci/conftest.pylib/crewai/tests/llms/oci/test_oci.pylib/crewai/tests/llms/oci/test_oci_integration_basic.pylib/crewai/tests/llms/oci/test_oci_integration_streaming.pylib/crewai/tests/llms/oci/test_oci_integration_tools.pylib/crewai/tests/llms/oci/test_oci_streaming.pylib/crewai/tests/llms/oci/test_oci_tools.py
| def iter_stream( | ||
| self, | ||
| messages: str | list[LLMMessage], | ||
| tools: list[dict[str, BaseTool]] | None = None, | ||
| callbacks: list[Any] | None = None, | ||
| available_functions: dict[str, Any] | None = None, | ||
| from_task: Task | None = None, | ||
| from_agent: Agent | None = None, | ||
| ) -> Any: | ||
| """Yield raw text chunks from OCI without triggering tool recursion.""" | ||
| normalized_messages = self._normalize_messages(messages) | ||
| chat_request = self._build_chat_request(normalized_messages, is_stream=True) | ||
| chat_details = self._oci.generative_ai_inference.models.ChatDetails( | ||
| compartment_id=self.compartment_id, | ||
| serving_mode=self._build_serving_mode(), | ||
| chat_request=chat_request, | ||
| ) | ||
| response = self._chat(chat_details) | ||
| usage_data: dict[str, int] = {} | ||
| response_metadata: dict[str, Any] = {} | ||
|
|
||
| for event in response.data.events(): | ||
| event_data = self._parse_stream_event(event) | ||
| if not event_data: | ||
| continue | ||
| text_chunk = self._extract_text_from_stream_event(event_data) | ||
| if text_chunk: | ||
| yield text_chunk | ||
| usage_chunk = self._extract_usage_from_stream_event(event_data) | ||
| if usage_chunk: | ||
| usage_data = usage_chunk | ||
| response_metadata.update(self._extract_metadata_from_stream_event(event_data)) | ||
|
|
||
| if usage_data: | ||
| self._track_token_usage_internal(usage_data) | ||
| response_metadata["usage"] = usage_data | ||
| self.last_response_metadata = response_metadata or None |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find the relevant file and inspect the surrounding methods.
FILE="lib/crewai/src/crewai/llms/providers/oci/completion.py"
echo "== outline =="
ast-grep outline "$FILE" --view expanded | sed -n '1,220p'
echo
echo "== iter_stream and _stream_chat_events locations =="
rg -n "def iter_stream|def _stream_chat_events|_client_lock|response\.data\.events\(" "$FILE"
echo
echo "== targeted excerpt =="
sed -n '1000,1265p' "$FILE"Repository: crewAIInc/crewAI
Length of output: 12317
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="lib/crewai/src/crewai/llms/providers/oci/completion.py"
echo "== iter_stream and _stream_chat_events locations =="
rg -n "def iter_stream|def _stream_chat_events|_client_lock|response\.data\.events\(" "$FILE"
echo
echo "== surrounding methods =="
sed -n '1030,1245p' "$FILE"Repository: crewAIInc/crewAI
Length of output: 8795
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="lib/crewai/src/crewai/llms/providers/oci/completion.py"
echo "== call sites of iter_stream / _stream_chat_events =="
rg -n "\biter_stream\(" "$FILE"
rg -n "\b_stream_chat_events\(" "$FILE"
echo
echo "== stream implementation excerpt =="
sed -n '920,1045p' "$FILE"Repository: crewAIInc/crewAI
Length of output: 5308
Hold the OCI client lock for the full SSE stream iter_stream should use _stream_chat_events(chat_details) so response.data.events() is consumed under _client_lock, matching _stream_call_impl. astream delegates to iter_stream, so concurrent calls can otherwise hit the SDK’s non-reentrant client.
🤖 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 1042 -
1078, The iter_stream method currently consumes response.data.events() outside
the OCI client lock. Replace the direct _chat call and event iteration with
_stream_chat_events(chat_details), ensuring the entire SSE stream is consumed
through the helper under _client_lock while preserving text, usage, metadata
handling, and final tracking behavior.
Summary
FunctionDefinition/CohereToolmax_sequential_tool_callssafety limitCohereToolCall/CohereToolResultmappingauto/none/required/function name)attribute_map/swagger_types)supports_function_calling()→TrueDepends on #4961 (streaming) and #4959 (basic text). Draft until both merge.
Tracking issue: #4944
Diff breakdown (vs streaming PR)
completion.py(tool methods + message updates)test_oci_tools.py(9 unit tests)test_oci_integration_tools.py(2 integration tests)Source is ~527 lines — slightly over 500 but tool calling is one cohesive feature that can't be meaningfully split further.
Test plan
meta.llama-3.3-70b-instruct: raw tool call return + recursive execution withadd_numbers