feat: add Cerebras integration to the Python SDK#7342
Conversation
Add track_cerebras() to wrap Cerebras / AsyncCerebras clients so calls to chat.completions.create() (including stream=True) are logged as Opik LLM spans, following the existing OpenAI/Anthropic integration pattern. Cerebras returns OpenAI-shaped payloads, so usage is parsed with the OpenAI converter while the span provider is recorded as 'cerebras'. Adds mock-based unit tests and wires the cerebras suite into the integration runner.
| aggregated_response = { | ||
| "choices": [{"index": 0, "message": {"role": "", "content": ""}}], | ||
| "created": first_chunk.created, | ||
| "id": first_chunk.id, | ||
| "model": first_chunk.model, |
There was a problem hiding this comment.
Duplicate chunk aggregation
aggregate() is a near copy of opik/integrations/openai/chat_completion_chunks_aggregator.py:22-63, so OpenAI and Cerebras chunk stitching will drift if this logic changes; should we extract it into a reusable helper?
Want Baz to fix this for you? Activate Fixer
| if delta.content: | ||
| text_chunks.append(delta.content) |
There was a problem hiding this comment.
Drops streamed reasoning output
delta.reasoning is dropped when the reducer aggregates streamed choices, so reasoning-enabled completions log a synthetic choices[0].message without the reasoning text and the span output is incomplete — should we preserve it too, as Cerebras’ official reasoning docs say it’s delivered in delta.reasoning?
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
sdks/python/src/opik/integrations/cerebras/chat_completion_chunks_aggregator.py around
lines 48-49 inside the `aggregate` function’s loop, the code currently appends only
`delta.content` into `text_chunks` and never copies `delta.reasoning` from the streamed
chunks. Refactor the aggregation to also collect `delta.reasoning` tokens (e.g., a
separate buffer) and set them onto the synthetic
`aggregated_response['choices'][0]['message']` in the appropriate field so downstream
span/log output includes reasoning-enabled text. Ensure this works when reasoning is
present across multiple streamed chunks by concatenating all reasoning tokens in order,
similar to how `content` is concatenated.
| def Stream__iter__decorator(dunder_iter_func: Callable) -> Callable: | ||
| @functools.wraps(dunder_iter_func) | ||
| def wrapper(self: cerebras.Stream) -> Iterator[StreamItem]: | ||
| try: | ||
| accumulated_items: List[StreamItem] = [] |
There was a problem hiding this comment.
Duplicate stream patching
patch_sync_stream/patch_async_stream duplicate the opik/integrations/openai/stream_patchers.py:30‑157 logic almost verbatim for cerebras.Stream, so any fix to generations_aggregator or finally_callback has to be mirrored in both places and can drift — should we parameterize the shared patcher instead?
Want Baz to fix this for you? Activate Fixer
| LOGGER.debug( | ||
| "Exception raised from cerebras.Stream.", | ||
| str(exception), | ||
| exc_info=True, | ||
| ) |
There was a problem hiding this comment.
Logging call raises formatting error
In both the sync and async stream wrappers, LOGGER.debug("Exception raised from cerebras.Stream.", str(exception), exc_info=True) passes str(exception) to a message with no %s, so logging raises a TypeError before the debug line is emitted and can interrupt exception handling while the span is still open — should we change it to LOGGER.debug("Exception raised from cerebras.Stream: %s", exception, exc_info=True)?
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
sdks/python/src/opik/integrations/cerebras/stream_patchers.py around lines 43-47 in
patch_sync_stream’s Stream__iter__decorator exception handler, the LOGGER.debug call
passes str(exception) as a positional arg to a message string with no formatting
placeholders, which can raise TypeError during logging and prevent cleanup. Refactor
that logging line to use standard logging formatting with a “%s” placeholder (e.g.,
append “: %s” to the message and pass the exception as the corresponding argument)
while keeping exc_info=True. Do the same for the analogous LOGGER.debug call in
patch_async_stream around lines 96-100 inside AsyncStream__aiter__decorator.
| import cerebras.cloud.sdk as cerebras | ||
|
|
||
| from opik.types import LLMProvider | ||
|
|
||
| from . import cerebras_chat_completions_decorator, chat_completion_chunks_aggregator |
There was a problem hiding this comment.
Optional import crashes package load
sdks/python/src/opik/integrations/cerebras/__init__.py imports cerebras.cloud.sdk and the decorator helpers at module load, so import opik.integrations.cerebras raises ModuleNotFoundError in environments without cerebras-cloud-sdk before track_cerebras() is reached — should we move those imports behind the public function boundary or guard them like the other optional integrations, as .agents/skills/python-sdk/SKILL.md requires?
Want Baz to fix this for you? Activate Fixer You can also update your AI coding guidelines based on this comment by apply pr to [branch name]
Other fix methods
Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
sdks/python/src/opik/integrations/cerebras/opik_tracker.py around lines 1-70,
`track_cerebras()` relies on `import cerebras.cloud.sdk as cerebras` and module-scope
imports of the Cerebras decorator modules, which execute during package import and can
raise ModuleNotFoundError when the optional `cerebras-cloud-sdk` isn’t installed.
Refactor by removing the top-level Cerebras SDK import and any other Cerebras-runtime
imports from module scope; instead, perform them inside `track_cerebras()` (and
`_extract_metadata_from_client` if needed) with a clear error message if Cerebras
isn’t installed. Keep typing aliases (e.g., `TypeVar`) behind `typing.TYPE_CHECKING`
or adjust them so they don’t require the Cerebras SDK at import time.
|
Same as #7341 (Groq) — these three points are intentional consistency with the existing OpenAI/Anthropic integrations:
|
Summary
Adds a Cerebras integration to the Opik Python SDK, so calls made with the native
cerebras-cloud-sdkclient are automatically logged as Opik LLM spans:Cerebras (very fast inference for Llama and other open models) has a distinct native SDK, so the OpenAI wrapper (
track_openai) can't instrument it — this adds first-class support, matching the existing standalone integrations.Details
track_cerebras()follows the existing OpenAI/Anthropic pattern:chat.completions.create()on bothCerebrasandAsyncCerebras, includingstream=True(sync + async streaming via aggregated chunks)."cerebras"; Cerebras returns OpenAI-shaped usage, so token usage is parsed with the OpenAI converter (the comment notes this denotes payload format, not the span provider).error_infoon failures.Files:
src/opik/integrations/cerebras/—track_cerebras, decorator, chunk aggregator, stream patchers.tests/library_integration/cerebras/— mock-based unit tests +requirements.txt.lib-cerebras-tests.yml+ registration inlib-integration-tests-runner.yml.Testing
pytest tests/library_integration/cerebras/→ 4 passed (sync happy-path with full trace-tree assertion, async, and error handling), using thefake_backendfixture and a mocked transport so no Cerebras API key is required.ruffandmypyclean.Happy to add a documentation/cookbook page as a follow-up if preferred.