Commit 15ac93d
authored
feat(oci): Add Oracle Cloud Infrastructure (OCI) Generative AI client support (#754)
* feat: Add Oracle Cloud Infrastructure (OCI) Generative AI client support
Adds OciClient (V1 API) and OciClientV2 (V2 API) for the OCI Generative
AI service, following the BedrockClient pattern with httpx event hooks.
Authentication: config file, custom profiles, session tokens, direct
credentials, instance principal, resource principal.
API coverage: embed (all models), chat with streaming (OciClient for
Command R family, OciClientV2 for Command A). Lazy-loads oci SDK as an
optional dependency; install with `pip install cohere[oci]`.
* fix: address review feedback — remove stale model names and fix test profile
- README: remove specific model names from Supported APIs and Model
Availability sections (per mkozakov review — will go out of date)
- tests: default OCI_PROFILE to DEFAULT instead of API_KEY_AUTH
* fix: remove dead chat_stream endpoint and body-based stream detection
The "stream" in endpoint check was dead code — both V1 and V2 SDK always
route through endpoint "chat" (v1/chat and v2/chat paths). Streaming is
reliably signalled via body["stream"], which the SDK always sets.
- Drop "stream" in endpoint guard on is_stream and isStream detection
- Remove "chat_stream" from action_map, transform, and response branches
- Update unit tests to use "chat" endpoint (the only real one)
* fix: don't trigger content-type transition on finish-only stream events
_current_content_type now returns None for events with no message content
(e.g. {"finishReason": "COMPLETE"}). The transition branch in
_transform_v2_event is skipped when event_content_type is None, so a
finish-only event after a thinking block no longer opens a spurious empty
text block before emitting content-end.
* test: add integration tests for light models, command-r-plus, multi-turn, and system message
* fix: use 'or []' to guard against explicit content=None in V2 messages
* Address cursor review: raise on unsupported endpoint, refresh session token per-request
- transform_request_to_oci now raises ValueError for endpoints other than
'embed' and 'chat' instead of silently returning the untransformed body
- Session token auth uses a refreshing wrapper that re-reads the token file
before each signing call, so OCI CLI token refreshes are picked up without
restarting the client
- Add test_unsupported_endpoint_raises to cover the new explicit error
- Update test_session_auth_prefers_security_token_signer to expect multi-call
behaviour from the refreshing signer
* Add test proving session token is re-read on subsequent requests
test_session_token_refreshed_on_subsequent_requests writes a real token file,
makes two requests with the file updated between them, and asserts that the
second signing call uses the new token — verifying the refreshing signer works
end-to-end.
* fix(oci): resolve mypy type errors and add type-checking test gate
The OCI client code introduced several mypy errors that went unnoticed
because mypy was configured but never enforced in tests or CI.
Type fixes:
- lazy_oci_deps.py: suppress import-untyped for oci SDK (no type stubs)
- oci_client.py: cast response.stream to Iterator[bytes] (httpx types it
as SyncByteStream | AsyncByteStream but it's iterable at runtime)
- oci_client.py: use .get("model", "") to satisfy str expectation
- test_oci_client.py: suppress attr-defined on dynamic module stubs
New test gate (tests/test_oci_mypy.py):
- Runs mypy on OCI source and test files as part of pytest
- Uses --follow-imports=silent to isolate from pre-existing AWS errors
- Skips gracefully if mypy is not on PATH
- Ensures future type regressions fail the test suite immediately
* fix(oci): add response_type to embed response for SDK discriminated union
The SDK's EmbedResponse is a discriminated union on response_type
(embeddings_floats vs embeddings_by_type). The OCI embed response
transformation was missing this field, causing pydantic to return None
instead of an EmbedResponse object. This broke V1 embed when the SDK's
merge_embed_responses tried to access .meta on None.
V1 (flat float arrays) now returns response_type="embeddings_floats",
V2 (typed dict) returns response_type="embeddings_by_type".
* test(oci): assert response_type on embed responses (unit + integration)
Adds unit tests for V1 (embeddings_floats) and V2 (embeddings_by_type)
response_type presence, and adds assertions to the live integration
embed tests. Ensures the discriminated union field can't be silently
removed without test failure.
* fix(oci): address Bugbot review — deduplicate Streamer and add V1 stream-start
1. Remove duplicate Streamer class (manually_maintained/streaming.py)
and import from aws_client.py instead. Both were identical
SyncByteStream wrappers.
2. Emit stream-start event with generation_id at the beginning of V1
streams, matching the standard Cohere V1 streaming chat format.
Consumers relying on stream-start for state initialization will now
receive it before text-generation events.
Updated test_v1_stream_wrapper_preserves_finish_reason to verify
stream-start is emitted first.
* fix(oci): cover both import-untyped and import-not-found for oci SDK
When oci is installed but lacks stubs, mypy raises import-untyped.
When oci is not installed (optional dep), mypy raises import-not-found.
Cover both cases since cohere[oci] is optional.1 parent cdb9f69 commit 15ac93d
9 files changed
Lines changed: 3246 additions & 286 deletions
File tree
- src/cohere
- manually_maintained
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
61 | 166 | | |
62 | 167 | | |
63 | 168 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
51 | 56 | | |
52 | 57 | | |
53 | 58 | | |
| |||
95 | 100 | | |
96 | 101 | | |
97 | 102 | | |
98 | | - | |
99 | | - | |
100 | | - | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
523 | 523 | | |
524 | 524 | | |
525 | 525 | | |
| 526 | + | |
| 527 | + | |
526 | 528 | | |
527 | 529 | | |
528 | 530 | | |
| |||
860 | 862 | | |
861 | 863 | | |
862 | 864 | | |
| 865 | + | |
| 866 | + | |
863 | 867 | | |
864 | 868 | | |
865 | 869 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
0 commit comments