Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
[codespell]
check-hidden = true
# skipping auto generated folders
skip = ./.git,./.tox,./.venv,./.mypy_cache,./docs/_build,./target,*/LICENSE,./venv,*/cassettes
skip = ./.git,./.tox,./.venv,./test_env,./.mypy_cache,./docs/_build,./target,*/LICENSE,./venv,*/cassettes
ignore-words-list = ot
1 change: 1 addition & 0 deletions instrumentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
| [opentelemetry-instrumentation-genai-agno](./opentelemetry-instrumentation-genai-agno) | agno >= 2.0.0 | No | development
| [opentelemetry-instrumentation-genai-anthropic](./opentelemetry-instrumentation-genai-anthropic) | anthropic >= 0.16.0 | No | development
| [opentelemetry-instrumentation-genai-claude-agent-sdk](./opentelemetry-instrumentation-genai-claude-agent-sdk) | claude-agent-sdk >= 0.1.14 | No | development
| [opentelemetry-instrumentation-genai-groq](./opentelemetry-instrumentation-genai-groq) | groq >= 0.6.0 | No | development
| [opentelemetry-instrumentation-genai-langchain](./opentelemetry-instrumentation-genai-langchain) | langchain >= 0.3.21 | No | development
| [opentelemetry-instrumentation-genai-llama-index](./opentelemetry-instrumentation-genai-llama-index) | llama-index-core >= 0.14.19 | No | development
| [opentelemetry-instrumentation-genai-openai](./opentelemetry-instrumentation-genai-openai) | openai >= 1.26.0 | Yes | development
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `opentelemetry-instrumentation-genai-groq` package for instrumenting the official Groq Python client.
114 changes: 114 additions & 0 deletions instrumentation/opentelemetry-instrumentation-genai-groq/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
OpenTelemetry Groq Instrumentation
==================================

|pypi|

.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-genai-groq.svg
:target: https://pypi.org/project/opentelemetry-instrumentation-genai-groq/

This library allows tracing LLM requests and logging of messages made by the
`Groq Python API library <https://pypi.org/project/groq/>`_. It also captures
the duration of the operations and the number of tokens used as metrics.

Installation
------------

If your application is already instrumented with OpenTelemetry, add this
package to your requirements.
::

pip install opentelemetry-instrumentation-genai-groq

If you don't have a Groq application, yet, try our `examples <examples>`_
which only need a valid Groq API key.

Check out `zero-code example <examples/zero-code>`_ for a quick start.

Usage
-----

This section describes how to set up Groq instrumentation if you're setting OpenTelemetry up manually.
Check out the `manual example <examples/manual>`_ for more details.

Instrumenting all clients
*************************

When using the instrumentor, all clients will automatically trace Groq operations including chat completions.
You can also optionally capture prompts and completions as log events.

Make sure to configure OpenTelemetry tracing, logging, and events to capture all telemetry emitted by the instrumentation.

.. code-block:: python

from opentelemetry.instrumentation.genai.groq import GroqInstrumentor
from groq import Groq

GroqInstrumentor().instrument()

client = Groq()
# Chat completion example
response = client.chat.completions.create(
model="llama3-8b-8192",
messages=[
{"role": "user", "content": "Write a short poem on open telemetry."},
],
)

Enabling message content
*************************

Message content such as the contents of the prompt, completion, function arguments and return values
are not captured by default. To capture message content, set the environment variable
``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`` to one of the following values:

- ``span_only`` - capture content on *span* attributes.
- ``event_only`` - capture content on *event* attributes.
- ``span_and_event`` - capture content on both *span* and *event* attributes.
- ``no_content`` - do not capture content (the default).

Uploading prompts and completions
*********************************

To enable the built-in upload hook, set:

