diff --git a/.github/component_owners.yml b/.github/component_owners.yml index 1e89d59567..5cbb6aa402 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -73,4 +73,5 @@ components: - lzchen - gyliu513 - nirga + - alizenhom - codefromthecrypt diff --git a/.github/scripts/update-version-patch.sh b/.github/scripts/update-version-patch.sh new file mode 100755 index 0000000000..fec7cd8260 --- /dev/null +++ b/.github/scripts/update-version-patch.sh @@ -0,0 +1,11 @@ +#!/bin/bash -e + +sed -i "/\[stable\]/{n;s/version=.*/version=$1/}" eachdist.ini +sed -i "/\[prerelease\]/{n;s/version=.*/version=$2/}" eachdist.ini + +./scripts/eachdist.py update_patch_versions \ + --stable_version=$1 \ + --unstable_version=$2 \ + --stable_version_prev=$3 \ + --unstable_version_prev=$4 + diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 7c854d436d..51e1dd8208 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -40,14 +40,18 @@ jobs: exit 1 fi + stable_version_prev="$stable_major_minor.$((stable_patch))" + unstable_version_prev="0.${unstable_minor}b$((unstable_patch))" stable_version="$stable_major_minor.$((stable_patch + 1))" unstable_version="0.${unstable_minor}b$((unstable_patch + 1))" echo "STABLE_VERSION=$stable_version" >> $GITHUB_ENV echo "UNSTABLE_VERSION=$unstable_version" >> $GITHUB_ENV + echo "STABLE_VERSION_PREV=$stable_version_prev" >> $GITHUB_ENV + echo "UNSTABLE_VERSION_PREV=$unstable_version_prev" >> $GITHUB_ENV - name: Update version - run: .github/scripts/update-version.sh $STABLE_VERSION $UNSTABLE_VERSION + run: .github/scripts/update-version-patch.sh $STABLE_VERSION $UNSTABLE_VERSION $STABLE_VERSION_PREV $UNSTABLE_VERSION_PREV - name: Set up Python 3.9 uses: actions/setup-python@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d7b18edc8..97e51c9c63 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -118,36 +118,3 @@ jobs: # the step below is creating a pull request against main ref: main - - name: Copy change log updates to main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo "VERSION=${STABLE_VERSION}\/${UNSTABLE_VERSION}" >> $GITHUB_ENV - echo "RELEASE_TAG=$STABLE_VERSION" >> $GITHUB_ENV - ./scripts/merge_changelog_to_main.sh - - - name: Use CLA approved github bot - run: .github/scripts/use-cla-approved-github-bot.sh - - - name: Create pull request against main - env: - # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows - GITHUB_TOKEN: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} - run: | - message="Copy change log updates from $GITHUB_REF_NAME" - body="Copy log updates from \`$GITHUB_REF_NAME\`." - branch="opentelemetrybot/copy-change-log-updates-from-${GITHUB_REF_NAME//\//-}" - - if [[ -z $PRIOR_VERSION_WHEN_PATCH ]]; then - if git diff --quiet; then - echo there are no updates needed to the change log on main, not creating pull request - exit 0 # success - fi - fi - - git commit -a -m "$message" - git push origin HEAD:$branch - gh pr create --title "$message" \ - --body "$body" \ - --head $branch \ - --base main diff --git a/.pylintrc b/.pylintrc index 39ea7b5d35..bc3b25c978 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ extension-pkg-whitelist=cassandra # Add list of files or directories to be excluded. They should be base names, not # paths. -ignore=CVS,gen,Dockerfile,docker-compose.yml,README.md,requirements.txt,docs +ignore=CVS,gen,Dockerfile,docker-compose.yml,README.md,requirements.txt,docs,.venv # Add files or directories matching the regex patterns to be excluded. The # regex matches against base names, not paths. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fdb7c123f..e53dbd9980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,15 +15,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-sqlalchemy` Update unit tests to run with SQLALchemy 2 ([#2976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2976)) +- Add `opentelemetry-instrumentation-openai-v2` to `opentelemetry-bootstrap` + ([#2996](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2996)) +- `opentelemetry-instrumentation-sqlalchemy` Add sqlcomment to `db.statement` attribute + ([#2937](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2937)) +- `opentelemetry-instrumentation-dbapi` Add sqlcomment to `db.statement` attribute + ([#2935](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2935)) +- `opentelemetry-instrumentation-dbapi` instrument_connection accepts optional connect_module + ([#3027](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3027)) +- `opentelemetry-instrumentation-mysqlclient` Add sqlcommenter support + ([#2941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2941)) +- `opentelemetry-instrumentation-pymysql` Add sqlcommenter support + ([#2942](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2942)) ### Fixed +- `opentelemetry-instrumentation-httpx`: instrument_client is a static method again + ([#3003](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3003)) +- `opentelemetry-instrumentation-system_metrics`: fix callbacks reading wrong config + ([#3025](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3025)) +- `opentelemetry-instrumentation-httpx`: Check if mount transport is none before wrap it + ([#3022](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3022)) +- Replace all instrumentor unit test `assertEqualSpanInstrumentationInfo` calls with `assertEqualSpanInstrumentationScope` calls + ([#3037](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3037)) + ### Breaking changes - `opentelemetry-instrumentation-sqlalchemy` teach instruments version ([#2971](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2971)) - Drop `opentelemetry-instrumentation-test` package from default instrumentation list ([#2969](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2969)) +- `opentelemetry-instrumentation-httpx`: remove private unused `_InstrumentedClient` and `_InstrumentedAsyncClient` classes + ([#3036](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3036)) ## Version 1.28.0/0.49b0 (2024-11-05) diff --git a/RELEASING.md b/RELEASING.md index 2706d36c8f..490269e2e4 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,8 +9,9 @@ (otherwise the workflow will pick up the version from `main` and just remove the `.dev` suffix). * Review the two pull requests that it creates. (one is targeted to the release branch and one is targeted to `main`). - * Merge the one targeted towards the release branch. - * The builds will fail for the `main` pr because of validation rules. Follow the [release workflow](https://github.com/open-telemetry/opentelemetry-python/blob/main/RELEASING.md) for the core repo up until this same point. Change the SHAs of each PR to point at each other to get the `main` builds to pass. + * The builds will fail for both the `main` and release pr because of validation rules. Follow the [release workflow](https://github.com/open-telemetry/opentelemetry-python/blob/main/RELEASING.md) for the core repo up until this same point. Change the SHAs of each PR to point at each other to get the `main` and release builds to pass. + * Merge the release PR. + * Merge the PR to main (this can be done separately from [making the release](#making-the-release)) ### Preparing a major or minor release for individual package @@ -62,6 +63,7 @@ The workflow can only be run against long-term release branch such as `package-r * Review and merge the pull request that it creates for updating the change log in main (note that if this is not a patch release then the change log on main may already be up-to-date, in which case no pull request will be created). + * Verify that a new [Github release](https://github.com/open-telemetry/opentelemetry-python-contrib/releases) has been created and that the CHANGELOGs look correct. ### Releasing individual package @@ -79,6 +81,14 @@ to pick a specific package to release. The workflow can only be run against long-term release branch such as `package-release/{package-name}/v{major}.{minor}.x` or `package-release/{package-name}/v{major}.{minor}bx`. +## After the release + +* Check PyPI + * This should be handled automatically on release by the [publish action](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/.github/workflows/release.yml). + * Check the [action logs](https://github.com/open-telemetry/opentelemetry-python-contrib/actions/workflows/release.yml) to make sure packages have been uploaded to PyPI + * Check the release history (e.g. https://pypi.org/project/opentelemetry-instrumentation/#history) on PyPI + * If for some reason the action failed, see [Publish failed](#publish-failed) below + ## Notes about version numbering for stable components * The version number for stable components in the `main` branch is always `X.Y.0.dev`, @@ -110,28 +120,6 @@ The workflow can only be run against long-term release branch such as `package-r * The version number for unstable components in the `main` branch will be bumped to the next version, e.g. `0.{Y+1}b0.dev`. -## After the release - -* Check PyPI - * This should be handled automatically on release by the [publish action](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/.github/workflows/release.yml). - * Check the [action logs](https://github.com/open-telemetry/opentelemetry-python-contrib/actions/workflows/release.yml) to make sure packages have been uploaded to PyPI - * Check the release history (e.g. https://pypi.org/project/opentelemetry-instrumentation/#history) on PyPI - * If for some reason the action failed, see [Publish failed](#publish-failed) below -* Move stable tag - * Run the following (TODO automate): - - ```bash - git tag -d stable - git tag stable - git push --delete origin tagname - git push origin stable - ``` - - * This will ensure the docs are pointing at the stable release. - * To validate this worked, ensure the stable build has run successfully: - . - If the build has not run automatically, it can be manually trigger via the readthedocs interface. - ## Releasing dev version of new packages to claim namespace When a contribution introduces a new package, in order to mitigate name-squatting incidents, release the current development version of the new package under the `opentelemetry` user to simply claim the namespace. This should be done shortly after the PR that introduced this package has been merged into `main`. diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md index 2fdeeea74f..2c10498511 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Add example to `opentelemetry-instrumentation-openai-v2` + ([#3006](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3006)) +- Support for `AsyncOpenAI/AsyncCompletions` ([#2984](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2984)) + ## Version 2.0b0 (2024-11-08) - Use generic `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/README.rst b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/README.rst index 85817896ff..bbd142a97e 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/README.rst +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/README.rst @@ -6,17 +6,21 @@ OpenTelemetry OpenAI Instrumentation .. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-openai-v2.svg :target: https://pypi.org/project/opentelemetry-instrumentation-openai-v2/ -Instrumentation with OpenAI that supports the OpenAI library and is -specified to trace_integration using 'OpenAI'. +This library allows tracing LLM requests and logging of messages made by the +`OpenAI Python API library `_. Installation ------------ +If your application is already instrumented with OpenTelemetry, add this +package to your requirements. :: pip install opentelemetry-instrumentation-openai-v2 +If you don't have an OpenAI application, yet, try our `example `_ +which only needs a valid OpenAI API key. References ---------- diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/.env b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/.env new file mode 100644 index 0000000000..d6afa66723 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/.env @@ -0,0 +1,18 @@ +# Update this with your real OpenAI API key +OPENAI_API_KEY=sk-YOUR_API_KEY + +# Uncomment to use Ollama instead of OpenAI +# OPENAI_BASE_URL=http://localhost:11434/v1 +# OPENAI_API_KEY=unused +# CHAT_MODEL=qwen2.5:0.5b + +OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf +OTEL_SERVICE_NAME=opentelemetry-python-openai + +# Change to 'false' to disable logging +OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true +# Change to 'console' if your OTLP endpoint doesn't support logs +OTEL_LOGS_EXPORTER=otlp_proto_http +# Change to 'false' to hide prompt and completion content +OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/README.rst b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/README.rst new file mode 100644 index 0000000000..019e141c70 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/README.rst @@ -0,0 +1,39 @@ +OpenTelemetry OpenAI Instrumentation Example +============================================ + +This is an example of how to instrument OpenAI calls with zero code changes, +using `opentelemetry-instrument`. + +When `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 OpenAI requests. + +Setup +----- + +Minimally, update the `.env <.env>`_ file with your "OPENAI_API_KEY". An +OTLP compatible endpoint should be listening for traces and logs on +http://localhost:4318. 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 -- opentelemetry-instrument python main.py + +You should see a poem generated by OpenAI while traces and logs export to your +configured observability tool. diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/main.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/main.py new file mode 100644 index 0000000000..d41b1dd933 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/main.py @@ -0,0 +1,21 @@ +import os + +from openai import OpenAI + + +def main(): + client = OpenAI() + chat_completion = client.chat.completions.create( + model=os.getenv("CHAT_MODEL", "gpt-4o-mini"), + messages=[ + { + "role": "user", + "content": "Write a short poem on OpenTelemetry.", + }, + ], + ) + print(chat_completion.choices[0].message.content) + + +if __name__ == "__main__": + main() diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/requirements.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/requirements.txt new file mode 100644 index 0000000000..9ec9bff320 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/example/requirements.txt @@ -0,0 +1,6 @@ +openai~=1.54.4 + +opentelemetry-sdk~=1.28.2 +opentelemetry-exporter-otlp-proto-http~=1.28.2 +opentelemetry-distro~=0.49b2 +opentelemetry-instrumentation-openai-v2~=2.0b0 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml index eddd85e5f0..e28611d0c5 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml @@ -32,7 +32,7 @@ dependencies = [ [project.optional-dependencies] instruments = [ - "openai >= 1.0.0", + "openai >= 1.26.0", ] [project.entry-points.opentelemetry_instrumentor] diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py index e8a782e404..ee3bbfdb73 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py @@ -52,7 +52,7 @@ from opentelemetry.semconv.schemas import Schemas from opentelemetry.trace import get_tracer -from .patch import chat_completions_create +from .patch import async_chat_completions_create, chat_completions_create class OpenAIInstrumentor(BaseInstrumentor): @@ -84,7 +84,16 @@ def _instrument(self, **kwargs): ), ) + wrap_function_wrapper( + module="openai.resources.chat.completions", + name="AsyncCompletions.create", + wrapper=async_chat_completions_create( + tracer, event_logger, is_content_enabled() + ), + ) + def _uninstrument(self, **kwargs): import openai # pylint: disable=import-outside-toplevel unwrap(openai.resources.chat.completions.Completions, "create") + unwrap(openai.resources.chat.completions.AsyncCompletions, "create") diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py index 8540bff219..cd284473ce 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py @@ -21,15 +21,12 @@ from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAIAttributes, ) -from opentelemetry.semconv.attributes import ( - error_attributes as ErrorAttributes, -) from opentelemetry.trace import Span, SpanKind, Tracer -from opentelemetry.trace.status import Status, StatusCode from .utils import ( choice_to_event, get_llm_request_attributes, + handle_span_exception, is_streaming, message_to_event, set_span_attribute, @@ -72,12 +69,49 @@ def traced_method(wrapped, instance, args, kwargs): return result except Exception as error: - span.set_status(Status(StatusCode.ERROR, str(error))) + handle_span_exception(span, error) + raise + + return traced_method + + +def async_chat_completions_create( + tracer: Tracer, event_logger: EventLogger, capture_content: bool +): + """Wrap the `create` method of the `AsyncChatCompletion` class to trace it.""" + + async def traced_method(wrapped, instance, args, kwargs): + span_attributes = {**get_llm_request_attributes(kwargs, instance)} + + span_name = f"{span_attributes[GenAIAttributes.GEN_AI_OPERATION_NAME]} {span_attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL]}" + with tracer.start_as_current_span( + name=span_name, + kind=SpanKind.CLIENT, + attributes=span_attributes, + end_on_exit=False, + ) as span: + if span.is_recording(): + for message in kwargs.get("messages", []): + event_logger.emit( + message_to_event(message, capture_content) + ) + + try: + result = await wrapped(*args, **kwargs) + if is_streaming(kwargs): + return StreamWrapper( + result, span, event_logger, capture_content + ) + if span.is_recording(): - span.set_attribute( - ErrorAttributes.ERROR_TYPE, type(error).__qualname__ + _set_response_attributes( + span, result, event_logger, capture_content ) span.end() + return result + + except Exception as error: + handle_span_exception(span, error) raise return traced_method @@ -286,10 +320,19 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): try: if exc_type is not None: - self.span.set_status(Status(StatusCode.ERROR, str(exc_val))) - self.span.set_attribute( - ErrorAttributes.ERROR_TYPE, exc_type.__qualname__ - ) + handle_span_exception(self.span, exc_val) + finally: + self.cleanup() + return False # Propagate the exception + + async def __aenter__(self): + self.setup() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + try: + if exc_type is not None: + handle_span_exception(self.span, exc_val) finally: self.cleanup() return False # Propagate the exception @@ -301,6 +344,9 @@ def close(self): def __iter__(self): return self + def __aiter__(self): + return self + def __next__(self): try: chunk = next(self.stream) @@ -310,10 +356,20 @@ def __next__(self): self.cleanup() raise except Exception as error: - self.span.set_status(Status(StatusCode.ERROR, str(error))) - self.span.set_attribute( - ErrorAttributes.ERROR_TYPE, type(error).__qualname__ - ) + handle_span_exception(self.span, error) + self.cleanup() + raise + + async def __anext__(self): + try: + chunk = await self.stream.__anext__() + self.process_chunk(chunk) + return chunk + except StopAsyncIteration: + self.cleanup() + raise + except Exception as error: + handle_span_exception(self.span, error) self.cleanup() raise diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py index a3a2d317ce..cf920c17ee 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py @@ -26,6 +26,10 @@ from opentelemetry.semconv._incubating.attributes import ( server_attributes as ServerAttributes, ) +from opentelemetry.semconv.attributes import ( + error_attributes as ErrorAttributes, +) +from opentelemetry.trace.status import Status, StatusCode OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = ( "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT" @@ -138,9 +142,11 @@ def choice_to_event(choice, capture_content): if choice.message: message = { - "role": choice.message.role - if choice.message and choice.message.role - else None + "role": ( + choice.message.role + if choice.message and choice.message.role + else None + ) } tool_calls = extract_tool_calls(choice.message, capture_content) if tool_calls: @@ -210,3 +216,12 @@ def get_llm_request_attributes( # filter out None values return {k: v for k, v in attributes.items() if v is not None} + + +def handle_span_exception(span, error): + span.set_status(Status(StatusCode.ERROR, str(error))) + if span.is_recording(): + span.set_attribute( + ErrorAttributes.ERROR_TYPE, type(error).__qualname__ + ) + span.end() diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-0.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-0.txt index 7a15734872..5e1693b69a 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-0.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-0.txt @@ -5,6 +5,7 @@ importlib-metadata==6.11.0 packaging==24.0 pytest==7.4.4 pytest-vcr==1.0.2 +pytest-asyncio==0.21.0 wrapt==1.16.0 opentelemetry-api==1.28 # when updating, also update in pyproject.toml opentelemetry-sdk==1.28 # when updating, also update in pyproject.toml diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-1.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-1.txt index ded849c8ee..618410edd3 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-1.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/test-requirements-1.txt @@ -5,6 +5,7 @@ importlib-metadata==6.11.0 packaging==24.0 pytest==7.4.4 pytest-vcr==1.0.2 +pytest-asyncio==0.21.0 wrapt==1.16.0 # test with the latest version of opentelemetry-api, sdk, and semantic conventions diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_404.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_404.yaml new file mode 100644 index 0000000000..e055e68f20 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_404.yaml @@ -0,0 +1,89 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": "Say this is a test" + } + ], + "model": "this-model-does-not-exist" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '103' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "error": { + "message": "The model `this-model-does-not-exist` does not exist or you do not have access to it.", + "type": "invalid_request_error", + "param": null, + "code": "model_not_found" + } + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80827a861852-MRS + Connection: + - keep-alive + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 13 Nov 2024 00:04:01 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '231' + openai-organization: test_openai_org_id + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Origin + x-request-id: + - req_5cf06a7fabd45ebe21ee38c14c5b2f76 + status: + code: 404 + message: Not Found +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_extra_params.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_extra_params.yaml new file mode 100644 index 0000000000..3d13c9344e --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_extra_params.yaml @@ -0,0 +1,137 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": "Say this is a test" + } + ], + "model": "gpt-4o-mini", + "max_tokens": 50, + "seed": 42, + "stream": false, + "temperature": 0.5, + "service_tier": "default" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '183' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-ASv9WMTAMZY4O1EImv3csZa6Ch7KI", + "object": "chat.completion", + "created": 1731456242, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "This is a test. How can I assist you further?", + "refusal": null + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 12, + "completion_tokens": 12, + "total_tokens": 24, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "service_tier": "default", + "system_fingerprint": "fp_0ba0d124f1" + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a8088f867e167-MRS + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 13 Nov 2024 00:04:02 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '825' + openai-organization: test_openai_org_id + openai-processing-ms: + - '488' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999943' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_6df08d6267415e8f5db3628a6757edad + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_choices.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_choices.yaml new file mode 100644 index 0000000000..1404b8163a --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_choices.yaml @@ -0,0 +1,143 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": "Say this is a test" + } + ], + "model": "gpt-4o-mini", + "n": 2, + "stream": false + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '114' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-ASv9XLlMmT7H3cf50dNTesHDBDwX5", + "object": "chat.completion", + "created": 1731456243, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "This is a test.", + "refusal": null + }, + "logprobs": null, + "finish_reason": "stop" + }, + { + "index": 1, + "message": { + "role": "assistant", + "content": "This is a test.", + "refusal": null + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 12, + "completion_tokens": 10, + "total_tokens": 22, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "system_fingerprint": "fp_0ba0d124f1" + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a808f6d8e0d8b-MRS + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 13 Nov 2024 00:04:04 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '970' + openai-organization: test_openai_org_id + openai-processing-ms: + - '306' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_1317908e0f9b73276b57d4e171c533ea + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_choices_streaming.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_choices_streaming.yaml new file mode 100644 index 0000000000..4bca03a9e8 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_choices_streaming.yaml @@ -0,0 +1,382 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "system", + "content": "You're a helpful assistant." + }, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?" + } + ], + "model": "gpt-4o-mini", + "n": 2, + "stream": true, + "stream_options": { + "include_usage": true + } + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '254' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" as"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" as"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" my"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" my"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" knowledge"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" knowledge"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" was"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" only"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" last"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" extends"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" until"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" updated"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" October"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" October"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"202"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"202"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" don't"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" have"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" access"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" don't"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" have"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" access"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" live"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" live"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" However"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" However"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" easily"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" easily"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" Seattle"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Seattle"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" by"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" visiting"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" mobile"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" If"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" accurate"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" need"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" up"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" historical"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" general"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" climate"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" those"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" cities"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" feel"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" free"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" ask"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"!"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"-to"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"-date"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + + data: {"id":"chatcmpl-ASv9hB9He94oQyZr1CDC8coqvmn5U","object":"chat.completion.chunk","created":1731456253,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":26,"completion_tokens":133,"total_tokens":159,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + + data: [DONE] + + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80ceac3ce19a-MRS + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Wed, 13 Nov 2024 00:04:13 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-organization: test_openai_org_id + openai-processing-ms: + - '126' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999945' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_5dd8b6845db59fa55cf226eda1f5a2c6 + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_tools_streaming_no_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_tools_streaming_no_content.yaml new file mode 100644 index 0000000000..19319de476 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_tools_streaming_no_content.yaml @@ -0,0 +1,164 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "system", + "content": "You're a helpful assistant." + }, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?" + } + ], + "model": "gpt-4o-mini", + "stream": true, + "stream_options": { + "include_usage": true + }, + "tool_choice": "auto", + "tools": [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. Boston, MA" + } + }, + "required": [ + "location" + ], + "additionalProperties": false + } + } + } + ] + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '602' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_hqkL24CLEwnniv4GDrjk14Iu","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"eatt"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le, W"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_0s1enkFttXjIR7ozHoGMcnUu","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"an F"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ranci"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"sco, C"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} + + data: {"id":"chatcmpl-ASv9l0RKJrq2fTx2dK5jhJoIr4rMI","object":"chat.completion.chunk","created":1731456257,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":75,"completion_tokens":51,"total_tokens":126,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + + data: [DONE] + + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80e4cfb00d86-MRS + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Wed, 13 Nov 2024 00:04:19 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-organization: test_openai_org_id + openai-processing-ms: + - '1597' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999960' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_487aef2347cb4d1f97077c488dd93628 + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_tools_streaming_with_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_tools_streaming_with_content.yaml new file mode 100644 index 0000000000..a026912ee1 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_multiple_tools_streaming_with_content.yaml @@ -0,0 +1,164 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "system", + "content": "You're a helpful assistant." + }, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?" + } + ], + "model": "gpt-4o-mini", + "stream": true, + "stream_options": { + "include_usage": true + }, + "tool_choice": "auto", + "tools": [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. Boston, MA" + } + }, + "required": [ + "location" + ], + "additionalProperties": false + } + } + } + ] + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '602' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_oJL2dc4GjWVxqBtWlGLwjbsR","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"eatt"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le, W"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_ON3lp1OWsbw2obNRD43KVDp6","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"an F"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ranci"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"sco, C"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} + + data: {"id":"chatcmpl-ASv9jfgm5JguNGaR9o9u94HpuhV7T","object":"chat.completion.chunk","created":1731456255,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":75,"completion_tokens":51,"total_tokens":126,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + + data: [DONE] + + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80d8efb9e1c8-MRS + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Wed, 13 Nov 2024 00:04:16 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-organization: test_openai_org_id + openai-processing-ms: + - '1162' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999960' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_0b6729aef347cecd61ba3b7b7a8d4719 + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_streaming.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_streaming.yaml new file mode 100644 index 0000000000..efffcd7423 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_streaming.yaml @@ -0,0 +1,117 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": "Say this is a test" + } + ], + "model": "gpt-4", + "stream": true, + "stream_options": { + "include_usage": true + } + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '142' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"This"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + + data: {"id":"chatcmpl-ASv9ejXDUtAhGOJJxWuw026zdinc4","object":"chat.completion.chunk","created":1731456250,"model":"gpt-4-0613","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":12,"completion_tokens":5,"total_tokens":17,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + + data: [DONE] + + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80bd2f31e1e5-MRS + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Wed, 13 Nov 2024 00:04:11 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-organization: test_openai_org_id + openai-processing-ms: + - '196' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '1000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '999977' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 1ms + x-request-id: + - req_cc9204ae23338b130df11c8c5b5f31af + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_streaming_not_complete.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_streaming_not_complete.yaml new file mode 100644 index 0000000000..9ef5613d17 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_streaming_not_complete.yaml @@ -0,0 +1,112 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": "Say this is a test" + } + ], + "model": "gpt-4", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '99' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-ASv9gROIIAvRs9QnmLP8Nzs3PGMCX","object":"chat.completion.chunk","created":1731456252,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9gROIIAvRs9QnmLP8Nzs3PGMCX","object":"chat.completion.chunk","created":1731456252,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"This"},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9gROIIAvRs9QnmLP8Nzs3PGMCX","object":"chat.completion.chunk","created":1731456252,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9gROIIAvRs9QnmLP8Nzs3PGMCX","object":"chat.completion.chunk","created":1731456252,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9gROIIAvRs9QnmLP8Nzs3PGMCX","object":"chat.completion.chunk","created":1731456252,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9gROIIAvRs9QnmLP8Nzs3PGMCX","object":"chat.completion.chunk","created":1731456252,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-ASv9gROIIAvRs9QnmLP8Nzs3PGMCX","object":"chat.completion.chunk","created":1731456252,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + + data: [DONE] + + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80c54d00e288-MRS + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Wed, 13 Nov 2024 00:04:12 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-organization: test_openai_org_id + openai-processing-ms: + - '283' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '1000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '999977' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 1ms + x-request-id: + - req_e9e4ea6fd060391e8cc8cfea78ad9a15 + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_tool_calls_no_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_tool_calls_no_content.yaml new file mode 100644 index 0000000000..053e271d45 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_tool_calls_no_content.yaml @@ -0,0 +1,342 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "system", + "content": "You're a helpful assistant." + }, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?" + } + ], + "model": "gpt-4o-mini", + "tool_choice": "auto", + "tools": [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. Boston, MA" + } + }, + "required": [ + "location" + ], + "additionalProperties": false + } + } + } + ] + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '543' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-ASv9bJqWatpvCC0YMsYRcTSIiXoxk", + "object": "chat.completion", + "created": 1731456247, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "call_vwOezSsB5j9ei1SSMlZjqx7g", + "type": "function", + "function": { + "name": "get_current_weather", + "arguments": "{\"location\": \"Seattle, WA\"}" + } + }, + { + "id": "call_LzeIYcKhHnVF60u4LmBpT1tv", + "type": "function", + "function": { + "name": "get_current_weather", + "arguments": "{\"location\": \"San Francisco, CA\"}" + } + } + ], + "refusal": null + }, + "logprobs": null, + "finish_reason": "tool_calls" + } + ], + "usage": { + "prompt_tokens": 75, + "completion_tokens": 51, + "total_tokens": 126, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "system_fingerprint": "fp_0ba0d124f1" + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80a9f8fbe1c9-MRS + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 13 Nov 2024 00:04:08 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '1308' + openai-organization: test_openai_org_id + openai-processing-ms: + - '808' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999960' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_f1b9b75e4a73b542c9b1b992cd52c66f + status: + code: 200 + message: OK +- request: + body: |- + { + "messages": [ + { + "role": "system", + "content": "You're a helpful assistant." + }, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?" + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_vwOezSsB5j9ei1SSMlZjqx7g", + "function": { + "arguments": "{\"location\": \"Seattle, WA\"}", + "name": "get_current_weather" + }, + "type": "function" + }, + { + "id": "call_LzeIYcKhHnVF60u4LmBpT1tv", + "function": { + "arguments": "{\"location\": \"San Francisco, CA\"}", + "name": "get_current_weather" + }, + "type": "function" + } + ] + }, + { + "role": "tool", + "content": "50 degrees and raining", + "tool_call_id": "call_vwOezSsB5j9ei1SSMlZjqx7g" + }, + { + "role": "tool", + "content": "70 degrees and sunny", + "tool_call_id": "call_LzeIYcKhHnVF60u4LmBpT1tv" + } + ], + "model": "gpt-4o-mini" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '746' + content-type: + - application/json + cookie: + - test_cookie + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-ASv9dfXfIwGCZgeWzDTbCh0FuU9kh", + "object": "chat.completion", + "created": 1731456249, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Today, the weather in Seattle is 50 degrees and raining, while in San Francisco, it's 70 degrees and sunny.", + "refusal": null + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 99, + "completion_tokens": 25, + "total_tokens": 124, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "system_fingerprint": "fp_0ba0d124f1" + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80b3baade1c9-MRS + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 13 Nov 2024 00:04:10 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '859' + openai-organization: test_openai_org_id + openai-processing-ms: + - '972' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999948' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_754e6b59f1d3da727e2210e3d8c56243 + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_tool_calls_with_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_tool_calls_with_content.yaml new file mode 100644 index 0000000000..ebebb20603 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_tool_calls_with_content.yaml @@ -0,0 +1,342 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "system", + "content": "You're a helpful assistant." + }, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?" + } + ], + "model": "gpt-4o-mini", + "tool_choice": "auto", + "tools": [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. Boston, MA" + } + }, + "required": [ + "location" + ], + "additionalProperties": false + } + } + } + ] + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '543' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-ASv9ZqgNAOJAOLYMgdmxouatKXJlk", + "object": "chat.completion", + "created": 1731456245, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "call_O8NOz8VlxosSASEsOY7LDUcP", + "type": "function", + "function": { + "name": "get_current_weather", + "arguments": "{\"location\": \"Seattle, WA\"}" + } + }, + { + "id": "call_3m7cyuckijnpiWr6tq0Tl8Mg", + "type": "function", + "function": { + "name": "get_current_weather", + "arguments": "{\"location\": \"San Francisco, CA\"}" + } + } + ], + "refusal": null + }, + "logprobs": null, + "finish_reason": "tool_calls" + } + ], + "usage": { + "prompt_tokens": 75, + "completion_tokens": 51, + "total_tokens": 126, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "system_fingerprint": "fp_0ba0d124f1" + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a8098ac5ae167-MRS + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 13 Nov 2024 00:04:06 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '1308' + openai-organization: test_openai_org_id + openai-processing-ms: + - '937' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999960' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_3cd7152d2c8c10b4f354b27165f6c2b5 + status: + code: 200 + message: OK +- request: + body: |- + { + "messages": [ + { + "role": "system", + "content": "You're a helpful assistant." + }, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?" + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_O8NOz8VlxosSASEsOY7LDUcP", + "function": { + "arguments": "{\"location\": \"Seattle, WA\"}", + "name": "get_current_weather" + }, + "type": "function" + }, + { + "id": "call_3m7cyuckijnpiWr6tq0Tl8Mg", + "function": { + "arguments": "{\"location\": \"San Francisco, CA\"}", + "name": "get_current_weather" + }, + "type": "function" + } + ] + }, + { + "role": "tool", + "content": "50 degrees and raining", + "tool_call_id": "call_O8NOz8VlxosSASEsOY7LDUcP" + }, + { + "role": "tool", + "content": "70 degrees and sunny", + "tool_call_id": "call_3m7cyuckijnpiWr6tq0Tl8Mg" + } + ], + "model": "gpt-4o-mini" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '746' + content-type: + - application/json + cookie: + - test_cookie + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-ASv9aQnGndy04lqKoPRagym1eEaQK", + "object": "chat.completion", + "created": 1731456246, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Today, Seattle is experiencing 50 degrees and raining, while San Francisco has a pleasant 70 degrees and sunny weather.", + "refusal": null + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 99, + "completion_tokens": 24, + "total_tokens": 123, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "system_fingerprint": "fp_f59a81427f" + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80a39c71e167-MRS + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 13 Nov 2024 00:04:07 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '871' + openai-organization: test_openai_org_id + openai-processing-ms: + - '477' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999948' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_193c74758ea30e77e55afe931e89fd6c + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_with_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_with_content.yaml new file mode 100644 index 0000000000..61ec4a646e --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_async_chat_completion_with_content.yaml @@ -0,0 +1,132 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": "Say this is a test" + } + ], + "model": "gpt-4o-mini", + "stream": false + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '106' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 1.26.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.26.0 + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.5 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-ASv9R2E7Yhb2e7bj4Xl0qm9s3J42Y", + "object": "chat.completion", + "created": 1731456237, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "This is a test. How can I assist you further?", + "refusal": null + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 12, + "completion_tokens": 12, + "total_tokens": 24, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "system_fingerprint": "fp_0ba0d124f1" + } + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 8e1a80679a8311a6-MRS + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 13 Nov 2024 00:03:58 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '796' + openai-organization: test_openai_org_id + openai-processing-ms: + - '359' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999978' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_41ea134c1fc450d4ca4cf8d0c6a7c53a + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_404.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_404.yaml index 0782cba71d..fb713363d5 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_404.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_404.yaml @@ -15,6 +15,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -58,13 +60,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e0ca7056be15f93-SIN + - 8e1225a16c283d93-SIN Connection: - keep-alive Content-Type: - application/json; charset=utf-8 Date: - - Mon, 11 Nov 2024 07:43:38 GMT + - Mon, 11 Nov 2024 23:43:52 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -76,13 +78,13 @@ interactions: - h3=":443"; ma=86400 content-length: - '231' - openai-organization: test_organization + openai-organization: test_openai_org_id strict-transport-security: - max-age=31536000; includeSubDomains; preload vary: - Origin x-request-id: - - req_75175efff56c313161c136c479e082ac + - req_c3e0f92d7b5426d1a4a17bb3d39953ea status: code: 404 message: Not Found diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_extra_params.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_extra_params.yaml index cfcd27074a..7cc89ad9b8 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_extra_params.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_extra_params.yaml @@ -20,6 +20,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -29,7 +31,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -39,7 +41,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -50,9 +54,9 @@ interactions: body: string: |- { - "id": "chatcmpl-APfFNvBVQx43PNOIf1dWnEUT5u5fA", + "id": "chatcmpl-ASYMT7913Sp58qhZqQgY7g7Ia2J4M", "object": "chat.completion", - "created": 1730680117, + "created": 1731368633, "model": "gpt-4o-mini-2024-07-18", "choices": [ { @@ -71,10 +75,14 @@ interactions: "completion_tokens": 12, "total_tokens": 24, "prompt_tokens_details": { - "cached_tokens": 0 + "cached_tokens": 0, + "audio_tokens": 0 }, "completion_tokens_details": { - "reasoning_tokens": 0 + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 } }, "service_tier": "default", @@ -84,13 +92,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c2d0a5a9f98-SIN + - 8e1225a3f8e9ce65-SIN Connection: - keep-alive Content-Type: - application/json Date: - - Mon, 04 Nov 2024 00:28:37 GMT + - Mon, 11 Nov 2024 23:43:53 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -103,10 +111,10 @@ interactions: alt-svc: - h3=":443"; ma=86400 content-length: - - '697' - openai-organization: test_organization + - '825' + openai-organization: test_openai_org_id openai-processing-ms: - - '228' + - '431' openai-version: - '2020-10-01' strict-transport-security: @@ -120,11 +128,11 @@ interactions: x-ratelimit-remaining-tokens: - '199943' x-ratelimit-reset-requests: - - 13.724s + - 14.746s x-ratelimit-reset-tokens: - 16ms x-request-id: - - req_0ded46a4535c24c36ef58363b7538421 + - req_81e29a8992ea8001c0240bd990acf0ab status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices.yaml index 9518e41225..23828e98f4 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices.yaml @@ -17,6 +17,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -26,7 +28,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -36,7 +38,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -47,16 +51,16 @@ interactions: body: string: |- { - "id": "chatcmpl-APfFO3w0cNVOphe5Lk9EaYyRaS2pm", + "id": "chatcmpl-ASYMUBq69UHDarAz2fsd0O50rv0r1", "object": "chat.completion", - "created": 1730680118, + "created": 1731368634, "model": "gpt-4o-mini-2024-07-18", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "This is a test.", + "content": "This is a test. How can I assist you further?", "refusal": null }, "logprobs": null, @@ -66,7 +70,7 @@ interactions: "index": 1, "message": { "role": "assistant", - "content": "This is a test! How can I assist you further?", + "content": "This is a test. How can I assist you further?", "refusal": null }, "logprobs": null, @@ -75,13 +79,17 @@ interactions: ], "usage": { "prompt_tokens": 12, - "completion_tokens": 17, - "total_tokens": 29, + "completion_tokens": 24, + "total_tokens": 36, "prompt_tokens_details": { - "cached_tokens": 0 + "cached_tokens": 0, + "audio_tokens": 0 }, "completion_tokens_details": { - "reasoning_tokens": 0 + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 } }, "system_fingerprint": "fp_0ba0d124f1" @@ -90,13 +98,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c30beda3dd4-SIN + - 8e1225a91a253e53-SIN Connection: - keep-alive Content-Type: - application/json Date: - - Mon, 04 Nov 2024 00:28:38 GMT + - Mon, 11 Nov 2024 23:43:54 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -109,10 +117,10 @@ interactions: alt-svc: - h3=":443"; ma=86400 content-length: - - '872' - openai-organization: test_organization + - '1030' + openai-organization: test_openai_org_id openai-processing-ms: - - '261' + - '399' openai-version: - '2020-10-01' strict-transport-security: @@ -126,11 +134,11 @@ interactions: x-ratelimit-remaining-tokens: - '199962' x-ratelimit-reset-requests: - - 21.785s + - 22.564s x-ratelimit-reset-tokens: - 11ms x-request-id: - - req_86ab6a7a85f95e2a001cfeb89e0827b1 + - req_01290a92a1a3d787c7a00bb3836da597 status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices_streaming.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices_streaming.yaml index e587ce5a38..ea06ca5984 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices_streaming.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_choices_streaming.yaml @@ -24,6 +24,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -33,7 +35,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -43,7 +45,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -53,237 +57,223 @@ interactions: response: body: string: |+ - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"I"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" don't"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" have"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" For"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" as"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" However"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" To"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" my"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" get"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" capabilities"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" accurate"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" do"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" latest"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" not"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" up"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" include"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" accessing"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" live"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Seattle"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Seattle"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"-to"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"-date"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" However"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" please"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" by"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" recommend"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" easily"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" checking"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" You"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" Seattle"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" like"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" also"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" use"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Channel"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" search"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" Acc"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" You"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" engine"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"u"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" also"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"Weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" ask"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" service"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" immediate"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" voice"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" assistant"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" service"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" local"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" news"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" station"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" search"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" on"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" Would"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"'s"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" online"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" Seattle"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" like"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" some"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" If"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" tips"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" there's"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"'s"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" on"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" anything"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" conditions"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" where"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" else"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" you'd"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" today"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" find"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" this"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" like"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{"content":"?"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" know"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" feel"},"logprobs":null,"finish_reason":null}],"usage":null} - - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" free"},"logprobs":null,"finish_reason":null}],"usage":null} - - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} - - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":" ask"},"logprobs":null,"finish_reason":null}],"usage":null} - - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"content":"!"},"logprobs":null,"finish_reason":null}],"usage":null} - - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - - data: {"id":"chatcmpl-APfFT3JSFkuTVECTo0DfULAD8vftU","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":26,"completion_tokens":111,"total_tokens":137,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}}} + data: {"id":"chatcmpl-ASYMaNc7XmbGRUNREnmvhyyISBHsv","object":"chat.completion.chunk","created":1731368640,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":26,"completion_tokens":104,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -291,13 +281,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c55783bce06-SIN + - 8e1225d0fa1481e4-SIN Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Mon, 04 Nov 2024 00:28:44 GMT + - Mon, 11 Nov 2024 23:44:00 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -309,9 +299,9 @@ interactions: - X-Request-ID alt-svc: - h3=":443"; ma=86400 - openai-organization: test_organization + openai-organization: test_openai_org_id openai-processing-ms: - - '127' + - '176' openai-version: - '2020-10-01' strict-transport-security: @@ -325,11 +315,11 @@ interactions: x-ratelimit-remaining-tokens: - '199945' x-ratelimit-reset-requests: - - 59.105s + - 59.369s x-ratelimit-reset-tokens: - 16ms x-request-id: - - req_4d50387aa59e08f2dc486aa5c62613f5 + - req_892a6021b1acc00254d8ff80adcd82fb status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_no_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_no_content.yaml index 8989eb9cfa..951aa52259 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_no_content.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_no_content.yaml @@ -46,6 +46,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -55,7 +57,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -65,7 +67,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -75,41 +79,41 @@ interactions: response: body: string: |+ - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_H7ADtKo1DKIzNyrCSBt1jKdw","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_j7L00WV19wwQCvKOIVZewXZm","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"eatt"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"eatt"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le, W"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le, W"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_Dmv04Iin9mOpBKWSwdb5DbpS","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_sAIOI3dvcd1YMsEqD8DI3l8B","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"an F"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"an F"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ranci"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ranci"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"sco, C"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"sco, C"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} - data: {"id":"chatcmpl-APfFYyp9joT2Wwh01iYvcm6iDYtV3","object":"chat.completion.chunk","created":1730680128,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":75,"completion_tokens":51,"total_tokens":126,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}}} + data: {"id":"chatcmpl-ASYMdbuQwnGMCODejEnbp2ufs6WgR","object":"chat.completion.chunk","created":1731368643,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[],"usage":{"prompt_tokens":75,"completion_tokens":51,"total_tokens":126,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -117,13 +121,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c715fd66beb-SIN + - 8e1225e40bed40c8-SIN Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Mon, 04 Nov 2024 00:28:49 GMT + - Mon, 11 Nov 2024 23:44:04 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -135,9 +139,9 @@ interactions: - X-Request-ID alt-svc: - h3=":443"; ma=86400 - openai-organization: test_organization + openai-organization: test_openai_org_id openai-processing-ms: - - '647' + - '1225' openai-version: - '2020-10-01' strict-transport-security: @@ -151,11 +155,11 @@ interactions: x-ratelimit-remaining-tokens: - '199961' x-ratelimit-reset-requests: - - 1m11.934s + - 1m13.588s x-ratelimit-reset-tokens: - 11ms x-request-id: - - req_66ecdeba3a9e89bbc2aa60243fb1fdfb + - req_102cd878d03c1cec0c12b95f928d03ee status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_with_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_with_content.yaml index c39facccc5..d42ac86e37 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_with_content.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_multiple_tools_streaming_with_content.yaml @@ -46,6 +46,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -55,7 +57,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -65,7 +67,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -75,41 +79,41 @@ interactions: response: body: string: |+ - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_CqwFQqzbiNttzDzEXiUitRos","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_fHCjJqt9Pysde6vcJcvbXGBx","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"eatt"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"eatt"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le, W"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le, W"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_SGVgEhhsqfE7s6MOHSXwKSGG","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_3J9foSw3CUb48lrqIXoTky6U","type":"function","function":{"name":"get_current_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"lo"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"catio"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"n\": \"S"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"an F"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"an F"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ranci"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ranci"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"sco, C"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"sco, C"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"A\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} - data: {"id":"chatcmpl-APfFVrgZUKVR18p3BuIQN3OYWjFIM","object":"chat.completion.chunk","created":1730680125,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":75,"completion_tokens":51,"total_tokens":126,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}}} + data: {"id":"chatcmpl-ASYMbACebDoWcuraMEWQhU48q4dAp","object":"chat.completion.chunk","created":1731368641,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[],"usage":{"prompt_tokens":75,"completion_tokens":51,"total_tokens":126,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -117,13 +121,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c5d48f0ce22-SIN + - 8e1225d8af0c3d93-SIN Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Mon, 04 Nov 2024 00:28:48 GMT + - Mon, 11 Nov 2024 23:44:03 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -135,9 +139,9 @@ interactions: - X-Request-ID alt-svc: - h3=":443"; ma=86400 - openai-organization: test_organization + openai-organization: test_openai_org_id openai-processing-ms: - - '2886' + - '1399' openai-version: - '2020-10-01' strict-transport-security: @@ -151,11 +155,11 @@ interactions: x-ratelimit-remaining-tokens: - '199961' x-ratelimit-reset-requests: - - 1m6.499s + - 1m6.779s x-ratelimit-reset-tokens: - 11ms x-request-id: - - req_c887b8540fec8e4eadca5d56c78cc4e1 + - req_d745d48bf030ac78e4790ee49848d05f status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming.yaml index 713ea762e0..db482a440c 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming.yaml @@ -19,6 +19,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -28,7 +30,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -38,7 +40,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -48,21 +52,21 @@ interactions: response: body: string: |+ - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"This"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\"This"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\""},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-APfFSQXvyhxafsCMim55E7utaYINv","object":"chat.completion.chunk","created":1730680122,"model":"gpt-4-0613","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":12,"completion_tokens":5,"total_tokens":17,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}}} + data: {"id":"chatcmpl-ASYMZ4oSykiIFK4lXLReDiKyAjsQl","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":12,"completion_tokens":5,"total_tokens":17,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -70,13 +74,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c4b99b74927-SIN + - 8e1225c87b273e53-SIN Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Mon, 04 Nov 2024 00:28:42 GMT + - Mon, 11 Nov 2024 23:43:59 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -88,9 +92,9 @@ interactions: - X-Request-ID alt-svc: - h3=":443"; ma=86400 - openai-organization: test_organization + openai-organization: test_openai_org_id openai-processing-ms: - - '157' + - '207' openai-version: - '2020-10-01' strict-transport-security: @@ -108,7 +112,7 @@ interactions: x-ratelimit-reset-tokens: - 132ms x-request-id: - - req_72de20dac77a9535e3f47a6ec3a39ddc + - req_c367cf360ee88481fb7cd6c5d45bf9dc status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming_not_complete.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming_not_complete.yaml index bec0658770..4d56e51a06 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming_not_complete.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_streaming_not_complete.yaml @@ -16,6 +16,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -25,7 +27,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -35,7 +37,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -45,19 +49,19 @@ interactions: response: body: string: |+ - data: {"id":"chatcmpl-APfFTxI7bvaTTeQd32CHwpV6GKo7x","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMZbRqo8Bkz53FVzaTj7W7feOn4","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFTxI7bvaTTeQd32CHwpV6GKo7x","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"This"},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMZbRqo8Bkz53FVzaTj7W7feOn4","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"This"},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFTxI7bvaTTeQd32CHwpV6GKo7x","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMZbRqo8Bkz53FVzaTj7W7feOn4","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFTxI7bvaTTeQd32CHwpV6GKo7x","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMZbRqo8Bkz53FVzaTj7W7feOn4","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFTxI7bvaTTeQd32CHwpV6GKo7x","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMZbRqo8Bkz53FVzaTj7W7feOn4","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFTxI7bvaTTeQd32CHwpV6GKo7x","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-ASYMZbRqo8Bkz53FVzaTj7W7feOn4","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-APfFTxI7bvaTTeQd32CHwpV6GKo7x","object":"chat.completion.chunk","created":1730680123,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + data: {"id":"chatcmpl-ASYMZbRqo8Bkz53FVzaTj7W7feOn4","object":"chat.completion.chunk","created":1731368639,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} data: [DONE] @@ -65,13 +69,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c505e24cdf2-SIN + - 8e1225ccb98e823b-SIN Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Mon, 04 Nov 2024 00:28:43 GMT + - Mon, 11 Nov 2024 23:43:59 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -83,9 +87,9 @@ interactions: - X-Request-ID alt-svc: - h3=":443"; ma=86400 - openai-organization: test_organization + openai-organization: test_openai_org_id openai-processing-ms: - - '269' + - '205' openai-version: - '2020-10-01' strict-transport-security: @@ -99,11 +103,11 @@ interactions: x-ratelimit-remaining-tokens: - '9978' x-ratelimit-reset-requests: - - 16.502s + - 16.601s x-ratelimit-reset-tokens: - 132ms x-request-id: - - req_04c174debe2eace86b1e2777d7ac7265 + - req_d13225164a822ec3ebab5591ed0b6d6a status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_no_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_no_content.yaml index b4d5acca4a..4731f202c3 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_no_content.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_no_content.yaml @@ -42,6 +42,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -51,7 +53,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -61,7 +63,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -72,9 +76,9 @@ interactions: body: string: |- { - "id": "chatcmpl-APfFQZB7CuqkWhGyMZegJZRX2Mqv7", + "id": "chatcmpl-ASYMW6w3m9qqpHUVhYTbQbw61zMqA", "object": "chat.completion", - "created": 1730680120, + "created": 1731368636, "model": "gpt-4o-mini-2024-07-18", "choices": [ { @@ -84,7 +88,7 @@ interactions: "content": null, "tool_calls": [ { - "id": "call_SBoEJuTov3qHLothPvvgZyO6", + "id": "call_eqbDFUdPqay2WjsSzZEiAn0U", "type": "function", "function": { "name": "get_current_weather", @@ -92,7 +96,7 @@ interactions: } }, { - "id": "call_2nL8HxSquLkKxpTENiZ17ynv", + "id": "call_tn3sgasg6GaftTdancBYJNJN", "type": "function", "function": { "name": "get_current_weather", @@ -111,10 +115,14 @@ interactions: "completion_tokens": 51, "total_tokens": 126, "prompt_tokens_details": { - "cached_tokens": 0 + "cached_tokens": 0, + "audio_tokens": 0 }, "completion_tokens_details": { - "reasoning_tokens": 0 + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 } }, "system_fingerprint": "fp_0ba0d124f1" @@ -123,13 +131,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c3f4ba7ce35-SIN + - 8e1225ba6a3440b0-SIN Connection: - keep-alive Content-Type: - application/json Date: - - Mon, 04 Nov 2024 00:28:41 GMT + - Mon, 11 Nov 2024 23:43:57 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -142,10 +150,10 @@ interactions: alt-svc: - h3=":443"; ma=86400 content-length: - - '1180' - openai-organization: test_organization + - '1308' + openai-organization: test_openai_org_id openai-processing-ms: - - '984' + - '761' openai-version: - '2020-10-01' strict-transport-security: @@ -159,11 +167,11 @@ interactions: x-ratelimit-remaining-tokens: - '199961' x-ratelimit-reset-requests: - - 45.38s + - 45.694s x-ratelimit-reset-tokens: - 11ms x-request-id: - - req_af3dca7532583ed0a68645314bcc9be0 + - req_f98710550c77b43865572fc1a7a0bc53 status: code: 200 message: OK @@ -183,7 +191,7 @@ interactions: "role": "assistant", "tool_calls": [ { - "id": "call_SBoEJuTov3qHLothPvvgZyO6", + "id": "call_eqbDFUdPqay2WjsSzZEiAn0U", "function": { "arguments": "{\"location\": \"Seattle, WA\"}", "name": "get_current_weather" @@ -191,7 +199,7 @@ interactions: "type": "function" }, { - "id": "call_2nL8HxSquLkKxpTENiZ17ynv", + "id": "call_tn3sgasg6GaftTdancBYJNJN", "function": { "arguments": "{\"location\": \"San Francisco, CA\"}", "name": "get_current_weather" @@ -203,12 +211,12 @@ interactions: { "role": "tool", "content": "50 degrees and raining", - "tool_call_id": "call_SBoEJuTov3qHLothPvvgZyO6" + "tool_call_id": "call_eqbDFUdPqay2WjsSzZEiAn0U" }, { "role": "tool", "content": "70 degrees and sunny", - "tool_call_id": "call_2nL8HxSquLkKxpTENiZ17ynv" + "tool_call_id": "call_tn3sgasg6GaftTdancBYJNJN" } ], "model": "gpt-4o-mini" @@ -218,6 +226,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -225,12 +235,11 @@ interactions: content-type: - application/json cookie: - - __cf_bm=moy9FTa50Ug99_ZQaP2atn.PgzKoQyF7pvWqG_CIf54-1730680121-1.0.1.1-mczyecbBSU5hWl8uat1lcU6ya1g6MY.Oso_vHVilj7O8C2RJrObTEyzD5DAMphUCFqBYVGHMurXg16CMssvuNw; - _cfuvid=FpMMQOb0sz4wVuQI7b1RY2KJKVNyHctKOqsWAL4tph0-1730680121454-0.0.1.1-604800000 + - test_cookie host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -240,7 +249,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -251,16 +262,16 @@ interactions: body: string: |- { - "id": "chatcmpl-APfFRbb22UJc07TGY9KcrrWO5fs9c", + "id": "chatcmpl-ASYMYObbcUyZ77rbvypWmcZPIVSf1", "object": "chat.completion", - "created": 1730680121, + "created": 1731368638, "model": "gpt-4o-mini-2024-07-18", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "Today, the weather in Seattle is 50 degrees and raining, while in San Francisco, it's 70 degrees and sunny.", + "content": "Today, the weather in Seattle is 50 degrees and raining, while San Francisco is enjoying 70 degrees and sunny weather.", "refusal": null }, "logprobs": null, @@ -272,10 +283,14 @@ interactions: "completion_tokens": 25, "total_tokens": 124, "prompt_tokens_details": { - "cached_tokens": 0 + "cached_tokens": 0, + "audio_tokens": 0 }, "completion_tokens_details": { - "reasoning_tokens": 0 + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 } }, "system_fingerprint": "fp_0ba0d124f1" @@ -284,13 +299,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c473cc4ce35-SIN + - 8e1225c12f6340b0-SIN Connection: - keep-alive Content-Type: - application/json Date: - - Mon, 04 Nov 2024 00:28:42 GMT + - Mon, 11 Nov 2024 23:43:58 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -303,10 +318,10 @@ interactions: alt-svc: - h3=":443"; ma=86400 content-length: - - '731' - openai-organization: test_organization + - '870' + openai-organization: test_openai_org_id openai-processing-ms: - - '380' + - '722' openai-version: - '2020-10-01' strict-transport-security: @@ -318,13 +333,13 @@ interactions: x-ratelimit-remaining-requests: - '9993' x-ratelimit-remaining-tokens: - - '199947' + - '199948' x-ratelimit-reset-requests: - - 52.749s + - 53.254s x-ratelimit-reset-tokens: - 15ms x-request-id: - - req_f56de3d8b168900542a01b971b367a9f + - req_2bb3d10a2a75f5d64a24aef595a0d8dd status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_with_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_with_content.yaml index fbf1abd640..6ceb618d24 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_with_content.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_tool_calls_with_content.yaml @@ -42,6 +42,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -51,7 +53,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -61,7 +63,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -72,9 +76,9 @@ interactions: body: string: |- { - "id": "chatcmpl-APfFOuWLsYvVpZYqENRo9rKnifNdD", + "id": "chatcmpl-ASYMU9Ntix7ePttk0MSuerJstef6U", "object": "chat.completion", - "created": 1730680118, + "created": 1731368634, "model": "gpt-4o-mini-2024-07-18", "choices": [ { @@ -84,7 +88,7 @@ interactions: "content": null, "tool_calls": [ { - "id": "call_6YvurIMPOgMo2q6V6KGJDA6g", + "id": "call_JpNb8OiAkbIbHzDggfpdDHpi", "type": "function", "function": { "name": "get_current_weather", @@ -92,7 +96,7 @@ interactions: } }, { - "id": "call_QSiqEulo25M3NtTLVbMGqY91", + "id": "call_vaFQc3zK6hHTRZKXRI5Eo2cJ", "type": "function", "function": { "name": "get_current_weather", @@ -111,10 +115,14 @@ interactions: "completion_tokens": 51, "total_tokens": 126, "prompt_tokens_details": { - "cached_tokens": 0 + "cached_tokens": 0, + "audio_tokens": 0 }, "completion_tokens_details": { - "reasoning_tokens": 0 + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 } }, "system_fingerprint": "fp_0ba0d124f1" @@ -123,13 +131,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c349cb38bcb-SIN + - 8e1225ae3ea281e4-SIN Connection: - keep-alive Content-Type: - application/json Date: - - Mon, 04 Nov 2024 00:28:39 GMT + - Mon, 11 Nov 2024 23:43:55 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -142,10 +150,10 @@ interactions: alt-svc: - h3=":443"; ma=86400 content-length: - - '1180' - openai-organization: test_organization + - '1308' + openai-organization: test_openai_org_id openai-processing-ms: - - '631' + - '748' openai-version: - '2020-10-01' strict-transport-security: @@ -159,11 +167,11 @@ interactions: x-ratelimit-remaining-tokens: - '199961' x-ratelimit-reset-requests: - - 29.791s + - 30.375s x-ratelimit-reset-tokens: - 11ms x-request-id: - - req_a121f023915b3c4a2ca2415663e54dd8 + - req_a0c3b432eb349a35b6d8dde6e01451e4 status: code: 200 message: OK @@ -183,7 +191,7 @@ interactions: "role": "assistant", "tool_calls": [ { - "id": "call_6YvurIMPOgMo2q6V6KGJDA6g", + "id": "call_JpNb8OiAkbIbHzDggfpdDHpi", "function": { "arguments": "{\"location\": \"Seattle, WA\"}", "name": "get_current_weather" @@ -191,7 +199,7 @@ interactions: "type": "function" }, { - "id": "call_QSiqEulo25M3NtTLVbMGqY91", + "id": "call_vaFQc3zK6hHTRZKXRI5Eo2cJ", "function": { "arguments": "{\"location\": \"San Francisco, CA\"}", "name": "get_current_weather" @@ -203,12 +211,12 @@ interactions: { "role": "tool", "content": "50 degrees and raining", - "tool_call_id": "call_6YvurIMPOgMo2q6V6KGJDA6g" + "tool_call_id": "call_JpNb8OiAkbIbHzDggfpdDHpi" }, { "role": "tool", "content": "70 degrees and sunny", - "tool_call_id": "call_QSiqEulo25M3NtTLVbMGqY91" + "tool_call_id": "call_vaFQc3zK6hHTRZKXRI5Eo2cJ" } ], "model": "gpt-4o-mini" @@ -218,6 +226,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -225,12 +235,11 @@ interactions: content-type: - application/json cookie: - - __cf_bm=Fyf0tyDn1mwa_82CHRs1nppW_PehJkXoYvNM4ZjPbXY-1730680119-1.0.1.1-yjV4_xtp96WEMEexlzeQsjZfgRKwMtgjBU5ysQffHMNwXbTqmZdE_pCTtQTmJ97xP37rkkAHdzC7O8661FKZ9A; - _cfuvid=WLvZI_.UlWSOmjux14bNggQUKClph3WCDG.6pjaRbjI-1730680119424-0.0.1.1-604800000 + - test_cookie host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -240,7 +249,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -251,9 +262,9 @@ interactions: body: string: |- { - "id": "chatcmpl-APfFP2ZYRsgIvw0HuTVbYFDxFs7Si", + "id": "chatcmpl-ASYMVzdmBGDbUoHFmt6R16tdtZUzR", "object": "chat.completion", - "created": 1730680119, + "created": 1731368635, "model": "gpt-4o-mini-2024-07-18", "choices": [ { @@ -272,25 +283,29 @@ interactions: "completion_tokens": 25, "total_tokens": 124, "prompt_tokens_details": { - "cached_tokens": 0 + "cached_tokens": 0, + "audio_tokens": 0 }, "completion_tokens_details": { - "reasoning_tokens": 0 + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 } }, - "system_fingerprint": "fp_0ba0d124f1" + "system_fingerprint": "fp_9b78b61c52" } headers: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c3ad9138bcb-SIN + - 8e1225b4cbf581e4-SIN Connection: - keep-alive Content-Type: - application/json Date: - - Mon, 04 Nov 2024 00:28:40 GMT + - Mon, 11 Nov 2024 23:43:56 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -303,10 +318,10 @@ interactions: alt-svc: - h3=":443"; ma=86400 content-length: - - '731' - openai-organization: test_organization + - '859' + openai-organization: test_openai_org_id openai-processing-ms: - - '390' + - '531' openai-version: - '2020-10-01' strict-transport-security: @@ -320,11 +335,11 @@ interactions: x-ratelimit-remaining-tokens: - '199947' x-ratelimit-reset-requests: - - 37.448s + - 37.973s x-ratelimit-reset-tokens: - 15ms x-request-id: - - req_525d3b9301fff00a98647143a9c26b6f + - req_22b79d2dddb920f55e33727d06724978 status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_with_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_with_content.yaml index bb61c001fb..2abb443fe3 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_with_content.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_with_content.yaml @@ -16,6 +16,8 @@ interactions: - application/json accept-encoding: - gzip, deflate + authorization: + - Bearer test_openai_api_key connection: - keep-alive content-length: @@ -25,7 +27,7 @@ interactions: host: - api.openai.com user-agent: - - OpenAI/Python 1.26.0 + - OpenAI/Python 1.54.3 x-stainless-arch: - arm64 x-stainless-async: @@ -35,7 +37,9 @@ interactions: x-stainless-os: - MacOS x-stainless-package-version: - - 1.26.0 + - 1.54.3 + x-stainless-retry-count: + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -46,16 +50,16 @@ interactions: body: string: |- { - "id": "chatcmpl-APfFJNBzCZVUpMJMK5KBhu4D6yVAc", + "id": "chatcmpl-ASYMQRl3A3DXL9FWCK9tnGRcKIO7q", "object": "chat.completion", - "created": 1730680113, + "created": 1731368630, "model": "gpt-4o-mini-2024-07-18", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "This is a test! How can I assist you today?", + "content": "This is a test.", "refusal": null }, "logprobs": null, @@ -64,13 +68,17 @@ interactions: ], "usage": { "prompt_tokens": 12, - "completion_tokens": 12, - "total_tokens": 24, + "completion_tokens": 5, + "total_tokens": 17, "prompt_tokens_details": { - "cached_tokens": 0 + "cached_tokens": 0, + "audio_tokens": 0 }, "completion_tokens_details": { - "reasoning_tokens": 0 + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 } }, "system_fingerprint": "fp_0ba0d124f1" @@ -79,13 +87,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8dd07c16eae44a1d-SIN + - 8e122593ff368bc8-SIN Connection: - keep-alive Content-Type: - application/json Date: - - Mon, 04 Nov 2024 00:28:34 GMT + - Mon, 11 Nov 2024 23:43:50 GMT Server: - cloudflare Set-Cookie: test_set_cookie @@ -98,10 +106,10 @@ interactions: alt-svc: - h3=":443"; ma=86400 content-length: - - '666' - openai-organization: test_organization + - '765' + openai-organization: test_openai_org_id openai-processing-ms: - - '732' + - '287' openai-version: - '2020-10-01' strict-transport-security: @@ -119,7 +127,7 @@ interactions: x-ratelimit-reset-tokens: - 6ms x-request-id: - - req_0bb3c9da7d953e477d1947130d2cf1df + - req_58cff97afd0e7c0bba910ccf0b044a6f status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py index 899b2f122c..7ff7e46777 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py @@ -5,7 +5,7 @@ import pytest import yaml -from openai import OpenAI +from openai import AsyncOpenAI, OpenAI from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor from opentelemetry.instrumentation.openai_v2.utils import ( @@ -55,7 +55,7 @@ def fixture_event_logger_provider(log_exporter): @pytest.fixture(autouse=True) def environment(): if not os.getenv("OPENAI_API_KEY"): - os.environ["OPENAI_API_KEY"] = "test-api-key" + os.environ["OPENAI_API_KEY"] = "test_openai_api_key" @pytest.fixture @@ -63,10 +63,20 @@ def openai_client(): return OpenAI() +@pytest.fixture +def async_openai_client(): + return AsyncOpenAI() + + @pytest.fixture(scope="module") def vcr_config(): return { - "filter_headers": ["authorization", "api-key"], + "filter_headers": [ + ("cookie", "test_cookie"), + ("authorization", "Bearer test_openai_api_key"), + ("openai-organization", "test_openai_org_id"), + ("openai-project", "test_openai_project_id"), + ], "decode_compressed_response": True, "before_record_response": scrub_response_headers, } @@ -171,6 +181,6 @@ def scrub_response_headers(response): """ This scrubs sensitive response headers. Note they are case-sensitive! """ - response["headers"]["openai-organization"] = "test_organization" + response["headers"]["openai-organization"] = "test_openai_org_id" response["headers"]["Set-Cookie"] = "test_set_cookie" return response diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py new file mode 100644 index 0000000000..1c4b3cb7dd --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py @@ -0,0 +1,847 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# pylint: disable=too-many-locals + +from typing import Optional + +import pytest +from openai import APIConnectionError, AsyncOpenAI, NotFoundError +from openai.resources.chat.completions import ChatCompletion + +from opentelemetry.sdk.trace import ReadableSpan +from opentelemetry.semconv._incubating.attributes import ( + error_attributes as ErrorAttributes, +) +from opentelemetry.semconv._incubating.attributes import ( + event_attributes as EventAttributes, +) +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAIAttributes, +) +from opentelemetry.semconv._incubating.attributes import ( + server_attributes as ServerAttributes, +) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_with_content( + span_exporter, log_exporter, async_openai_client, instrument_with_content +): + llm_model_value = "gpt-4o-mini" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + response = await async_openai_client.chat.completions.create( + messages=messages_value, model=llm_model_value, stream=False + ) + + spans = span_exporter.get_finished_spans() + assert_completion_attributes(spans[0], llm_model_value, response) + + logs = log_exporter.get_finished_logs() + assert len(logs) == 2 + + user_message = {"content": messages_value[0]["content"]} + assert_message_in_logs( + logs[0], "gen_ai.user.message", user_message, spans[0] + ) + + choice_event = { + "index": 0, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": response.choices[0].message.content, + }, + } + assert_message_in_logs(logs[1], "gen_ai.choice", choice_event, spans[0]) + + +@pytest.mark.asyncio() +async def test_async_chat_completion_bad_endpoint( + span_exporter, instrument_no_content +): + llm_model_value = "gpt-4o-mini" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + client = AsyncOpenAI(base_url="http://localhost:4242") + + with pytest.raises(APIConnectionError): + await client.chat.completions.create( + messages=messages_value, + model=llm_model_value, + timeout=0.1, + ) + + spans = span_exporter.get_finished_spans() + assert_all_attributes( + spans[0], llm_model_value, server_address="localhost" + ) + assert 4242 == spans[0].attributes[ServerAttributes.SERVER_PORT] + assert ( + "APIConnectionError" == spans[0].attributes[ErrorAttributes.ERROR_TYPE] + ) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_404( + span_exporter, async_openai_client, instrument_no_content +): + llm_model_value = "this-model-does-not-exist" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + with pytest.raises(NotFoundError): + await async_openai_client.chat.completions.create( + messages=messages_value, + model=llm_model_value, + ) + + spans = span_exporter.get_finished_spans() + + assert_all_attributes(spans[0], llm_model_value) + assert "NotFoundError" == spans[0].attributes[ErrorAttributes.ERROR_TYPE] + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_extra_params( + span_exporter, async_openai_client, instrument_no_content +): + llm_model_value = "gpt-4o-mini" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + response = await async_openai_client.chat.completions.create( + messages=messages_value, + model=llm_model_value, + seed=42, + temperature=0.5, + max_tokens=50, + stream=False, + extra_body={"service_tier": "default"}, + ) + + spans = span_exporter.get_finished_spans() + assert_completion_attributes(spans[0], llm_model_value, response) + assert ( + spans[0].attributes[GenAIAttributes.GEN_AI_OPENAI_REQUEST_SEED] == 42 + ) + assert ( + spans[0].attributes[GenAIAttributes.GEN_AI_REQUEST_TEMPERATURE] == 0.5 + ) + assert spans[0].attributes[GenAIAttributes.GEN_AI_REQUEST_MAX_TOKENS] == 50 + assert ( + spans[0].attributes[GenAIAttributes.GEN_AI_OPENAI_REQUEST_SERVICE_TIER] + == "default" + ) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_multiple_choices( + span_exporter, log_exporter, async_openai_client, instrument_with_content +): + llm_model_value = "gpt-4o-mini" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + response = await async_openai_client.chat.completions.create( + messages=messages_value, model=llm_model_value, n=2, stream=False + ) + + spans = span_exporter.get_finished_spans() + assert_completion_attributes(spans[0], llm_model_value, response) + + logs = log_exporter.get_finished_logs() + assert len(logs) == 3 # 1 user message + 2 choice messages + + user_message = {"content": messages_value[0]["content"]} + assert_message_in_logs( + logs[0], "gen_ai.user.message", user_message, spans[0] + ) + + choice_event_0 = { + "index": 0, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": response.choices[0].message.content, + }, + } + assert_message_in_logs(logs[1], "gen_ai.choice", choice_event_0, spans[0]) + + choice_event_1 = { + "index": 1, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": response.choices[1].message.content, + }, + } + assert_message_in_logs(logs[2], "gen_ai.choice", choice_event_1, spans[0]) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_tool_calls_with_content( + span_exporter, log_exporter, async_openai_client, instrument_with_content +): + await chat_completion_tool_call( + span_exporter, log_exporter, async_openai_client, True + ) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_tool_calls_no_content( + span_exporter, log_exporter, async_openai_client, instrument_no_content +): + await chat_completion_tool_call( + span_exporter, log_exporter, async_openai_client, False + ) + + +async def chat_completion_tool_call( + span_exporter, log_exporter, async_openai_client, expect_content +): + llm_model_value = "gpt-4o-mini" + messages_value = [ + {"role": "system", "content": "You're a helpful assistant."}, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?", + }, + ] + + response_0 = await async_openai_client.chat.completions.create( + messages=messages_value, + model=llm_model_value, + tool_choice="auto", + tools=[get_current_weather_tool_definition()], + ) + + # sanity check + assert "tool_calls" in response_0.choices[0].finish_reason + + # final request + messages_value.append( + { + "role": "assistant", + "tool_calls": response_0.choices[0].message.to_dict()[ + "tool_calls" + ], + } + ) + + tool_call_result_0 = { + "role": "tool", + "content": "50 degrees and raining", + "tool_call_id": response_0.choices[0].message.tool_calls[0].id, + } + tool_call_result_1 = { + "role": "tool", + "content": "70 degrees and sunny", + "tool_call_id": response_0.choices[0].message.tool_calls[1].id, + } + + messages_value.append(tool_call_result_0) + messages_value.append(tool_call_result_1) + + response_1 = await async_openai_client.chat.completions.create( + messages=messages_value, model=llm_model_value + ) + + # sanity check + assert "stop" in response_1.choices[0].finish_reason + + # validate both calls + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + assert_completion_attributes(spans[0], llm_model_value, response_0) + assert_completion_attributes(spans[1], llm_model_value, response_1) + + logs = log_exporter.get_finished_logs() + assert len(logs) == 9 # 3 logs for first completion, 6 for second + + # call one + system_message = ( + {"content": messages_value[0]["content"]} if expect_content else None + ) + assert_message_in_logs( + logs[0], "gen_ai.system.message", system_message, spans[0] + ) + + user_message = ( + {"content": messages_value[1]["content"]} if expect_content else None + ) + assert_message_in_logs( + logs[1], "gen_ai.user.message", user_message, spans[0] + ) + + function_call_0 = {"name": "get_current_weather"} + function_call_1 = {"name": "get_current_weather"} + if expect_content: + function_call_0["arguments"] = ( + response_0.choices[0] + .message.tool_calls[0] + .function.arguments.replace("\n", "") + ) + function_call_1["arguments"] = ( + response_0.choices[0] + .message.tool_calls[1] + .function.arguments.replace("\n", "") + ) + + choice_event = { + "index": 0, + "finish_reason": "tool_calls", + "message": { + "role": "assistant", + "tool_calls": [ + { + "id": response_0.choices[0].message.tool_calls[0].id, + "type": "function", + "function": function_call_0, + }, + { + "id": response_0.choices[0].message.tool_calls[1].id, + "type": "function", + "function": function_call_1, + }, + ], + }, + } + assert_message_in_logs(logs[2], "gen_ai.choice", choice_event, spans[0]) + + # call two + system_message = ( + {"content": messages_value[0]["content"]} if expect_content else None + ) + assert_message_in_logs( + logs[3], "gen_ai.system.message", system_message, spans[1] + ) + + user_message = ( + {"content": messages_value[1]["content"]} if expect_content else None + ) + assert_message_in_logs( + logs[4], "gen_ai.user.message", user_message, spans[1] + ) + + assistant_tool_call = {"tool_calls": messages_value[2]["tool_calls"]} + if not expect_content: + assistant_tool_call["tool_calls"][0]["function"]["arguments"] = None + assistant_tool_call["tool_calls"][1]["function"]["arguments"] = None + + assert_message_in_logs( + logs[5], "gen_ai.assistant.message", assistant_tool_call, spans[1] + ) + + tool_message_0 = { + "id": tool_call_result_0["tool_call_id"], + "content": tool_call_result_0["content"] if expect_content else None, + } + + assert_message_in_logs( + logs[6], "gen_ai.tool.message", tool_message_0, spans[1] + ) + + tool_message_1 = { + "id": tool_call_result_1["tool_call_id"], + "content": tool_call_result_1["content"] if expect_content else None, + } + + assert_message_in_logs( + logs[7], "gen_ai.tool.message", tool_message_1, spans[1] + ) + + message = { + "role": "assistant", + "content": response_1.choices[0].message.content + if expect_content + else None, + } + choice = { + "index": 0, + "finish_reason": "stop", + "message": message, + } + assert_message_in_logs(logs[8], "gen_ai.choice", choice, spans[1]) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_streaming( + span_exporter, log_exporter, async_openai_client, instrument_with_content +): + llm_model_value = "gpt-4" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + kwargs = { + "model": llm_model_value, + "messages": messages_value, + "stream": True, + "stream_options": {"include_usage": True}, + } + + response_stream_usage = None + response_stream_model = None + response_stream_id = None + response_stream_result = "" + response = await async_openai_client.chat.completions.create(**kwargs) + async for chunk in response: + if chunk.choices: + response_stream_result += chunk.choices[0].delta.content or "" + + # get the last chunk + if getattr(chunk, "usage", None): + response_stream_usage = chunk.usage + response_stream_model = chunk.model + response_stream_id = chunk.id + + spans = span_exporter.get_finished_spans() + assert_all_attributes( + spans[0], + llm_model_value, + response_stream_id, + response_stream_model, + response_stream_usage.prompt_tokens, + response_stream_usage.completion_tokens, + ) + + logs = log_exporter.get_finished_logs() + assert len(logs) == 2 + + user_message = {"content": "Say this is a test"} + assert_message_in_logs( + logs[0], "gen_ai.user.message", user_message, spans[0] + ) + + choice_event = { + "index": 0, + "finish_reason": "stop", + "message": {"role": "assistant", "content": response_stream_result}, + } + assert_message_in_logs(logs[1], "gen_ai.choice", choice_event, spans[0]) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_streaming_not_complete( + span_exporter, log_exporter, async_openai_client, instrument_with_content +): + llm_model_value = "gpt-4" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + kwargs = { + "model": llm_model_value, + "messages": messages_value, + "stream": True, + } + + response_stream_model = None + response_stream_id = None + response_stream_result = "" + response = await async_openai_client.chat.completions.create(**kwargs) + idx = 0 + async for chunk in response: + if chunk.choices: + response_stream_result += chunk.choices[0].delta.content or "" + if idx == 1: + # fake a stop + break + + if chunk.model: + response_stream_model = chunk.model + if chunk.id: + response_stream_id = chunk.id + idx += 1 + + response.close() + spans = span_exporter.get_finished_spans() + assert_all_attributes( + spans[0], llm_model_value, response_stream_id, response_stream_model + ) + + logs = log_exporter.get_finished_logs() + assert len(logs) == 2 + + user_message = {"content": "Say this is a test"} + assert_message_in_logs( + logs[0], "gen_ai.user.message", user_message, spans[0] + ) + + choice_event = { + "index": 0, + "finish_reason": "error", + "message": {"role": "assistant", "content": response_stream_result}, + } + assert_message_in_logs(logs[1], "gen_ai.choice", choice_event, spans[0]) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_multiple_choices_streaming( + span_exporter, log_exporter, async_openai_client, instrument_with_content +): + llm_model_value = "gpt-4o-mini" + messages_value = [ + {"role": "system", "content": "You're a helpful assistant."}, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?", + }, + ] + + response_0 = await async_openai_client.chat.completions.create( + messages=messages_value, + model=llm_model_value, + n=2, + stream=True, + stream_options={"include_usage": True}, + ) + + # two strings for each choice + response_stream_result = ["", ""] + finish_reasons = ["", ""] + async for chunk in response_0: + if chunk.choices: + for choice in chunk.choices: + response_stream_result[choice.index] += ( + choice.delta.content or "" + ) + if choice.finish_reason: + finish_reasons[choice.index] = choice.finish_reason + + # get the last chunk + if getattr(chunk, "usage", None): + response_stream_usage = chunk.usage + response_stream_model = chunk.model + response_stream_id = chunk.id + + # sanity check + assert "stop" == finish_reasons[0] + + spans = span_exporter.get_finished_spans() + assert_all_attributes( + spans[0], + llm_model_value, + response_stream_id, + response_stream_model, + response_stream_usage.prompt_tokens, + response_stream_usage.completion_tokens, + ) + + logs = log_exporter.get_finished_logs() + assert len(logs) == 4 + + system_message = {"content": messages_value[0]["content"]} + assert_message_in_logs( + logs[0], "gen_ai.system.message", system_message, spans[0] + ) + + user_message = { + "content": "What's the weather in Seattle and San Francisco today?" + } + assert_message_in_logs( + logs[1], "gen_ai.user.message", user_message, spans[0] + ) + + choice_event_0 = { + "index": 0, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": "".join(response_stream_result[0]), + }, + } + assert_message_in_logs(logs[2], "gen_ai.choice", choice_event_0, spans[0]) + + choice_event_1 = { + "index": 1, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": "".join(response_stream_result[1]), + }, + } + assert_message_in_logs(logs[3], "gen_ai.choice", choice_event_1, spans[0]) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_multiple_tools_streaming_with_content( + span_exporter, log_exporter, async_openai_client, instrument_with_content +): + await async_chat_completion_multiple_tools_streaming( + span_exporter, log_exporter, async_openai_client, True + ) + + +@pytest.mark.vcr() +@pytest.mark.asyncio() +async def test_async_chat_completion_multiple_tools_streaming_no_content( + span_exporter, log_exporter, async_openai_client, instrument_no_content +): + await async_chat_completion_multiple_tools_streaming( + span_exporter, log_exporter, async_openai_client, False + ) + + +async def async_chat_completion_multiple_tools_streaming( + span_exporter, log_exporter, async_openai_client, expect_content +): + llm_model_value = "gpt-4o-mini" + messages_value = [ + {"role": "system", "content": "You're a helpful assistant."}, + { + "role": "user", + "content": "What's the weather in Seattle and San Francisco today?", + }, + ] + + response = await async_openai_client.chat.completions.create( + messages=messages_value, + model=llm_model_value, + tool_choice="auto", + tools=[get_current_weather_tool_definition()], + stream=True, + stream_options={"include_usage": True}, + ) + + finish_reason = None + # two tools + tool_names = ["", ""] + tool_call_ids = ["", ""] + tool_args = ["", ""] + async for chunk in response: + if chunk.choices: + if chunk.choices[0].finish_reason: + finish_reason = chunk.choices[0].finish_reason + for tool_call in chunk.choices[0].delta.tool_calls or []: + t_idx = tool_call.index + if tool_call.id: + tool_call_ids[t_idx] = tool_call.id + if tool_call.function: + if tool_call.function.arguments: + tool_args[t_idx] += tool_call.function.arguments + if tool_call.function.name: + tool_names[t_idx] = tool_call.function.name + + # get the last chunk + if getattr(chunk, "usage", None): + response_stream_usage = chunk.usage + response_stream_model = chunk.model + response_stream_id = chunk.id + + # sanity check + assert "tool_calls" == finish_reason + + spans = span_exporter.get_finished_spans() + assert_all_attributes( + spans[0], + llm_model_value, + response_stream_id, + response_stream_model, + response_stream_usage.prompt_tokens, + response_stream_usage.completion_tokens, + ) + + logs = log_exporter.get_finished_logs() + assert len(logs) == 3 + + system_message = ( + {"content": messages_value[0]["content"]} if expect_content else None + ) + assert_message_in_logs( + logs[0], "gen_ai.system.message", system_message, spans[0] + ) + + user_message = ( + {"content": "What's the weather in Seattle and San Francisco today?"} + if expect_content + else None + ) + assert_message_in_logs( + logs[1], "gen_ai.user.message", user_message, spans[0] + ) + + choice_event = { + "index": 0, + "finish_reason": "tool_calls", + "message": { + "role": "assistant", + "tool_calls": [ + { + "id": tool_call_ids[0], + "type": "function", + "function": { + "name": tool_names[0], + "arguments": ( + tool_args[0].replace("\n", "") + if expect_content + else None + ), + }, + }, + { + "id": tool_call_ids[1], + "type": "function", + "function": { + "name": tool_names[1], + "arguments": ( + tool_args[1].replace("\n", "") + if expect_content + else None + ), + }, + }, + ], + }, + } + assert_message_in_logs(logs[2], "gen_ai.choice", choice_event, spans[0]) + + +def assert_message_in_logs(log, event_name, expected_content, parent_span): + assert log.log_record.attributes[EventAttributes.EVENT_NAME] == event_name + assert ( + log.log_record.attributes[GenAIAttributes.GEN_AI_SYSTEM] + == GenAIAttributes.GenAiSystemValues.OPENAI.value + ) + + if not expected_content: + assert not log.log_record.body + else: + assert log.log_record.body + assert dict(log.log_record.body) == remove_none_values( + expected_content + ) + assert_log_parent(log, parent_span) + + +def remove_none_values(body): + result = {} + for key, value in body.items(): + if value is None: + continue + if isinstance(value, dict): + result[key] = remove_none_values(value) + elif isinstance(value, list): + result[key] = [remove_none_values(i) for i in value] + else: + result[key] = value + return result + + +def assert_completion_attributes( + span: ReadableSpan, + request_model: str, + response: ChatCompletion, + operation_name: str = "chat", + server_address: str = "api.openai.com", +): + return assert_all_attributes( + span, + request_model, + response.id, + response.model, + response.usage.prompt_tokens, + response.usage.completion_tokens, + operation_name, + server_address, + ) + + +def assert_all_attributes( + span: ReadableSpan, + request_model: str, + response_id: str = None, + response_model: str = None, + input_tokens: Optional[int] = None, + output_tokens: Optional[int] = None, + operation_name: str = "chat", + server_address: str = "api.openai.com", +): + assert span.name == f"{operation_name} {request_model}" + assert ( + operation_name + == span.attributes[GenAIAttributes.GEN_AI_OPERATION_NAME] + ) + assert ( + GenAIAttributes.GenAiSystemValues.OPENAI.value + == span.attributes[GenAIAttributes.GEN_AI_SYSTEM] + ) + assert ( + request_model == span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] + ) + if response_model: + assert ( + response_model + == span.attributes[GenAIAttributes.GEN_AI_RESPONSE_MODEL] + ) + else: + assert GenAIAttributes.GEN_AI_RESPONSE_MODEL not in span.attributes + + if response_id: + assert ( + response_id == span.attributes[GenAIAttributes.GEN_AI_RESPONSE_ID] + ) + else: + assert GenAIAttributes.GEN_AI_RESPONSE_ID not in span.attributes + + if input_tokens: + assert ( + input_tokens + == span.attributes[GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS] + ) + else: + assert GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS not in span.attributes + + if output_tokens: + assert ( + output_tokens + == span.attributes[GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS] + ) + else: + assert ( + GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS not in span.attributes + ) + + assert server_address == span.attributes[ServerAttributes.SERVER_ADDRESS] + + +def assert_log_parent(log, span): + assert log.log_record.trace_id == span.get_span_context().trace_id + assert log.log_record.span_id == span.get_span_context().span_id + assert log.log_record.trace_flags == span.get_span_context().trace_flags + + +def get_current_weather_tool_definition(): + return { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. Boston, MA", + }, + }, + "required": ["location"], + "additionalProperties": False, + }, + }, + } diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py index c497ae4564..f32ef3514b 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py @@ -67,7 +67,7 @@ def test_instrumentor_connect(self): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.aiopg ) @@ -96,7 +96,7 @@ async def _ctx_manager_connect(): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.aiopg ) @@ -117,7 +117,7 @@ def test_instrumentor_create_pool(self): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.aiopg ) @@ -148,7 +148,7 @@ async def _ctx_manager_pool(): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.aiopg ) diff --git a/instrumentation/opentelemetry-instrumentation-cassandra/tests/test_cassandra_integration.py b/instrumentation/opentelemetry-instrumentation-cassandra/tests/test_cassandra_integration.py index ed488ab07f..9a36bb9069 100644 --- a/instrumentation/opentelemetry-instrumentation-cassandra/tests/test_cassandra_integration.py +++ b/instrumentation/opentelemetry-instrumentation-cassandra/tests/test_cassandra_integration.py @@ -78,7 +78,7 @@ def test_instrumentor( span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.cassandra ) self.assertEqual(span.name, "Cassandra") diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/README.rst b/instrumentation/opentelemetry-instrumentation-confluent-kafka/README.rst index 163c2a4393..1ce6dcbd26 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/README.rst +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/README.rst @@ -19,5 +19,5 @@ Installation References ---------- -* `OpenTelemetry confluent-kafka/ Tracing `_ +* `OpenTelemetry confluent-kafka/ Tracing `_ * `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py index fc3911f744..d8db967f47 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py @@ -190,6 +190,7 @@ def instrument_connection( capture_parameters: bool = False, enable_commenter: bool = False, commenter_options: dict = None, + connect_module: typing.Callable[..., typing.Any] = None, ): """Enable instrumentation in a database connection. @@ -204,6 +205,7 @@ def instrument_connection( capture_parameters: Configure if db.statement.parameters should be captured. enable_commenter: Flag to enable/disable sqlcommenter. commenter_options: Configurations for tags to be appended at the sql query. + connect_module: Module name where connect method is available. Returns: An instrumented connection. @@ -221,6 +223,7 @@ def instrument_connection( capture_parameters=capture_parameters, enable_commenter=enable_commenter, commenter_options=commenter_options, + connect_module=connect_module, ) db_integration.get_connection_attributes(connection) return get_traced_connection_proxy(connection, db_integration) @@ -492,49 +495,54 @@ def traced_execution( with self._db_api_integration._tracer.start_as_current_span( name, kind=SpanKind.CLIENT ) as span: - self._populate_span(span, cursor, *args) - if args and self._commenter_enabled: - try: - args_list = list(args) - - # lazy capture of mysql-connector client version using cursor - if ( - self._db_api_integration.database_system == "mysql" - and self._db_api_integration.connect_module.__name__ - == "mysql.connector" - and not self._db_api_integration.commenter_data[ - "mysql_client_version" - ] - ): - self._db_api_integration.commenter_data[ - "mysql_client_version" - ] = cursor._cnx._cmysql.get_client_info() - - commenter_data = dict( - self._db_api_integration.commenter_data - ) - if self._commenter_options.get( - "opentelemetry_values", True - ): - commenter_data.update(**_get_opentelemetry_values()) - - # Filter down to just the requested attributes. - commenter_data = { - k: v - for k, v in commenter_data.items() - if self._commenter_options.get(k, True) - } - statement = _add_sql_comment( - args_list[0], **commenter_data - ) - - args_list[0] = statement - args = tuple(args_list) - - except Exception as exc: # pylint: disable=broad-except - _logger.exception( - "Exception while generating sql comment: %s", exc - ) + if span.is_recording(): + if args and self._commenter_enabled: + try: + args_list = list(args) + + # lazy capture of mysql-connector client version using cursor + if ( + self._db_api_integration.database_system == "mysql" + and self._db_api_integration.connect_module.__name__ + == "mysql.connector" + and not self._db_api_integration.commenter_data[ + "mysql_client_version" + ] + ): + self._db_api_integration.commenter_data[ + "mysql_client_version" + ] = cursor._cnx._cmysql.get_client_info() + + commenter_data = dict( + self._db_api_integration.commenter_data + ) + if self._commenter_options.get( + "opentelemetry_values", True + ): + commenter_data.update( + **_get_opentelemetry_values() + ) + + # Filter down to just the requested attributes. + commenter_data = { + k: v + for k, v in commenter_data.items() + if self._commenter_options.get(k, True) + } + statement = _add_sql_comment( + args_list[0], **commenter_data + ) + + args_list[0] = statement + args = tuple(args_list) + + except Exception as exc: # pylint: disable=broad-except + _logger.exception( + "Exception while generating sql comment: %s", exc + ) + + self._populate_span(span, cursor, *args) + return query_method(*args, **kwargs) diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py index e29a8ad380..2ffa2f3d5b 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py @@ -14,6 +14,7 @@ import logging +import re from unittest import mock from opentelemetry import context @@ -306,6 +307,44 @@ def __getattr__(self, name): r"Select 1 /\*dbapi_level='1.0',dbapi_threadsafety='unknown',driver_paramstyle='unknown',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", ) + def test_executemany_comment_matches_db_statement_attribute(self): + connect_module = mock.MagicMock() + connect_module.__version__ = mock.MagicMock() + connect_module.__libpq_version__ = 123 + connect_module.apilevel = 123 + connect_module.threadsafety = 123 + connect_module.paramstyle = "test" + + db_integration = dbapi.DatabaseApiIntegration( + "testname", + "postgresql", + enable_commenter=True, + commenter_options={"db_driver": False, "dbapi_level": False}, + connect_module=connect_module, + ) + mock_connection = db_integration.wrapped_connection( + mock_connect, {}, {} + ) + cursor = mock_connection.cursor() + cursor.executemany("Select 1;") + self.assertRegex( + cursor.query, + r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + spans_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans_list), 1) + span = spans_list[0] + self.assertRegex( + span.attributes[SpanAttributes.DB_STATEMENT], + r"Select 1 /\*dbapi_threadsafety=123,driver_paramstyle='test',libpq_version=123,traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/", + ) + + cursor_span_id = re.search(r"[a-zA-Z0-9_]{16}", cursor.query).group() + db_statement_span_id = re.search( + r"[a-zA-Z0-9_]{16}", span.attributes[SpanAttributes.DB_STATEMENT] + ).group() + self.assertEqual(cursor_span_id, db_statement_span_id) + def test_compatible_build_version_psycopg_psycopg2_libpq(self): connect_module = mock.MagicMock() connect_module.__name__ = "test" @@ -553,6 +592,43 @@ def test_instrument_connection(self): connection2 = dbapi.instrument_connection(self.tracer, connection, "-") self.assertIs(connection2.__wrapped__, connection) + @mock.patch("opentelemetry.instrumentation.dbapi.DatabaseApiIntegration") + def test_instrument_connection_kwargs_defaults(self, mock_dbapiint): + dbapi.instrument_connection(self.tracer, mock.Mock(), "foo") + kwargs = mock_dbapiint.call_args[1] + self.assertEqual(kwargs["connection_attributes"], None) + self.assertEqual(kwargs["version"], "") + self.assertEqual(kwargs["tracer_provider"], None) + self.assertEqual(kwargs["capture_parameters"], False) + self.assertEqual(kwargs["enable_commenter"], False) + self.assertEqual(kwargs["commenter_options"], None) + self.assertEqual(kwargs["connect_module"], None) + + @mock.patch("opentelemetry.instrumentation.dbapi.DatabaseApiIntegration") + def test_instrument_connection_kwargs_provided(self, mock_dbapiint): + mock_tracer_provider = mock.MagicMock() + mock_connect_module = mock.MagicMock() + dbapi.instrument_connection( + self.tracer, + mock.Mock(), + "foo", + connection_attributes={"foo": "bar"}, + version="test", + tracer_provider=mock_tracer_provider, + capture_parameters=True, + enable_commenter=True, + commenter_options={"foo": "bar"}, + connect_module=mock_connect_module, + ) + kwargs = mock_dbapiint.call_args[1] + self.assertEqual(kwargs["connection_attributes"], {"foo": "bar"}) + self.assertEqual(kwargs["version"], "test") + self.assertIs(kwargs["tracer_provider"], mock_tracer_provider) + self.assertEqual(kwargs["capture_parameters"], True) + self.assertEqual(kwargs["enable_commenter"], True) + self.assertEqual(kwargs["commenter_options"], {"foo": "bar"}) + self.assertIs(kwargs["connect_module"], mock_connect_module) + def test_uninstrument_connection(self): connection = mock.Mock() # Set connection.database to avoid a failure because mock can't diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py index b7e24d87c9..8a707c8c2a 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py @@ -114,7 +114,7 @@ def test_instrumentor(self, request_mock): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.elasticsearch ) @@ -608,7 +608,7 @@ def test_bulk(self, request_mock): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.elasticsearch ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py index 7ae1649149..4cd19da4af 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py @@ -114,7 +114,7 @@ async def test_unary_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -142,7 +142,7 @@ async def test_unary_stream(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -170,7 +170,7 @@ async def test_stream_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -200,7 +200,7 @@ async def test_stream_stream(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_server_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_server_interceptor.py index 050f6f8d13..ee917ca26c 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_server_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_server_interceptor.py @@ -105,7 +105,7 @@ async def request(channel): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -164,7 +164,7 @@ async def request(channel): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -217,7 +217,7 @@ async def request(channel): self.assertIs(parent_span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( parent_span, opentelemetry.instrumentation.grpc ) @@ -263,7 +263,7 @@ async def request(channel): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -318,7 +318,7 @@ async def request(channel): self.assertIs(parent_span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( parent_span, opentelemetry.instrumentation.grpc ) @@ -514,7 +514,7 @@ async def request(channel): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -579,7 +579,7 @@ async def request(channel): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py index 38759352b3..9fb922a615 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py @@ -111,7 +111,7 @@ def test_unary_unary_future(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -125,7 +125,7 @@ def test_unary_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -151,7 +151,7 @@ def test_unary_stream(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -177,7 +177,7 @@ def test_stream_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -205,7 +205,7 @@ def test_stream_stream(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py index b6ae975dff..81e8d708f2 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py @@ -116,7 +116,7 @@ def test_unary_unary_future(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -130,7 +130,7 @@ def test_unary_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -260,7 +260,7 @@ def test_unary_unary_future(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -274,7 +274,7 @@ def test_unary_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -443,7 +443,7 @@ def test_unary_unary_future(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -457,7 +457,7 @@ def test_unary_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -483,7 +483,7 @@ def test_unary_stream(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -509,7 +509,7 @@ def test_stream_unary(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -537,7 +537,7 @@ def test_stream_stream(self): self.assertIs(span.kind, trace.SpanKind.CLIENT) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor.py index de79269894..08aa16187a 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor.py @@ -120,7 +120,7 @@ def handler(request, context): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -188,7 +188,7 @@ def test_create_span(self): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -252,7 +252,7 @@ def SimpleMethod(self, request, context): self.assertIs(parent_span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( parent_span, opentelemetry.instrumentation.grpc ) @@ -307,7 +307,7 @@ def test_create_span_streaming(self): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -371,7 +371,7 @@ def ServerStreamingMethod(self, request, context): self.assertIs(parent_span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( parent_span, opentelemetry.instrumentation.grpc ) @@ -594,7 +594,7 @@ def unset_status_handler(request, context): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -626,7 +626,7 @@ def unset_status_handler(request, context): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor_filter.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor_filter.py index 95e70236cb..6a0081c4af 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor_filter.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor_filter.py @@ -109,7 +109,7 @@ def handler(request, context): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) @@ -195,7 +195,7 @@ def test_create_span(self): self.assertIs(span.kind, trace.SpanKind.SERVER) # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.grpc ) diff --git a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py index d3a2cecfe6..195c784408 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py @@ -641,77 +641,6 @@ async def aclose(self) -> None: await self._transport.aclose() -class _InstrumentedClient(httpx.Client): - _tracer_provider = None - _request_hook = None - _response_hook = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._original_transport = self._transport - self._original_mounts = self._mounts.copy() - self._is_instrumented_by_opentelemetry = True - - self._transport = SyncOpenTelemetryTransport( - self._transport, - tracer_provider=_InstrumentedClient._tracer_provider, - request_hook=_InstrumentedClient._request_hook, - response_hook=_InstrumentedClient._response_hook, - ) - self._mounts.update( - { - url_pattern: ( - SyncOpenTelemetryTransport( - transport, - tracer_provider=_InstrumentedClient._tracer_provider, - request_hook=_InstrumentedClient._request_hook, - response_hook=_InstrumentedClient._response_hook, - ) - if transport is not None - else transport - ) - for url_pattern, transport in self._original_mounts.items() - } - ) - - -class _InstrumentedAsyncClient(httpx.AsyncClient): - _tracer_provider = None - _request_hook = None - _response_hook = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._original_transport = self._transport - self._original_mounts = self._mounts.copy() - self._is_instrumented_by_opentelemetry = True - - self._transport = AsyncOpenTelemetryTransport( - self._transport, - tracer_provider=_InstrumentedAsyncClient._tracer_provider, - request_hook=_InstrumentedAsyncClient._request_hook, - response_hook=_InstrumentedAsyncClient._response_hook, - ) - - self._mounts.update( - { - url_pattern: ( - AsyncOpenTelemetryTransport( - transport, - tracer_provider=_InstrumentedAsyncClient._tracer_provider, - request_hook=_InstrumentedAsyncClient._request_hook, - response_hook=_InstrumentedAsyncClient._response_hook, - ) - if transport is not None - else transport - ) - for url_pattern, transport in self._original_mounts.items() - } - ) - - class HTTPXClientInstrumentor(BaseInstrumentor): # pylint: disable=protected-access,attribute-defined-outside-init """An instrumentor for httpx Client and AsyncClient @@ -938,8 +867,9 @@ async def _handle_async_request_wrapper( # pylint: disable=too-many-locals return response + @classmethod def instrument_client( - self, + cls, client: typing.Union[httpx.Client, httpx.AsyncClient], tracer_provider: TracerProvider = None, request_hook: typing.Union[ @@ -996,7 +926,7 @@ def instrument_client( client._transport, "handle_request", partial( - self._handle_request_wrapper, + cls._handle_request_wrapper, tracer=tracer, sem_conv_opt_in_mode=sem_conv_opt_in_mode, request_hook=request_hook, @@ -1004,24 +934,25 @@ def instrument_client( ), ) for transport in client._mounts.values(): - wrap_function_wrapper( - transport, - "handle_request", - partial( - self._handle_request_wrapper, - tracer=tracer, - sem_conv_opt_in_mode=sem_conv_opt_in_mode, - request_hook=request_hook, - response_hook=response_hook, - ), - ) + if hasattr(transport, "handle_request"): + wrap_function_wrapper( + transport, + "handle_request", + partial( + cls._handle_request_wrapper, + tracer=tracer, + sem_conv_opt_in_mode=sem_conv_opt_in_mode, + request_hook=request_hook, + response_hook=response_hook, + ), + ) client._is_instrumented_by_opentelemetry = True if hasattr(client._transport, "handle_async_request"): wrap_function_wrapper( client._transport, "handle_async_request", partial( - self._handle_async_request_wrapper, + cls._handle_async_request_wrapper, tracer=tracer, sem_conv_opt_in_mode=sem_conv_opt_in_mode, async_request_hook=async_request_hook, @@ -1029,17 +960,18 @@ def instrument_client( ), ) for transport in client._mounts.values(): - wrap_function_wrapper( - transport, - "handle_async_request", - partial( - self._handle_async_request_wrapper, - tracer=tracer, - sem_conv_opt_in_mode=sem_conv_opt_in_mode, - async_request_hook=async_request_hook, - async_response_hook=async_response_hook, - ), - ) + if hasattr(transport, "handle_async_request"): + wrap_function_wrapper( + transport, + "handle_async_request", + partial( + cls._handle_async_request_wrapper, + tracer=tracer, + sem_conv_opt_in_mode=sem_conv_opt_in_mode, + async_request_hook=async_request_hook, + async_response_hook=async_response_hook, + ), + ) client._is_instrumented_by_opentelemetry = True @staticmethod diff --git a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py index 07699700c4..148fe27893 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py @@ -216,7 +216,7 @@ def test_basic(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.httpx ) @@ -240,7 +240,7 @@ def test_nonstandard_http_method(self): self.assertIs(span.status.status_code, trace.StatusCode.ERROR) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.httpx ) @@ -269,7 +269,7 @@ def test_nonstandard_http_method_new_semconv(self): self.assertIs(span.status.status_code, trace.StatusCode.ERROR) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.httpx ) @@ -309,7 +309,7 @@ def test_basic_new_semconv(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.httpx ) @@ -350,7 +350,7 @@ def test_basic_both_semconv(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.httpx ) @@ -741,6 +741,10 @@ def create_client( def create_proxy_transport(self, url: str): pass + @abc.abstractmethod + def get_transport_handler(self, transport): + pass + def setUp(self): super().setUp() self.client = self.create_client() @@ -763,17 +767,15 @@ def assert_proxy_mounts(self, mounts, num_mounts, transport_type=None): self.assertEqual(len(mounts), num_mounts) for transport in mounts: with self.subTest(transport): + if transport is None: + continue if transport_type: self.assertIsInstance( transport, transport_type, ) else: - handler = getattr(transport, "handle_request", None) - if not handler: - handler = getattr( - transport, "handle_async_request" - ) + handler = self.get_transport_handler(transport) self.assertTrue( isinstance(handler, ObjectProxy) and getattr(handler, "__wrapped__") @@ -910,13 +912,20 @@ def test_suppress_instrumentation_new_client(self): self.assert_span(num_spans=0) - def test_instrument_client(self): + def test_instrument_client_called_on_the_instance(self): client = self.create_client() HTTPXClientInstrumentor().instrument_client(client) result = self.perform_request(self.URL, client=client) self.assertEqual(result.text, "Hello!") self.assert_span(num_spans=1) + def test_instrument_client_called_on_the_class(self): + client = self.create_client() + HTTPXClientInstrumentor.instrument_client(client) + result = self.perform_request(self.URL, client=client) + self.assertEqual(result.text, "Hello!") + self.assert_span(num_spans=1) + def test_instrumentation_without_client(self): HTTPXClientInstrumentor().instrument() results = [ @@ -976,6 +985,21 @@ def test_uninstrument_new_client(self): self.assertEqual(result.text, "Hello!") self.assert_span() + @mock.patch.dict( + "os.environ", {"NO_PROXY": "http://mock/status/200"}, clear=True + ) + def test_instrument_with_no_proxy(self): + proxy_mounts = self.create_proxy_mounts() + HTTPXClientInstrumentor().instrument() + client = self.create_client(mounts=proxy_mounts) + result = self.perform_request(self.URL, client=client) + self.assert_span(num_spans=1) + self.assertEqual(result.text, "Hello!") + self.assert_proxy_mounts( + client._mounts.values(), + 3, + ) + def test_instrument_proxy(self): proxy_mounts = self.create_proxy_mounts() HTTPXClientInstrumentor().instrument() @@ -987,6 +1011,27 @@ def test_instrument_proxy(self): 2, ) + @mock.patch.dict( + "os.environ", {"NO_PROXY": "http://mock/status/200"}, clear=True + ) + def test_instrument_client_with_no_proxy(self): + proxy_mounts = self.create_proxy_mounts() + client = self.create_client(mounts=proxy_mounts) + self.assert_proxy_mounts( + client._mounts.values(), + 3, + (httpx.HTTPTransport, httpx.AsyncHTTPTransport), + ) + HTTPXClientInstrumentor.instrument_client(client) + result = self.perform_request(self.URL, client=client) + self.assertEqual(result.text, "Hello!") + self.assert_span(num_spans=1) + self.assert_proxy_mounts( + client._mounts.values(), + 3, + ) + HTTPXClientInstrumentor.uninstrument_client(client) + def test_instrument_client_with_proxy(self): proxy_mounts = self.create_proxy_mounts() client = self.create_client(mounts=proxy_mounts) @@ -1181,6 +1226,9 @@ def perform_request( def create_proxy_transport(self, url): return httpx.HTTPTransport(proxy=httpx.Proxy(url)) + def get_transport_handler(self, transport): + return getattr(transport, "handle_request", None) + def test_can_instrument_subclassed_client(self): class CustomClient(httpx.Client): pass @@ -1234,6 +1282,9 @@ async def _perform_request(): def create_proxy_transport(self, url): return httpx.AsyncHTTPTransport(proxy=httpx.Proxy(url)) + def get_transport_handler(self, transport): + return getattr(transport, "handle_async_request", None) + def test_basic_multiple(self): # We need to create separate clients because in httpx >= 0.19, # closing the client after "with" means the second http call fails diff --git a/instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py b/instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py index 3614febffd..79399cce7f 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py +++ b/instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py @@ -50,7 +50,7 @@ def test_instrumentor(self, mock_connect): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.mysql ) diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py index 85083cff2e..5b08b0b50d 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py @@ -36,6 +36,72 @@ cursor.close() cnx.close() +SQLCOMMENTER +***************************************** +You can optionally configure MySQLClient instrumentation to enable sqlcommenter which enriches +the query with contextual information. + +.. code:: python + + import MySQLdb + from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor + + + MySQLClientInstrumentor().instrument(enable_commenter=True, commenter_options={}) + + cnx = MySQLdb.connect(database="MySQL_Database") + cursor = cnx.cursor() + cursor.execute("INSERT INTO test (testField) VALUES (123)" + cnx.commit() + cursor.close() + cnx.close() + +For example, +:: + + Invoking cursor.execute("INSERT INTO test (testField) VALUES (123)") will lead to sql query "INSERT INTO test (testField) VALUES (123)" but when SQLCommenter is enabled + the query will get appended with some configurable tags like "INSERT INTO test (testField) VALUES (123) /*tag=value*/;" + +SQLCommenter Configurations +*************************** +We can configure the tags to be appended to the sqlquery log by adding configuration inside commenter_options(default:{}) keyword + +db_driver = True(Default) or False + +For example, +:: +Enabling this flag will add MySQLdb and its version, e.g. /*MySQLdb%%3A1.2.3*/ + +dbapi_threadsafety = True(Default) or False + +For example, +:: +Enabling this flag will add threadsafety /*dbapi_threadsafety=2*/ + +dbapi_level = True(Default) or False + +For example, +:: +Enabling this flag will add dbapi_level /*dbapi_level='2.0'*/ + +mysql_client_version = True(Default) or False + +For example, +:: +Enabling this flag will add mysql_client_version /*mysql_client_version='123'*/ + +driver_paramstyle = True(Default) or False + +For example, +:: +Enabling this flag will add driver_paramstyle /*driver_paramstyle='pyformat'*/ + +opentelemetry_values = True(Default) or False + +For example, +:: +Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/ + API --- """ @@ -59,14 +125,16 @@ class MySQLClientInstrumentor(BaseInstrumentor): - def instrumentation_dependencies(self) -> Collection[str]: + def instrumentation_dependencies(self) -> Collection[str]: # pylint: disable=no-self-use return _instruments - def _instrument(self, **kwargs): + def _instrument(self, **kwargs): # pylint: disable=no-self-use """Integrate with the mysqlclient library. https://github.com/PyMySQL/mysqlclient/ """ tracer_provider = kwargs.get("tracer_provider") + enable_sqlcommenter = kwargs.get("enable_commenter", False) + commenter_options = kwargs.get("commenter_options", {}) dbapi.wrap_connect( __name__, @@ -76,14 +144,21 @@ def _instrument(self, **kwargs): _CONNECTION_ATTRIBUTES, version=__version__, tracer_provider=tracer_provider, + enable_commenter=enable_sqlcommenter, + commenter_options=commenter_options, ) - def _uninstrument(self, **kwargs): + def _uninstrument(self, **kwargs): # pylint: disable=no-self-use """ "Disable mysqlclient instrumentation""" dbapi.unwrap_connect(MySQLdb, "connect") @staticmethod - def instrument_connection(connection, tracer_provider=None): + def instrument_connection( + connection, + tracer_provider=None, + enable_commenter=None, + commenter_options=None, + ): """Enable instrumentation in a mysqlclient connection. Args: @@ -102,6 +177,9 @@ def instrument_connection(connection, tracer_provider=None): _CONNECTION_ATTRIBUTES, version=__version__, tracer_provider=tracer_provider, + enable_commenter=enable_commenter, + commenter_options=commenter_options, + connect_module=MySQLdb, ) @staticmethod diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py b/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py index 35fdecc8e1..ae221f68f4 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/tests/test_mysqlclient_integration.py @@ -23,6 +23,7 @@ class TestMySQLClientIntegration(TestBase): + # pylint: disable=invalid-name def tearDown(self): super().tearDown() with self.disable_logging(): @@ -43,7 +44,7 @@ def test_instrumentor(self, mock_connect): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.mysqlclient ) @@ -96,6 +97,256 @@ def test_instrument_connection(self, mock_connect): spans_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans_list), 1) + @mock.patch("opentelemetry.instrumentation.dbapi.instrument_connection") + @mock.patch("MySQLdb.connect") + # pylint: disable=unused-argument + def test_instrument_connection_enable_commenter_dbapi_kwargs( + self, + mock_connect, + mock_instrument_connection, + ): + cnx = MySQLdb.connect(database="test") + cnx = MySQLClientInstrumentor().instrument_connection( + cnx, + enable_commenter=True, + commenter_options={"foo": True}, + ) + cursor = cnx.cursor() + cursor.execute("Select 1;") + kwargs = mock_instrument_connection.call_args[1] + self.assertEqual(kwargs["enable_commenter"], True) + self.assertEqual(kwargs["commenter_options"], {"foo": True}) + + def test_instrument_connection_with_dbapi_sqlcomment_enabled(self): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + cnx_proxy = MySQLClientInstrumentor().instrument_connection( + mock_connection, + enable_commenter=True, + ) + cnx_proxy.cursor().execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + cnx_proxy = MySQLClientInstrumentor().instrument_connection( + mock_connection, + enable_commenter=True, + commenter_options={ + "dbapi_level": False, + "dbapi_threadsafety": True, + "driver_paramstyle": False, + }, + ) + cnx_proxy.cursor().execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + cnx_proxy = MySQLClientInstrumentor().instrument_connection( + mock_connection, + ) + cnx_proxy.cursor().execute("Select 1;") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + "Select 1;", + ) + + @mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect") + @mock.patch("MySQLdb.connect") + # pylint: disable=unused-argument + def test_instrument_enable_commenter_dbapi_kwargs( + self, + mock_connect, + mock_wrap_connect, + ): + MySQLClientInstrumentor()._instrument( + enable_commenter=True, + commenter_options={"foo": True}, + ) + kwargs = mock_wrap_connect.call_args[1] + self.assertEqual(kwargs["enable_commenter"], True) + self.assertEqual(kwargs["commenter_options"], {"foo": True}) + + def test_instrument_with_dbapi_sqlcomment_enabled( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + MySQLClientInstrumentor()._instrument( + enable_commenter=True, + ) + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_with_dbapi_sqlcomment_enabled_with_options( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + MySQLClientInstrumentor()._instrument( + enable_commenter=True, + commenter_options={ + "dbapi_level": False, + "dbapi_threadsafety": True, + "driver_paramstyle": False, + }, + ) + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='MySQLdb%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_with_dbapi_sqlcomment_not_enabled_default( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="MySQLdb", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module._mysql.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.mysqlclient.MySQLdb", + mock_connect_module, + ), mock.patch( + "opentelemetry.instrumentation.dbapi.util_version", + return_value="foobar", + ): + MySQLClientInstrumentor()._instrument() + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + "Select 1;", + ) + @mock.patch("MySQLdb.connect") # pylint: disable=unused-argument def test_uninstrument_connection(self, mock_connect): diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py b/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py index dc9969ba8c..4ddaad9174 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py @@ -182,7 +182,7 @@ def test_instrumentor(self): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.psycopg ) @@ -213,7 +213,7 @@ def test_instrumentor_with_connection_class(self): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.psycopg ) @@ -407,7 +407,7 @@ async def test_async_connection(): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.psycopg ) @@ -435,7 +435,7 @@ async def test_async_connection(): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.psycopg ) diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py index 6671073043..9a6a5ff2fa 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py @@ -100,7 +100,7 @@ def test_instrumentor(self): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.psycopg2 ) diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py index c48a5b6b3d..eb4435813d 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py @@ -26,7 +26,6 @@ import pymysql from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor - PyMySQLInstrumentor().instrument() cnx = pymysql.connect(database="MySQL_Database") @@ -36,6 +35,76 @@ cursor.close() cnx.close() +SQLCOMMENTER +***************************************** +You can optionally configure PyMySQL instrumentation to enable sqlcommenter which enriches +the query with contextual information. + +Usage +----- + +.. code:: python + + import pymysql + from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor + + PyMySQLInstrumentor().instrument(enable_commenter=True, commenter_options={}) + + cnx = pymysql.connect(database="MySQL_Database") + cursor = cnx.cursor() + cursor.execute("INSERT INTO test (testField) VALUES (123)" + cnx.commit() + cursor.close() + cnx.close() + + +For example, +:: + + Invoking cursor.execute("INSERT INTO test (testField) VALUES (123)") will lead to sql query "INSERT INTO test (testField) VALUES (123)" but when SQLCommenter is enabled + the query will get appended with some configurable tags like "INSERT INTO test (testField) VALUES (123) /*tag=value*/;" + + +SQLCommenter Configurations +*************************** +We can configure the tags to be appended to the sqlquery log by adding configuration inside commenter_options(default:{}) keyword + +db_driver = True(Default) or False + +For example, +:: +Enabling this flag will add pymysql and its version, e.g. /*pymysql%%3A1.2.3*/ + +dbapi_threadsafety = True(Default) or False + +For example, +:: +Enabling this flag will add threadsafety /*dbapi_threadsafety=2*/ + +dbapi_level = True(Default) or False + +For example, +:: +Enabling this flag will add dbapi_level /*dbapi_level='2.0'*/ + +mysql_client_version = True(Default) or False + +For example, +:: +Enabling this flag will add mysql_client_version /*mysql_client_version='123'*/ + +driver_paramstyle = True(Default) or False + +For example, +:: +Enabling this flag will add driver_paramstyle /*driver_paramstyle='pyformat'*/ + +opentelemetry_values = True(Default) or False + +For example, +:: +Enabling this flag will add traceparent values /*traceparent='00-03afa25236b8cd948fa853d67038ac79-405ff022e8247c46-01'*/ + API --- """ @@ -59,14 +128,16 @@ class PyMySQLInstrumentor(BaseInstrumentor): - def instrumentation_dependencies(self) -> Collection[str]: + def instrumentation_dependencies(self) -> Collection[str]: # pylint: disable=no-self-use return _instruments - def _instrument(self, **kwargs): + def _instrument(self, **kwargs): # pylint: disable=no-self-use """Integrate with the PyMySQL library. https://github.com/PyMySQL/PyMySQL/ """ tracer_provider = kwargs.get("tracer_provider") + enable_sqlcommenter = kwargs.get("enable_commenter", False) + commenter_options = kwargs.get("commenter_options", {}) dbapi.wrap_connect( __name__, @@ -76,14 +147,21 @@ def _instrument(self, **kwargs): _CONNECTION_ATTRIBUTES, version=__version__, tracer_provider=tracer_provider, + enable_commenter=enable_sqlcommenter, + commenter_options=commenter_options, ) - def _uninstrument(self, **kwargs): + def _uninstrument(self, **kwargs): # pylint: disable=no-self-use """ "Disable PyMySQL instrumentation""" dbapi.unwrap_connect(pymysql, "connect") @staticmethod - def instrument_connection(connection, tracer_provider=None): + def instrument_connection( + connection, + tracer_provider=None, + enable_commenter=None, + commenter_options=None, + ): """Enable instrumentation in a PyMySQL connection. Args: @@ -102,6 +180,9 @@ def instrument_connection(connection, tracer_provider=None): _CONNECTION_ATTRIBUTES, version=__version__, tracer_provider=tracer_provider, + enable_commenter=enable_commenter, + commenter_options=commenter_options, + connect_module=pymysql, ) @staticmethod diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py b/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py index 6f8af5d8df..82294236f0 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py +++ b/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py @@ -24,6 +24,7 @@ class TestPyMysqlIntegration(TestBase): + # pylint: disable=invalid-name def tearDown(self): super().tearDown() with self.disable_logging(): @@ -44,7 +45,7 @@ def test_instrumentor(self, mock_connect): span = spans_list[0] # Check version and name in span's instrumentation info - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.pymysql ) @@ -111,6 +112,244 @@ def test_instrument_connection(self, mock_connect): spans_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans_list), 1) + @mock.patch("opentelemetry.instrumentation.dbapi.instrument_connection") + @mock.patch("pymysql.connect") + # pylint: disable=unused-argument + def test_instrument_connection_enable_commenter_dbapi_kwargs( + self, + mock_connect, + mock_instrument_connection, + ): + cnx = pymysql.connect(database="test") + cnx = PyMySQLInstrumentor().instrument_connection( + cnx, + enable_commenter=True, + commenter_options={"foo": True}, + ) + cursor = cnx.cursor() + cursor.execute("SELECT * FROM test") + kwargs = mock_instrument_connection.call_args[1] + self.assertEqual(kwargs["enable_commenter"], True) + self.assertEqual(kwargs["commenter_options"], {"foo": True}) + + def test_instrument_connection_with_dbapi_sqlcomment_enabled(self): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + cnx_proxy = PyMySQLInstrumentor().instrument_connection( + mock_connection, + enable_commenter=True, + ) + cnx_proxy.cursor().execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_connection_with_dbapi_sqlcomment_enabled_with_options( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + cnx_proxy = PyMySQLInstrumentor().instrument_connection( + mock_connection, + enable_commenter=True, + commenter_options={ + "dbapi_level": False, + "dbapi_threadsafety": True, + "driver_paramstyle": False, + }, + ) + cnx_proxy.cursor().execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_connection_with_dbapi_sqlcomment_not_enabled_default( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + cnx_proxy = PyMySQLInstrumentor().instrument_connection( + mock_connection, + ) + cnx_proxy.cursor().execute("Select 1;") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + "Select 1;", + ) + + @mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect") + @mock.patch("pymysql.connect") + # pylint: disable=unused-argument + def test_instrument_enable_commenter_dbapi_kwargs( + self, + mock_connect, + mock_wrap_connect, + ): + PyMySQLInstrumentor()._instrument( + enable_commenter=True, + commenter_options={"foo": True}, + ) + kwargs = mock_wrap_connect.call_args[1] + self.assertEqual(kwargs["enable_commenter"], True) + self.assertEqual(kwargs["commenter_options"], {"foo": True}) + + def test_instrument_with_dbapi_sqlcomment_enabled( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + PyMySQLInstrumentor()._instrument( + enable_commenter=True, + ) + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_with_dbapi_sqlcomment_enabled_with_options( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + PyMySQLInstrumentor()._instrument( + enable_commenter=True, + commenter_options={ + "dbapi_level": False, + "dbapi_threadsafety": True, + "driver_paramstyle": False, + }, + ) + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + + spans_list = self.memory_exporter.get_finished_spans() + span = spans_list[0] + span_id = format(span.get_span_context().span_id, "016x") + trace_id = format(span.get_span_context().trace_id, "032x") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + f"Select 1 /*db_driver='pymysql%%3Afoobar',dbapi_threadsafety='123',mysql_client_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/;", + ) + + def test_instrument_with_dbapi_sqlcomment_not_enabled_default( + self, + ): + mock_connect_module = mock.MagicMock( + __name__="pymysql", + __version__="foobar", + threadsafety="123", + apilevel="123", + paramstyle="test", + ) + mock_connect_module.get_client_info.return_value = "foobaz" + mock_cursor = mock_connect_module.connect().cursor() + mock_connection = mock.MagicMock() + mock_connection.cursor.return_value = mock_cursor + + with mock.patch( + "opentelemetry.instrumentation.pymysql.pymysql", + mock_connect_module, + ): + PyMySQLInstrumentor()._instrument() + cnx = mock_connect_module.connect(database="test") + cursor = cnx.cursor() + cursor.execute("Select 1;") + self.assertEqual( + mock_cursor.execute.call_args[0][0], + "Select 1;", + ) + @mock.patch("pymysql.connect") # pylint: disable=unused-argument def test_uninstrument_connection(self, mock_connect): diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index a5cb8927ae..366cd0c233 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -166,7 +166,7 @@ def test_basic(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.requests ) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py index 172c1193f3..b64af796d1 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py @@ -219,28 +219,31 @@ def _before_cur_exec( ) with trace.use_span(span, end_on_exit=False): if span.is_recording(): + if self.enable_commenter: + commenter_data = { + "db_driver": conn.engine.driver, + # Driver/framework centric information. + "db_framework": f"sqlalchemy:{sqlalchemy.__version__}", + } + + if self.commenter_options.get( + "opentelemetry_values", True + ): + commenter_data.update(**_get_opentelemetry_values()) + + # Filter down to just the requested attributes. + commenter_data = { + k: v + for k, v in commenter_data.items() + if self.commenter_options.get(k, True) + } + + statement = _add_sql_comment(statement, **commenter_data) + span.set_attribute(SpanAttributes.DB_STATEMENT, statement) span.set_attribute(SpanAttributes.DB_SYSTEM, self.vendor) for key, value in attrs.items(): span.set_attribute(key, value) - if self.enable_commenter: - commenter_data = { - "db_driver": conn.engine.driver, - # Driver/framework centric information. - "db_framework": f"sqlalchemy:{sqlalchemy.__version__}", - } - - if self.commenter_options.get("opentelemetry_values", True): - commenter_data.update(**_get_opentelemetry_values()) - - # Filter down to just the requested attributes. - commenter_data = { - k: v - for k, v in commenter_data.items() - if self.commenter_options.get(k, True) - } - - statement = _add_sql_comment(statement, **commenter_data) context._otel_span = span diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py index ec2fc51e5b..8490721e3e 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +import re import pytest from sqlalchemy import ( @@ -21,6 +22,7 @@ from opentelemetry import context from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor +from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.test.test_base import TestBase @@ -59,6 +61,38 @@ def test_sqlcommenter_enabled(self): r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", ) + def test_sqlcommenter_enabled_matches_db_statement_attribute(self): + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + enable_commenter=True, + commenter_options={"db_framework": False}, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1;")).fetchall() + query_log = self.caplog.records[-2].getMessage() + self.assertRegex( + query_log, + r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 2) + # first span is connection to db + self.assertEqual(spans[0].name, "connect") + # second span is query itself + query_span = spans[1] + self.assertRegex( + query_span.attributes[SpanAttributes.DB_STATEMENT], + r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + cnx_span_id = re.search(r"[a-zA-Z0-9_]{16}", query_log).group() + db_statement_span_id = re.search( + r"[a-zA-Z0-9_]{16}", + query_span.attributes[SpanAttributes.DB_STATEMENT], + ).group() + self.assertEqual(cnx_span_id, db_statement_span_id) + def test_sqlcommenter_enabled_otel_values_false(self): engine = create_engine("sqlite:///:memory:") SQLAlchemyInstrumentor().instrument( diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py index 4e1ee2a5df..aff86ea77b 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py @@ -40,6 +40,7 @@ "process.runtime.thread_count": None, "process.runtime.cpu.utilization": None, "process.runtime.context_switches": ["involuntary", "voluntary"], + "process.open_file_descriptor.count": None, } Usage @@ -595,7 +596,7 @@ def _get_system_network_packets( """Observer callback for network packets""" for device, counters in psutil.net_io_counters(pernic=True).items(): - for metric in self._config["system.network.dropped.packets"]: + for metric in self._config["system.network.packets"]: recv_sent = {"receive": "recv", "transmit": "sent"}[metric] if hasattr(counters, f"packets_{recv_sent}"): self._system_network_packets_labels["device"] = device @@ -626,7 +627,7 @@ def _get_system_network_io( """Observer callback for network IO""" for device, counters in psutil.net_io_counters(pernic=True).items(): - for metric in self._config["system.network.dropped.packets"]: + for metric in self._config["system.network.io"]: recv_sent = {"receive": "recv", "transmit": "sent"}[metric] if hasattr(counters, f"bytes_{recv_sent}"): self._system_network_io_labels["device"] = device diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py index 83abcff4c0..92c30a66f0 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py @@ -20,6 +20,7 @@ from unittest import mock, skipIf from opentelemetry.instrumentation.system_metrics import ( + _DEFAULT_CONFIG, SystemMetricsInstrumentor, ) from opentelemetry.sdk.metrics import MeterProvider @@ -865,3 +866,14 @@ def test_open_file_descriptor_count(self, mock_process_num_fds): expected, ) mock_process_num_fds.assert_called() + + +class TestConfigSystemMetrics(TestBase): + # pylint:disable=no-self-use + def test_that_correct_config_is_read(self): + for key, value in _DEFAULT_CONFIG.items(): + meter_provider = MeterProvider([InMemoryMetricReader()]) + instrumentor = SystemMetricsInstrumentor(config={key: value}) + instrumentor.instrument(meter_provider=meter_provider) + meter_provider.force_flush() + instrumentor.uninstrument() diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py index 8ac0284939..b085cc50d5 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py @@ -158,7 +158,7 @@ def test_basic(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.urllib ) @@ -182,7 +182,7 @@ def test_basic_new_semconv(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.urllib ) @@ -209,7 +209,7 @@ def test_basic_both_semconv(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.urllib ) @@ -344,7 +344,7 @@ def test_response_code_none(self): self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqualSpanInstrumentationInfo( + self.assertEqualSpanInstrumentationScope( span, opentelemetry.instrumentation.urllib ) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index 2e7b5532f3..6b7eae6b00 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -16,6 +16,10 @@ # RUN `python scripts/generate_instrumentation_bootstrap.py` TO REGENERATE. libraries = [ + { + "library": "openai >= 1.26.0", + "instrumentation": "opentelemetry-instrumentation-openai-v2==2.1b0.dev", + }, { "library": "aio_pika >= 7.2.0, < 10.0.0", "instrumentation": "opentelemetry-instrumentation-aio-pika==0.50b0.dev", diff --git a/scripts/eachdist.py b/scripts/eachdist.py index b82d16a8ec..ca490546eb 100755 --- a/scripts/eachdist.py +++ b/scripts/eachdist.py @@ -237,6 +237,16 @@ def setup_instparser(instparser): "releaseargs", nargs=argparse.REMAINDER, help=extraargs_help("pytest") ) + patchreleaseparser = subparsers.add_parser( + "update_patch_versions", + help="Updates version numbers during patch release, used by maintainers and CI", + ) + patchreleaseparser.set_defaults(func=patch_release_args) + patchreleaseparser.add_argument("--stable_version", required=True) + patchreleaseparser.add_argument("--unstable_version", required=True) + patchreleaseparser.add_argument("--stable_version_prev", required=True) + patchreleaseparser.add_argument("--unstable_version_prev", required=True) + fmtparser = subparsers.add_parser( "format", help="Formats all source code with black and isort.", @@ -655,6 +665,27 @@ def update_dependencies(targets, version, packages): ) +def update_patch_dependencies(targets, version, prev_version, packages): + print("updating patch dependencies") + if "all" in packages: + packages.extend(targets) + + # PEP 508 allowed specifier operators + operators = ["==", "!=", "<=", ">=", "<", ">", "===", "~=", "="] + operators_pattern = "|".join(re.escape(op) for op in operators) + + for pkg in packages: + search = rf"({basename(pkg)}[^,]*?)(\s?({operators_pattern})\s?)(.*{prev_version})" + replace = r"\g<1>\g<2>" + version + print(f"{search=}\t{replace=}\t{pkg=}") + update_files( + targets, + "pyproject.toml", + search, + replace, + ) + + def update_files(targets, filename, search, replace): errors = False for target in targets: @@ -687,10 +718,12 @@ def release_args(args): versions = args.versions updated_versions = [] + # remove excluded packages excluded = cfg["exclude_release"]["packages"].split() targets = [ target for target in targets if basename(target) not in excluded ] + for group in versions.split(","): mcfg = cfg[group] version = mcfg["version"] @@ -707,6 +740,40 @@ def release_args(args): update_changelogs("-".join(updated_versions)) +def patch_release_args(args): + print("preparing patch release") + + rootpath = find_projectroot() + targets = list(find_targets_unordered(rootpath)) + cfg = ConfigParser() + cfg.read(str(find_projectroot() / "eachdist.ini")) + + # remove excluded packages + excluded = cfg["exclude_release"]["packages"].split() + targets = [ + target for target in targets if basename(target) not in excluded + ] + + # stable + mcfg = cfg["stable"] + packages = mcfg["packages"].split() + print(f"update stable packages to {args.stable_version}") + + update_patch_dependencies( + targets, args.stable_version, args.stable_version_prev, packages + ) + update_version_files(targets, args.stable_version, packages) + + # prerelease + mcfg = cfg["prerelease"] + packages = mcfg["packages"].split() + print(f"update prerelease packages to {args.unstable_version}") + update_patch_dependencies( + targets, args.unstable_version, args.unstable_version_prev, packages + ) + update_version_files(targets, args.unstable_version, packages) + + def test_args(args): clean_remainder_args(args.pytestargs) execute_args( diff --git a/scripts/otel_packaging.py b/scripts/otel_packaging.py index 2f42e44189..3b09dd87eb 100644 --- a/scripts/otel_packaging.py +++ b/scripts/otel_packaging.py @@ -21,14 +21,23 @@ scripts_path = os.path.dirname(os.path.abspath(__file__)) root_path = os.path.dirname(scripts_path) instrumentations_path = os.path.join(root_path, "instrumentation") +genai_instrumentations_path = os.path.join(root_path, "instrumentation-genai") def get_instrumentation_packages(): - for pkg in sorted(os.listdir(instrumentations_path)): + pkg_paths = [] + for pkg in os.listdir(instrumentations_path): pkg_path = os.path.join(instrumentations_path, pkg) if not os.path.isdir(pkg_path): continue + pkg_paths.append(pkg_path) + for pkg in os.listdir(genai_instrumentations_path): + pkg_path = os.path.join(genai_instrumentations_path, pkg) + if not os.path.isdir(pkg_path): + continue + pkg_paths.append(pkg_path) + for pkg_path in sorted(pkg_paths): try: version = subprocess.check_output( "hatch version",