Skip to content

Commit 06d473e

Browse files
authored
feat(cohere): add integrations api support (#316)
Add patchers and tracing helpers for Cohere v1/v2 chat, chat_stream, embed, and rerank calls, plus auto_instrument wiring and nox session coverage. Include cassette-backed tests for sync/async and v1/v2 request shapes, including v1 embed and chat_stream coverage. resolves #260
1 parent 5d63b9b commit 06d473e

29 files changed

Lines changed: 3445 additions & 17 deletions

py/noxfile.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,18 @@ def test_anthropic(session, version):
159159
_run_tests(session, f"{INTEGRATION_DIR}/anthropic/test_anthropic.py", version=version)
160160

161161

162+
COHERE_VERSIONS = _get_matrix_versions("cohere")
163+
164+
165+
@nox.session()
166+
@nox.parametrize("version", COHERE_VERSIONS, ids=COHERE_VERSIONS)
167+
def test_cohere(session, version):
168+
"""Test the native Cohere SDK integration."""
169+
_install_test_deps(session)
170+
_install_matrix_dep(session, "cohere", version)
171+
_run_tests(session, f"{INTEGRATION_DIR}/cohere/test_cohere.py", version=version)
172+
173+
162174
OPENAI_VERSIONS = _get_matrix_versions("openai")
163175

164176

py/pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ lint = [
167167
"agentscope",
168168
"agno",
169169
"anthropic",
170+
"cohere",
170171
"autoevals",
171172
"braintrust-core",
172173
"dspy",
@@ -251,6 +252,10 @@ latest = "openai==2.32.0"
251252
latest = "anthropic==0.96.0"
252253
"0.48.0" = "anthropic==0.48.0"
253254

255+
[tool.braintrust.matrix.cohere]
256+
latest = "cohere==6.1.0"
257+
"5.0.0" = "cohere==5.0.0"
258+
254259
[tool.braintrust.matrix.openai-agents]
255260
latest = "openai-agents==0.14.1"
256261
"0.13.6" = "openai-agents==0.13.6"
@@ -344,6 +349,7 @@ adk = ["google-adk"]
344349
agentscope = ["agentscope"]
345350
agno = ["agno"]
346351
anthropic = ["anthropic"]
352+
cohere = ["cohere"]
347353
claude_agent_sdk = ["claude-agent-sdk"]
348354
dspy = ["dspy"]
349355
google_genai = ["google-genai"]
@@ -359,6 +365,7 @@ pydantic_ai = ["pydantic-ai-integration", "pydantic-ai-wrap-openai"]
359365
agno = "agno"
360366
agentscope = "agentscope"
361367
anthropic = "anthropic"
368+
cohere = "cohere"
362369
autoevals = "autoevals"
363370
braintrust-core = "braintrust_core"
364371
dspy = "dspy"

py/src/braintrust/auto.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
AgnoIntegration,
1414
AnthropicIntegration,
1515
ClaudeAgentSDKIntegration,
16+
CohereIntegration,
1617
DSPyIntegration,
1718
GoogleGenAIIntegration,
1819
LangChainIntegration,
@@ -23,6 +24,7 @@
2324
OpenRouterIntegration,
2425
PydanticAIIntegration,
2526
)
27+
from braintrust.integrations.base import BaseIntegration
2628

2729

2830
__all__ = ["auto_instrument"]
@@ -57,6 +59,7 @@ def auto_instrument(
5759
adk: bool = True,
5860
langchain: bool = True,
5961
openai_agents: bool = True,
62+
cohere: bool = True,
6063
) -> dict[str, bool]:
6164
"""
6265
Auto-instrument supported AI/ML libraries for Braintrust tracing.
@@ -82,6 +85,7 @@ def auto_instrument(
8285
adk: Enable Google ADK instrumentation (default: True)
8386
langchain: Enable LangChain instrumentation (default: True)
8487
openai_agents: Enable OpenAI Agents SDK instrumentation (default: True)
88+
cohere: Enable Cohere instrumentation (default: True)
8589
8690
Returns:
8791
Dict mapping integration name to whether it was successfully instrumented.
@@ -127,7 +131,7 @@ def auto_instrument(
127131
client.models.generate_content(model="gemini-2.0-flash", contents="Hello!")
128132
```
129133
"""
130-
results = {}
134+
results: dict[str, bool] = {}
131135

132136
if openai:
133137
results["openai"] = _instrument_integration(OpenAIIntegration)
@@ -157,11 +161,13 @@ def auto_instrument(
157161
results["langchain"] = _instrument_integration(LangChainIntegration)
158162
if openai_agents:
159163
results["openai_agents"] = _instrument_integration(OpenAIAgentsIntegration)
164+
if cohere:
165+
results["cohere"] = _instrument_integration(CohereIntegration)
160166

161167
return results
162168

163169

164-
def _instrument_integration(integration) -> bool:
170+
def _instrument_integration(integration: type[BaseIntegration]) -> bool:
165171
with _try_patch():
166172
return integration.setup()
167173
return False

py/src/braintrust/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ def setup_braintrust():
153153
os.environ.setdefault("OPENAI_API_KEY", "sk-test-dummy-api-key-for-vcr-tests")
154154
os.environ.setdefault("ANTHROPIC_API_KEY", "sk-ant-test-dummy-api-key-for-vcr-tests")
155155
os.environ.setdefault("MISTRAL_API_KEY", "mistral-test-dummy-api-key-for-vcr-tests")
156+
os.environ.setdefault("CO_API_KEY", os.getenv("COHERE_API_KEY", "co-test-dummy-api-key-for-vcr-tests"))
157+
os.environ.setdefault("COHERE_API_KEY", os.getenv("CO_API_KEY", "co-test-dummy-api-key-for-vcr-tests"))
156158

157159

158160
@pytest.fixture(autouse=True)

py/src/braintrust/integrations/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .agno import AgnoIntegration
44
from .anthropic import AnthropicIntegration
55
from .claude_agent_sdk import ClaudeAgentSDKIntegration
6+
from .cohere import CohereIntegration
67
from .dspy import DSPyIntegration
78
from .google_genai import GoogleGenAIIntegration
89
from .langchain import LangChainIntegration
@@ -20,6 +21,7 @@
2021
"AgnoIntegration",
2122
"AnthropicIntegration",
2223
"ClaudeAgentSDKIntegration",
24+
"CohereIntegration",
2325
"DSPyIntegration",
2426
"GoogleGenAIIntegration",
2527
"LiteLLMIntegration",
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Test auto_instrument for Cohere."""
2+
3+
import os
4+
5+
6+
os.environ.setdefault("CO_API_KEY", "co-test-dummy-api-key-for-vcr-tests")
7+
os.environ.setdefault("COHERE_API_KEY", os.environ["CO_API_KEY"])
8+
9+
import cohere
10+
from braintrust.auto import auto_instrument
11+
from braintrust.integrations.test_utils import autoinstrument_test_context
12+
13+
14+
results = auto_instrument()
15+
assert results.get("cohere") is True
16+
17+
results2 = auto_instrument()
18+
assert results2.get("cohere") is True
19+
20+
21+
with autoinstrument_test_context("test_auto_cohere", integration="cohere") as memory_logger:
22+
use_v2 = hasattr(cohere, "ClientV2") and hasattr(cohere.ClientV2, "chat")
23+
if use_v2:
24+
client = cohere.ClientV2(api_key=os.environ["CO_API_KEY"])
25+
response = client.chat(
26+
model="command-a-03-2025",
27+
messages=[{"role": "user", "content": "Say hi in one word."}],
28+
max_tokens=10,
29+
)
30+
assert response.message.role == "assistant"
31+
else:
32+
client = cohere.Client(api_key=os.environ["CO_API_KEY"])
33+
response = client.chat(
34+
model="command-a-03-2025",
35+
message="Say hi in one word.",
36+
max_tokens=10,
37+
)
38+
assert isinstance(response.text, str)
39+
40+
spans = memory_logger.pop()
41+
assert len(spans) == 1, f"Expected 1 span, got {len(spans)}"
42+
span = spans[0]
43+
assert span["metadata"]["provider"] == "cohere"
44+
assert span["metadata"]["model"] == "command-a-03-2025"
45+
assert span["span_attributes"]["name"] == "cohere.chat"
46+
47+
print("SUCCESS")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""Braintrust integration for the Cohere Python SDK."""
2+
3+
from .integration import CohereIntegration
4+
from .tracing import wrap_cohere
5+
6+
7+
__all__ = [
8+
"CohereIntegration",
9+
"wrap_cohere",
10+
]
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
interactions:
2+
- request:
3+
body: '{"message":"Say hi in one word.","model":"command-a-03-2025","max_tokens":10,"stream":false}'
4+
headers:
5+
Accept:
6+
- '*/*'
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
Content-Length:
12+
- '92'
13+
Host:
14+
- api.cohere.ai
15+
User-Agent:
16+
- cohere/5.0.0
17+
X-Fern-Language:
18+
- Python
19+
X-Fern-Platform:
20+
- darwin/25.2.0
21+
X-Fern-Runtime:
22+
- python/3.14.3
23+
X-Fern-SDK-Name:
24+
- cohere
25+
X-Fern-SDK-Version:
26+
- 6.1.0
27+
content-type:
28+
- application/json
29+
method: POST
30+
uri: https://api.cohere.ai/v1/chat
31+
response:
32+
body:
33+
string: '{"response_id":"c5a7c7f7-9008-494a-b98f-0a4ba7884028","text":"Hi!","generation_id":"f65b695b-32c1-4bce-8181-301bad8e8629","chat_history":[{"role":"USER","message":"Say
34+
hi in one word."},{"role":"CHATBOT","message":"Hi!"}],"finish_reason":"COMPLETE","meta":{"api_version":{"version":"1"},"billed_units":{"input_tokens":6,"output_tokens":2},"tokens":{"input_tokens":501,"output_tokens":4},"cached_tokens":0}}'
35+
headers:
36+
Alt-Svc:
37+
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
38+
Transfer-Encoding:
39+
- chunked
40+
Via:
41+
- 1.1 google
42+
access-control-expose-headers:
43+
- X-Debug-Trace-ID
44+
cache-control:
45+
- no-cache, no-store, no-transform, must-revalidate, private, max-age=0
46+
content-length:
47+
- '406'
48+
content-type:
49+
- application/json
50+
date:
51+
- Thu, 16 Apr 2026 22:25:12 GMT
52+
expires:
53+
- Thu, 01 Jan 1970 00:00:00 GMT
54+
num_chars:
55+
- '2597'
56+
num_tokens:
57+
- '8'
58+
pragma:
59+
- no-cache
60+
server:
61+
- envoy
62+
vary:
63+
- Origin,Accept-Encoding
64+
x-accel-expires:
65+
- '0'
66+
x-debug-trace-id:
67+
- 86e32c7996b5cfbbb75c0d2092031db2
68+
x-endpoint-monthly-call-limit:
69+
- '1000'
70+
x-envoy-upstream-service-time:
71+
- '211'
72+
x-trial-endpoint-call-limit:
73+
- '20'
74+
x-trial-endpoint-call-remaining:
75+
- '18'
76+
status:
77+
code: 200
78+
message: OK
79+
version: 1

0 commit comments

Comments
 (0)