- ``OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK=upload``
- ``OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH`` to an ``fsspec``-compatible URI/path
(e.g. ``/path/to/prompts`` or ``gs://my_bucket``).

Install the ``upload`` extra to pull in ``fsspec``::

pip install opentelemetry-util-genai[upload]

See the `opentelemetry-util-genai
<https://github.com/open-telemetry/opentelemetry-python-genai/blob/main/util/opentelemetry-util-genai/README.rst>`_
for additional options.

Enabling the latest experimental features
***********************************************

The latest experimental GenAI semantic conventions are used unconditionally; there is
no environment variable to opt in or out.

.. note:: Generative AI semantic conventions are still evolving. The latest experimental features may introduce breaking changes in future releases.

Uninstrument
************

To uninstrument clients, call the uninstrument method:

.. code-block:: python

from opentelemetry.instrumentation.genai.groq import GroqInstrumentor

GroqInstrumentor().instrument()
# ...

# Uninstrument all clients
GroqInstrumentor().uninstrument()

References
----------

* `OpenTelemetry Project <https://opentelemetry.io/>`_
* `OpenTelemetry GenAI semantic conventions <https://opentelemetry.io/docs/specs/semconv/gen-ai/>`_
* `Groq SDK (Python) <https://github.com/groq/groq-python>`_
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# TODO: Set your Groq API key here
GROQ_API_KEY=sk-YOUR_API_KEY

# Uncomment to use Ollama instead of Groq
# GROQ_BASE_URL=http://localhost:11434/v1
# GROQ_API_KEY=unused
# CHAT_MODEL=qwen2.5:0.5b

# Uncomment and change to your OTLP endpoint
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# OTEL_EXPORTER_OTLP_PROTOCOL=grpc

OTEL_SERVICE_NAME=opentelemetry-python-groq

# Remove to hide prompt and completion content
# Possible values (case insensitive):
# - `span_only` - record content on span attributes
# - `event_only` - record content on event attributes
# - `span_and_event` - record content on both span and event attributes
# - everything else - don't record content on any signal
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=span_only

# Uncomment to upload prompts and responses to an fsspec-compatible
# destination instead of or in addition to recording them inline on spans/events.
# OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK=upload
# OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH=/path/to/prompts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
OpenTelemetry Groq Instrumentation Example
============================================

This is an example of how to instrument Groq calls when configuring OpenTelemetry SDK and Instrumentations manually.

When `main.py <main.py>`_ is run, it exports traces and logs to an OTLP
compatible endpoint. Traces include details such as the model used and the
duration of the chat request. Logs capture the chat request and the generated
response, providing a comprehensive view of the performance and behavior of
your Groq requests.

Note: `.env <.env>`_ file configures additional environment variables:

- ``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=span_only`` configures Groq instrumentation to capture prompt and completion contents on *span* attributes.
- ``OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK`` (commented out) - uncomment along with ``OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH`` to upload prompts and completions to an ``fsspec``-compatible destination instead of recording them inline. Also uncomment the ``opentelemetry-util-genai[upload]`` line in `requirements.txt <requirements.txt>`_ and reinstall.

Setup
-----

Minimally, update the `.env <.env>`_ file with your ``GROQ_API_KEY``. An
OTLP compatible endpoint should be listening for traces and logs on
http://localhost:4317. If not, update ``OTEL_EXPORTER_OTLP_ENDPOINT`` as well.

Next, set up a virtual environment like this:

::

python3 -m venv .venv
source .venv/bin/activate
pip install "python-dotenv[cli]"
pip install -r requirements.txt

Run
---

Run the example like this:

::

dotenv run -- python main.py

You should see a poem generated by Groq while traces and logs export to your
configured observability tool.

Custom completion hook
----------------------

`custom_hook.py <custom_hook.py>`_ is a variant of ``main.py`` that passes a
custom ``CompletionHook`` implementation programmatically via
``GroqInstrumentor().instrument(completion_hook=...)``. The example hook
prints prompts and completions to stdout; real hooks typically forward content
to external storage and record reference URIs on the span/log_record.

Run it the same way:

::

dotenv run -- python custom_hook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

# pylint: skip-file
"""Same as main.py, but instruments Groq with a custom CompletionHook
that prints prompts and completions to stdout.

Run with: dotenv run -- python custom_hook.py
"""

import os

from groq import Groq

from opentelemetry import _logs, metrics, trace
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
OTLPLogExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
OTLPMetricExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.instrumentation.genai.groq import GroqInstrumentor
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.util.genai.completion_hook import CompletionHook
from opentelemetry.util.genai.types import (
InputMessage,
MessagePart,
OutputMessage,
ToolDefinition,
)


class PrintCompletionHook(CompletionHook):
"""Minimal CompletionHook that prints inputs/outputs to stdout.

Real hooks typically forward content to external storage (object store,
database, etc.) and record reference URIs on the span/log_record.
"""

def on_completion(
self,
*,
inputs: list[InputMessage],
outputs: list[OutputMessage],
system_instruction: list[MessagePart],
tool_definitions: list[ToolDefinition] | None = None,
span=None,
log_record=None,
) -> None:
print(f"[hook] inputs: {inputs}")
print(f"[hook] outputs: {outputs}")


trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(OTLPSpanExporter())
)

_logs.set_logger_provider(LoggerProvider())
_logs.get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)

metrics.set_meter_provider(
MeterProvider(
metric_readers=[
PeriodicExportingMetricReader(OTLPMetricExporter()),
]
)
)

GroqInstrumentor().instrument(completion_hook=PrintCompletionHook())


def main():
client = Groq()
chat_completion = client.chat.completions.create(
model=os.getenv("CHAT_MODEL", "llama3-8b-8192"),
messages=[
{
"role": "user",
"content": "Write a short poem on OpenTelemetry.",
},
],
)
print(chat_completion.choices[0].message.content)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

# pylint: skip-file
import os

from groq import Groq

# NOTE: OpenTelemetry Python Logs API is in beta
from opentelemetry import _logs, metrics, trace
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
OTLPLogExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
OTLPMetricExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.instrumentation.genai.groq import GroqInstrumentor
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# configure tracing
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(OTLPSpanExporter())
)

# configure logging
_logs.set_logger_provider(LoggerProvider())
_logs.get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)

# configure metrics
metrics.set_meter_provider(
MeterProvider(
metric_readers=[
PeriodicExportingMetricReader(
OTLPMetricExporter(),
),
]
)
)

# instrument Groq
GroqInstrumentor().instrument()


def main():
client = Groq()
chat_completion = client.chat.completions.create(
model=os.getenv("CHAT_MODEL", "llama3-8b-8192"),
messages=[
{
"role": "user",
"content": "Write a short poem on OpenTelemetry.",
},
],
)
print(chat_completion.choices[0].message.content)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
groq~=0.13.0

opentelemetry-sdk~=1.43.0
opentelemetry-exporter-otlp-proto-grpc~=1.43.0
opentelemetry-instrumentation-genai-groq~=1.0b0

# Uncomment to enable the upload completion hook (pulls in fsspec).
# Required when OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK=upload.
# opentelemetry-util-genai[upload]
Loading