diff --git a/.gitignore b/.gitignore index 30e4d2c1a1..fc4d1bbe4a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ megalinter-reports/ # Benchmarks .asv/ +# LLM Cache Files +.tiktoken_cache + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/newrelic/common/llm_utils.py b/newrelic/common/llm_utils.py index b336aeeaf5..7b8137ba22 100644 --- a/newrelic/common/llm_utils.py +++ b/newrelic/common/llm_utils.py @@ -120,12 +120,19 @@ def __init__(self, wrapped, on_stop_iteration, on_error, on_stream_chunk=None): self._nr_on_stream_chunk = on_stream_chunk or noop # Track if we've sent the LLM events yet to avoid sending them multiple times self._nr_closed = False + # Lazily established by __aiter__ or __anext__. With LangChain's + # astream_events, __anext__ may be called before __aiter__. + self._nr_wrapped_iter = None def __aiter__(self): self._nr_wrapped_iter = self.__wrapped__.__aiter__() return self async def __anext__(self): + # Lazily establish the wrapped iterator. With astream_events, + # __anext__ may be called before __aiter__. + if self._nr_wrapped_iter is None: + self._nr_wrapped_iter = self.__wrapped__.__aiter__() try: return_val = await self._nr_wrapped_iter.__anext__() self._nr_on_stream_chunk(self, return_val) diff --git a/newrelic/hooks/mlmodel_langchain.py b/newrelic/hooks/mlmodel_langchain.py index c924fba55f..f76be9ee31 100644 --- a/newrelic/hooks/mlmodel_langchain.py +++ b/newrelic/hooks/mlmodel_langchain.py @@ -267,6 +267,33 @@ def astream(self, *args, **kwargs): return return_val + def astream_events(self, *args, **kwargs): + transaction = current_transaction() + if not transaction: + return self.__wrapped__.astream_events(*args, **kwargs) + + agent_name = getattr(self.__wrapped__, "name", "agent") + agent_id = str(uuid.uuid4()) + agent_event_dict = _construct_base_agent_event_dict(agent_name, agent_id, transaction) + function_trace_name = f"astream_events/{agent_name}" + agentic_subcomponent_data = {"type": "APM-AI_AGENT", "name": agent_name} + + ft = FunctionTrace(name=function_trace_name, group="Llm/agent/LangChain") + ft.__enter__() + ft._add_agent_attribute("subcomponent", json.dumps(agentic_subcomponent_data)) + try: + return_val = self.__wrapped__.astream_events(*args, **kwargs) + return_val = AsyncLLMStreamProxy( + return_val, + on_stop_iteration=self._nr_on_stop_iteration(ft, agent_event_dict), + on_error=self._nr_on_error(ft, agent_event_dict, agent_id), + ) + except Exception: + self._nr_on_error(ft, agent_event_dict, agent_id)(transaction) + raise + + return return_val + def transform(self, *args, **kwargs): transaction = current_transaction() if not transaction: diff --git a/tests/mlmodel_langchain/_mock_external_openai_server.py b/tests/mlmodel_langchain/_mock_external_openai_server.py deleted file mode 100644 index fba87da511..0000000000 --- a/tests/mlmodel_langchain/_mock_external_openai_server.py +++ /dev/null @@ -1,1004 +0,0 @@ -# Copyright 2010 New Relic, Inc. -# -# 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. - -import json - -import pytest -from testing_support.mock_external_http_server import MockExternalHTTPServer - -from newrelic.common.package_version_utils import get_package_version_tuple - -# This defines an external server test apps can make requests to instead of -# the real OpenAI backend. This provides 3 features: -# -# 1) This removes dependencies on external websites. -# 2) Provides a better mechanism for making an external call in a test app than -# simple calling another endpoint the test app makes available because this -# server will not be instrumented meaning we don't have to sort through -# transactions to separate the ones created in the test app and the ones -# created by an external call. -# 3) This app runs on a separate thread meaning it won't block the test app. -STREAMED_RESPONSES_V1 = { - "system: You are a world class algorithm for extracting information in structured formats. | user: Use the given format to extract information from the following input: Hello, world | user: Tip: Make sure to answer in the correct format": [ - { - "content-type": "text/event-stream; charset=utf-8", - "openai-organization": "nr-test-org", - "openai-processing-ms": "440", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999942", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_1addfc2e713648af834cb9992fd417d7", - }, - 200, - [ - { - "id": "chatcmpl-CvUIm4qNNuiHpumRpuX0HISeNKViC", - "object": "chat.completion.chunk", - "created": 1767817212, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [ - { - "index": 0, - "delta": {"role": "assistant", "content": "", "refusal": None}, - "logprobs": None, - "finish_reason": None, - } - ], - "usage": None, - "obfuscation": "HeQWMY8H", - }, - { - "id": "chatcmpl-CvUIm4qNNuiHpumRpuX0HISeNKViC", - "object": "chat.completion.chunk", - "created": 1767817212, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {"content": "Hello,"}, "logprobs": None, "finish_reason": None}], - "usage": None, - "obfuscation": "DferQO2zD", - }, - { - "id": "chatcmpl-CvUIm4qNNuiHpumRpuX0HISeNKViC", - "object": "chat.completion.chunk", - "created": 1767817212, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {"content": " world!"}, "logprobs": None, "finish_reason": None}], - "usage": None, - "obfuscation": "LlLJvKqz", - }, - { - "id": "chatcmpl-CvUIm4qNNuiHpumRpuX0HISeNKViC", - "object": "chat.completion.chunk", - "created": 1767817212, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {}, "logprobs": None, "finish_reason": "stop"}], - "usage": None, - "obfuscation": "Qzvy", - }, - { - "id": "chatcmpl-CvUIm4qNNuiHpumRpuX0HISeNKViC", - "object": "chat.completion.chunk", - "created": 1767817212, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [], - "usage": { - "prompt_tokens": 96, - "completion_tokens": 24, - "total_tokens": 120, - "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, - }, - }, - "obfuscation": "NzeDrNhe", - }, - ], - ], - 'user: Use a tool to add an exclamation to the word "Hello"': [ - { - "content-type": "text/event-stream; charset=utf-8", - "openai-organization": "nr-test-org", - "openai-processing-ms": "134", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999985", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_4566af5dd7224f00a2407fa1d3e32864", - }, - 200, - [ - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1778607301, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [ - { - "index": 0, - "delta": {"role": "assistant", "content": "", "refusal": None}, - "logprobs": None, - "finish_reason": None, - } - ], - "usage": None, - "obfuscation": "VHZBuhMN", - }, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1778607301, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {"content": "Hello"}, "logprobs": None, "finish_reason": None}], - "usage": None, - "obfuscation": "kjWyA", - }, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1778607301, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {"content": "!"}, "logprobs": None, "finish_reason": None}], - "usage": None, - "obfuscation": "259VYkZdM", - }, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1778607301, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {}, "logprobs": None, "finish_reason": "stop"}], - "usage": None, - "obfuscation": "7s5h", - }, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1778607301, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [], - "usage": { - "prompt_tokens": 21, - "completion_tokens": 2, - "total_tokens": 23, - "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, - }, - }, - "obfuscation": "29QTnO6xx2", - }, - ], - ], - "user: What is the capital of France? Answer in one word.": [ - { - "content-type": "text/event-stream; charset=utf-8", - "openai-organization": "nr-test-org", - "openai-processing-ms": "245", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999985", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_22204b237d22427fbfd99c665d8a9964", - }, - 200, - [ - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1781626980, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [ - { - "index": 0, - "delta": {"role": "assistant", "content": "", "refusal": None}, - "logprobs": None, - "finish_reason": None, - } - ], - "usage": None, - "obfuscation": "dxxvf59Q", - }, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1781626980, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {"content": "Paris"}, "logprobs": None, "finish_reason": None}], - "usage": None, - "obfuscation": "MKT8v", - }, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1781626980, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [{"index": 0, "delta": {}, "logprobs": None, "finish_reason": "stop"}], - "usage": None, - "obfuscation": "Z61m", - }, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "object": "chat.completion.chunk", - "created": 1781626980, - "model": "gpt-3.5-turbo-0125", - "service_tier": "default", - "system_fingerprint": None, - "choices": [], - "usage": { - "prompt_tokens": 19, - "completion_tokens": 2, - "total_tokens": 21, - "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, - }, - }, - "obfuscation": "Plc7EI1Jr6", - }, - ], - ], -} -RESPONSES_V1 = { - 'system: You are a text manipulation algorithm. | user: Use a tool to add an exclamation to the word "Hello"': [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "324", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999974", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_619548c272db4f1ab380b83de9fdedef", - }, - 200, - { - "id": "chatcmpl-CukvsGfSQihNO9I3FTqaNKERWtUca", - "object": "chat.completion", - "created": 1767642812, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": None, - "tool_calls": [ - { - "id": "call_ymnsNurMgr3atFVr7BnJ2XYK", - "type": "function", - "function": {"name": "add_exclamation", "arguments": '{"message":"Hello"}'}, - } - ], - "refusal": None, - "annotations": [], - }, - "logprobs": None, - "finish_reason": "tool_calls", - } - ], - "usage": { - "prompt_tokens": 70, - "completion_tokens": 15, - "total_tokens": 85, - "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": None, - }, - ], - 'system: You are a text manipulation algorithm. | user: Use a tool to add an exclamation to the word "Hello" | assistant: None | tool: Hello!': [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "751", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999970", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_619548c272db4f1ab380b83de9fdedef", - }, - 200, - { - "id": "chatcmpl-CukvtgYHPS8HRHqCQiQgQrs7a2Tx1", - "object": "chat.completion", - "created": 1767642813, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": 'The word "Hello" with an exclamation mark added is "Hello!"', - "refusal": None, - "annotations": [], - }, - "logprobs": None, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 96, - "completion_tokens": 16, - "total_tokens": 112, - "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": None, - }, - ], - 'system: You are a text manipulation algorithm. | user: Use a tool to add an exclamation to the word "exc"': [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "767", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999975", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_27d106351bab9878a3969f23108cd4c6", - }, - 200, - { - "id": "chatcmpl-CxGq2dnBYh5JR5o4OANlkHgBhuxfK", - "object": "chat.completion", - "created": 1768242114, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": None, - "tool_calls": [ - { - "id": "call_blmqxOaZvxUtgB0JVLXYnEu1", - "type": "function", - "function": {"name": "add_exclamation", "arguments": '{"message":"exc"}'}, - } - ], - "refusal": None, - "annotations": [], - }, - "logprobs": None, - "finish_reason": "tool_calls", - } - ], - "usage": { - "prompt_tokens": 70, - "completion_tokens": 15, - "total_tokens": 85, - "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": None, - }, - ], - "system: You are a helpful assistant who generates a random first name. A user will pass in a first letter, and you should generate a name that starts with that first letter. | user: M": [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "236", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999955", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_58e5f91c0c3d4c2c9b6ee9ad8c4e8961", - }, - 200, - { - "id": "chatcmpl-CxGtBIjrsLMSkCUPSLOlAiHFxLz7A", - "object": "chat.completion", - "created": 1768242309, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": {"role": "assistant", "content": "Milo", "refusal": None, "annotations": []}, - "logprobs": None, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 46, - "completion_tokens": 2, - "total_tokens": 48, - "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": None, - }, - ], - "system: You are a helpful assistant who generates comma separated lists.\n A user will pass in a category, and you should generate 5 objects in that category in a comma separated list.\n ONLY return a comma separated list, and nothing more. | user: colors": [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "289", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999935", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_fbc7bb2ab3e149c1845699cfea9403d4", - }, - 200, - { - "id": "chatcmpl-CxGyV8CzGN80ByzFb4wN1hwGktOKD", - "object": "chat.completion", - "created": 1768242639, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": "red, blue, green, yellow, orange", - "refusal": None, - "annotations": [], - }, - "logprobs": None, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 60, - "completion_tokens": 9, - "total_tokens": 69, - "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": None, - }, - ], - "system: You are a world class algorithm for extracting information in structured formats. | user: Use the given format to extract information from the following input: Sally is 13 | user: Tip: Make sure to answer in the correct format": [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "201", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999944", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_40a68eb08b684844b1e1f2253c85f00c", - }, - 200, - { - "id": "chatcmpl-CxGyZUlLnBXQkOnJyJNSlshVXdOwQ", - "object": "chat.completion", - "created": 1768242643, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": None, - "function_call": {"name": "output_formatter", "arguments": '{"name":"Sally","age":13}'}, - "refusal": None, - "annotations": [], - }, - "logprobs": None, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 159, - "completion_tokens": 10, - "total_tokens": 169, - "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": None, - }, - ], - "system: You are a world class algorithm for extracting information in structured formats with openai failures. | user: Use the given format to extract information from the following input: Sally is 13 | user: Tip: Make sure to answer in the correct format": [ - {"content-type": "application/json; charset=utf-8", "x-request-id": "e58911d54d574647d36237e4e53c0f1a"}, - 401, - { - "error": { - "message": "Incorrect API key provided: No-exist. You can find your API key at https://platform.openai.com/account/api-keys.", - "type": "invalid_request_error", - "param": None, - "code": "invalid_api_key", - } - }, - ], - "system: You are a generator of quiz questions for a seminar. Use the following pieces of retrieved context to generate 5 multiple choice questions (A,B,C,D) on the subject matter. Use a three sentence maximum and keep the answer concise. Render the output as HTML\n\nWhat is 2 + 4? | user: math": [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "2029", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999927", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_008a31c6023e42c9ae640eae2ae3b5ad", - }, - 200, - { - "id": "chatcmpl-CxfJTw2pCnRMvza9LZyE8qitryqFC", - "object": "chat.completion", - "created": 1768336195, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": "```html\n\n\n\n Math Quiz\n\n\n

Math Quiz Questions

\n
    \n
  1. What is the result of 5 + 3?
  2. \n \n
  3. What is the product of 6 x 7?
  4. \n \n
  5. What is the square root of 64?
  6. \n \n
  7. What is the result of 12 / 4?
  8. \n \n
  9. What is the sum of 15 + 9?
  10. \n \n
\n\n\n```", - "refusal": None, - "annotations": [], - }, - "logprobs": None, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 73, - "completion_tokens": 337, - "total_tokens": 410, - "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": None, - }, - ], - # Embedding Responses - "3923": [ - { - "content-type": "application/json", - "openai-model": "text-embedding-ada-002-v2", - "openai-organization": "nr-test-org", - "openai-processing-ms": "42", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "10000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "9999992", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_72a807dee044452d85ae14ec24d2497a", - }, - 200, - { - "object": "list", - "data": [ - { - "object": "embedding", - "index": 0, - "embedding": "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", - } - ], - "model": "text-embedding-ada-002-v2", - "usage": {"prompt_tokens": 8, "total_tokens": 8}, - }, - ], - "10590": [ - { - "content-type": "application/json", - "openai-model": "text-embedding-ada-002-v2", - "openai-organization": "nr-test-org", - "openai-processing-ms": "82", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "10000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "9999998", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_39f823ffb1ba4f0db5866f82f4a16be9", - }, - 200, - { - "object": "list", - "data": [ - { - "object": "embedding", - "index": 0, - "embedding": "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", - } - ], - "model": "text-embedding-ada-002-v2", - "usage": {"prompt_tokens": 1, "total_tokens": 1}, - }, - ], - "9906": [ - { - "content-type": "application/json", - "openai-model": "text-embedding-ada-002-v2", - "openai-organization": "nr-test-org", - "openai-processing-ms": "158", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "10000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "9999996", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_2f1a3eb66e7b4f55849cac5a35bcb9c9", - }, - 200, - { - "object": "list", - "data": [ - { - "object": "embedding", - "index": 0, - "embedding": "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", - } - ], - "model": "text-embedding-ada-002-v2", - "usage": {"prompt_tokens": 4, "total_tokens": 4}, - }, - ], - "12833": [ - { - "content-type": "application/json", - "openai-model": "text-embedding-ada-002-v2", - "openai-organization": "nr-test-org", - "openai-processing-ms": "116", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "10000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "9999995", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_92ab81c1ce20420591176c5507d7e04e", - }, - 200, - { - "object": "list", - "data": [ - { - "object": "embedding", - "index": 0, - "embedding": "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", - } - ], - "model": "text-embedding-ada-002-v2", - "usage": {"prompt_tokens": 5, "total_tokens": 5}, - }, - ], - 'user: Use a tool to add an exclamation to the word "Hello"': [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "238", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999985", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_22204b237d22427fbfd99c665d8a9964", - }, - 200, - { - "id": "chatcmpl-Dd0Na8gXEDyFIhYMsL72TYk3bSZun", - "object": "chat.completion", - "created": 1778188622, - "model": "gpt-3.5-turbo-0125", - "choices": [ - { - "index": 0, - "message": {"role": "assistant", "content": "Hello!", "refusal": None, "annotations": []}, - "logprobs": None, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 21, - "completion_tokens": 2, - "total_tokens": 23, - "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": None, - }, - ], - "user: What is the capital of France? Answer in one word.": [ - { - "content-type": "application/json", - "openai-organization": "nr-test-org", - "openai-processing-ms": "238", - "openai-project": "proj_0Wv6taeZjWf793P67JMswYY3", - "openai-version": "2020-10-01", - "x-ratelimit-limit-requests": "10000", - "x-ratelimit-limit-tokens": "50000000", - "x-ratelimit-remaining-requests": "9999", - "x-ratelimit-remaining-tokens": "49999985", - "x-ratelimit-reset-requests": "6ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_22204b237d22427fbfd99c665d8a9964", - }, - 200, - { - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i", - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "logprobs": None, - "message": { - "content": "Paris", - "refusal": None, - "role": "assistant", - "annotations": [], - "audio": None, - "function_call": None, - "tool_calls": None, - }, - "content_filter_results": { - "hate": {"filtered": False, "severity": "safe"}, - "self_harm": {"filtered": False, "severity": "safe"}, - "sexual": {"filtered": False, "severity": "safe"}, - "violence": {"filtered": False, "severity": "safe"}, - }, - } - ], - "created": 1780077445, - "model": "gpt-3.5-turbo-0125", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_a7294185dc", - "usage": { - "completion_tokens": 2, - "prompt_tokens": 19, - "total_tokens": 21, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0, - }, - "prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 0}, - "latency_checkpoint": { - "engine_tbt_ms": 11, - "engine_ttft_ms": 56, - "engine_ttlt_ms": 78, - "pre_inference_ms": 108, - "service_tbt_ms": 16, - "service_ttft_ms": 196, - "service_ttlt_ms": 219, - "total_duration_ms": 120, - "user_visible_ttft_ms": 88, - }, - }, - "prompt_filter_results": [ - { - "prompt_index": 0, - "content_filter_results": { - "hate": {"filtered": False, "severity": "safe"}, - "self_harm": {"filtered": False, "severity": "safe"}, - "sexual": {"filtered": False, "severity": "safe"}, - "violence": {"filtered": False, "severity": "safe"}, - }, - } - ], - }, - ], -} - - -@pytest.fixture(scope="session") -def simple_get(): - def _simple_get(self): - content_len = int(self.headers.get("content-length")) - content = json.loads(self.rfile.read(content_len).decode("utf-8")) - stream = content.get("stream", False) - prompt = extract_shortened_prompt(content) - if not prompt: - self.send_response(500) - self.end_headers() - self.wfile.write(b"Could not parse prompt.") - return - - headers, response = ({}, "") - - mocked_responses = RESPONSES_V1 - if stream: - mocked_responses = STREAMED_RESPONSES_V1 - - for k, v in mocked_responses.items(): - if prompt == k: - headers, status_code, response = v - break - else: # If no matches found - self.send_response(500) - self.end_headers() - self.wfile.write(f"Unknown Prompt ({'Streaming' if stream else 'Non-Streaming'}):\n{prompt}".encode()) - return - - # Send response code - self.send_response(status_code) - - # Send headers - for k, v in headers.items(): - self.send_header(k, v) - self.end_headers() - - # Send response body - if stream and status_code < 400: - for resp in response: - data = json.dumps(resp).encode("utf-8") - if prompt == "Stream parsing error.": - # Force a parsing error by writing an invalid streamed response. - self.wfile.write(b"data: %s" % data) - else: - self.wfile.write(b"data: %s\n\n" % data) - else: - self.wfile.write(json.dumps(response).encode("utf-8")) - return - - return _simple_get - - -@pytest.fixture(scope="session") -def MockExternalOpenAIServer(simple_get): - class _MockExternalOpenAIServer(MockExternalHTTPServer): - # To use this class in a test one needs to start and stop this server - # before and after making requests to the test app that makes the external - # calls. - - def __init__(self, handler=simple_get, port=None, *args, **kwargs): - super().__init__(handler=handler, port=port, *args, **kwargs) # noqa: B026 - - return _MockExternalOpenAIServer - - -def extract_shortened_prompt(content): - _input = content.get("input", None) - if _input: - return str(_input[0][0]) - - # Transform all input messages into a single prompt - messages = content.get("messages") - prompt = [f"{message['role']}: {message['content']}" for message in messages] - return " | ".join(prompt) - - -if __name__ == "__main__": - _MockExternalOpenAIServer = MockExternalOpenAIServer() - with MockExternalOpenAIServer() as server: - print(f"MockExternalOpenAIServer serving on port {server.port!s}") - while True: - pass # Serve forever diff --git a/tests/mlmodel_langchain/_test_tools.py b/tests/mlmodel_langchain/_test_tools.py index a187a68767..8592d27f97 100644 --- a/tests/mlmodel_langchain/_test_tools.py +++ b/tests/mlmodel_langchain/_test_tools.py @@ -32,22 +32,22 @@ async def add_exclamation_async(message: str) -> str: return f"{message}!" -@pytest.fixture(scope="session", params=["sync_tool", "async_tool"]) +@pytest.fixture(params=["sync_tool", "async_tool"]) def tool_type(request): return request.param -@pytest.fixture(scope="session") +@pytest.fixture def tool_method_name(tool_type): return "run" if tool_type == "sync_tool" else "arun" -@pytest.fixture(scope="session") +@pytest.fixture def add_exclamation(tool_type, exercise_agent): if tool_type == "sync_tool": return add_exclamation_sync elif tool_type == "async_tool": - if exercise_agent._called_method in {"invoke", "stream"}: + if exercise_agent._called_method in ("invoke", "stream"): pytest.skip("Async tools cannot be invoked synchronously.") return add_exclamation_async else: diff --git a/tests/mlmodel_langchain/cassette.yaml b/tests/mlmodel_langchain/cassette.yaml new file mode 100644 index 0000000000..ce4ef9ab0a --- /dev/null +++ b/tests/mlmodel_langchain/cassette.yaml @@ -0,0 +1,1293 @@ +# Copyright 2010 New Relic, Inc. +# +# 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. + +interactions: +- request: + body: '{"messages":[{"content":"You are a text manipulation algorithm.","role":"system"},{"content":"Use + a tool to add an exclamation to the word \"Hello\". Answer in one word with + no formatting.","role":"user"}],"model":"gpt-3.5-turbo","stream":false,"tools":[{"type":"function","function":{"name":"add_exclamation","description":"Adds + an exclamation mark to the input message.","parameters":{"properties":{"message":{"type":"string"}},"required":["message"],"type":"object"}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-DtzuyY4hQfbRHuaHFg2vTu2zZmdmF", "object": "chat.completion", + "created": 1782238424, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": + "call_v1KSBmXVXiL9PuaNS6HvdZ8s", "type": "function", "function": {"name": + "add_exclamation", "arguments": "{\"message\":\"Hello\"}"}}], "refusal": null, + "annotations": []}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": + {"prompt_tokens": 78, "completion_tokens": 15, "total_tokens": 93, "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": null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a10577ea599cfefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:13:46 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '1445' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_c78b73e8e4c84348bd4effe1cf6eff49 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a text manipulation algorithm.","role":"system"},{"content":"Use + a tool to add an exclamation to the word \"Hello\". Answer in one word with + no formatting.","role":"user"},{"content":null,"name":"my_agent","role":"assistant","tool_calls":[{"type":"function","id":"call_v1KSBmXVXiL9PuaNS6HvdZ8s","function":{"name":"add_exclamation","arguments":"{\"message\": + \"Hello\"}"}}]},{"content":"Hello!","role":"tool","tool_call_id":"call_v1KSBmXVXiL9PuaNS6HvdZ8s"}],"model":"gpt-3.5-turbo","stream":false,"tools":[{"type":"function","function":{"name":"add_exclamation","description":"Adds + an exclamation mark to the input message.","parameters":{"properties":{"message":{"type":"string"}},"required":["message"],"type":"object"}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-Dtzv0TaMu8o6TWPyiHh8hNfYbDfyf", "object": "chat.completion", + "created": 1782238426, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": "Hello!", "refusal": null, + "annotations": []}, "logprobs": null, "finish_reason": "stop"}], "usage": + {"prompt_tokens": 107, "completion_tokens": 3, "total_tokens": 110, "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": null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a10577f3e9e1fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:13:48 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '779' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_1e4fc67953f54ce59fc1362273978fb1 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a text manipulation algorithm.","role":"system"},{"content":"Use + a tool to add an exclamation to the word \"Hello\". Answer in one word with + no formatting.","role":"user"}],"model":"gpt-3.5-turbo","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"add_exclamation","description":"Adds + an exclamation mark to the input message.","parameters":{"properties":{"message":{"type":"string"}},"required":["message"],"type":"object"}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_qSJl6PzmkqfRPVSuEPdBELbT","type":"function","function":{"name":"add_exclamation","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"5QhAeLgV4qO2WVn"} + + + data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"n2TOzf3Y5PgiP"} + + + data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"message"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Leczi8d7G"} + + + data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"5clfWHp5VGb"} + + + data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Hello"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"156T1DFg3Ox"} + + + data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"quY8mLaNnHh2F"} + + + data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"ecqCGxKqgo65aY"} + + + data: {"id":"chatcmpl-Dtzv3dUO9qZjpDSmKSQeV9P5mR1oV","object":"chat.completion.chunk","created":1782238429,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":78,"completion_tokens":15,"total_tokens":93,"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}},"obfuscation":"haKN4r0l8"} + + + data: [DONE] + + + ' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a10578046d37fefe-PDX + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 23 Jun 2026 18:13:49 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '580' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_6cbef5b39ac8479f89e33629144f03f5 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a text manipulation algorithm.","role":"system"},{"content":"Use + a tool to add an exclamation to the word \"Hello\". Answer in one word with + no formatting.","role":"user"},{"content":null,"name":"my_agent","role":"assistant","tool_calls":[{"type":"function","id":"call_qSJl6PzmkqfRPVSuEPdBELbT","function":{"name":"add_exclamation","arguments":"{\"message\": + \"Hello\"}"}}]},{"content":"Hello!","role":"tool","tool_call_id":"call_qSJl6PzmkqfRPVSuEPdBELbT"}],"model":"gpt-3.5-turbo","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"add_exclamation","description":"Adds + an exclamation mark to the input message.","parameters":{"properties":{"message":{"type":"string"}},"required":["message"],"type":"object"}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-Dtzv4oL7iOI8y72DZ6ll6CUwQQsT9","object":"chat.completion.chunk","created":1782238430,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"yMRs9Eoy"} + + + data: {"id":"chatcmpl-Dtzv4oL7iOI8y72DZ6ll6CUwQQsT9","object":"chat.completion.chunk","created":1782238430,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"PvpE1"} + + + data: {"id":"chatcmpl-Dtzv4oL7iOI8y72DZ6ll6CUwQQsT9","object":"chat.completion.chunk","created":1782238430,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"!"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"mpwmJo8VD"} + + + data: {"id":"chatcmpl-Dtzv4oL7iOI8y72DZ6ll6CUwQQsT9","object":"chat.completion.chunk","created":1782238430,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"7wIk"} + + + data: {"id":"chatcmpl-Dtzv4oL7iOI8y72DZ6ll6CUwQQsT9","object":"chat.completion.chunk","created":1782238430,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":107,"completion_tokens":3,"total_tokens":110,"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}},"obfuscation":"jtYuLazy"} + + + data: [DONE] + + + ' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105780a9d5ffefe-PDX + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 23 Jun 2026 18:13:51 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '1275' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_5ee97765fc384ea68ebdc738542bbba0 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a helpful assistant who generates a random + first name. A user will pass in a first letter, and you should generate a name + that starts with that first letter.","role":"system"},{"content":"M","role":"user"}],"model":"gpt-3.5-turbo","stream":false}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-DtzvAELO463xzY61iPrGrmHrUGhIW", "object": "chat.completion", + "created": 1782238436, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": "Matilda", "refusal": null, "annotations": + []}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": + 46, "completion_tokens": 2, "total_tokens": 48, "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": null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a1057830fa6ffefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:13:58 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '2524' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_e31f8fe594af4069b725889e7772404a + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a helpful assistant who generates comma + separated lists.\n A user will pass in a category, and you should generate + 5 objects in that category in a comma separated list.\n ONLY return a comma + separated list, and nothing more.","role":"system"},{"content":"colors","role":"user"}],"model":"gpt-3.5-turbo","stream":false}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-DtzviMJ9x5mk5FNLwrRlqroV4GXG7", "object": "chat.completion", + "created": 1782238470, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": "red, blue, green, yellow, + purple", "refusal": null, "annotations": []}, "logprobs": null, "finish_reason": + "stop"}], "usage": {"prompt_tokens": 60, "completion_tokens": 9, "total_tokens": + 69, "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": + null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105790a2ef5fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:31 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '983' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_a9a2797091ce4fa59b9287edb8bc468a + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a world class algorithm for extracting + information in structured formats.","role":"system"},{"content":"Use the given + format to extract information from the following input: Sally is 13","role":"user"},{"content":"Tip: + Make sure to answer in the correct format","role":"user"}],"model":"gpt-3.5-turbo","function_call":{"name":"output_formatter"},"functions":[{"name":"output_formatter","description":"Output + formatter. Should always be used to format your response to the user.","parameters":{"title":"Person","description":"Identifying + information about a person.","type":"object","properties":{"name":{"title":"Name","description":"The + person''s name","type":"string"},"age":{"title":"Age","description":"The person''s + age","type":"integer"},"fav_food":{"title":"Fav Food","description":"The person''s + favorite food","type":"string"}},"required":["name","age"]}}],"stream":false}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-DtzvjKBeXn8jRNIVWvEHU6mG8FveQ", "object": "chat.completion", + "created": 1782238471, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": null, "function_call": {"name": + "output_formatter", "arguments": "{\"name\":\"Sally\",\"age\":13}"}, "refusal": + null, "annotations": []}, "logprobs": null, "finish_reason": "stop"}], "usage": + {"prompt_tokens": 159, "completion_tokens": 10, "total_tokens": 169, "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": null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a1057910fd69fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:33 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '1393' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_31f7c4d9b8d44de8a9b04829c5349a18 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a world class algorithm for extracting + information in structured formats with openai failures.","role":"system"},{"content":"Use + the given format to extract information from the following input: Sally is 13","role":"user"},{"content":"Tip: + Make sure to answer in the correct format","role":"user"}],"model":"gpt-3.5-turbo","function_call":{"name":"output_formatter"},"functions":[{"name":"output_formatter","description":"Output + formatter. Should always be used to format your response to the user.","parameters":{"title":"Person","description":"Identifying + information about a person.","type":"object","properties":{"name":{"title":"Name","description":"The + person''s name","type":"string"},"age":{"title":"Age","description":"The person''s + age","type":"integer"},"fav_food":{"title":"Fav Food","description":"The person''s + favorite food","type":"string"}},"required":["name","age"]}}],"stream":false}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"error": {"message": "Incorrect API key provided: FAKE-OPE***-KEY. + You can find your API key at https://platform.openai.com/account/api-keys.", + "type": "invalid_request_error", "param": null, "code": "invalid_api_key"}}' + headers: + access-control-expose-headers: + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105791b4bbbfefe-PDX + connection: + - keep-alive + content-type: + - application/json; charset=utf-8 + date: + - Tue, 23 Jun 2026 18:14:33 GMT + server: + - cloudflare + vary: + - Origin + x-request-id: + - req_e6bfa34797e441acbaf273cd12ffc1f4 + status: + code: 401 + message: Unauthorized +- request: + body: '{"input":[[3923,374,220,17,489,220,19,30]],"model":"text-embedding-ada-002","encoding_format":"base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/embeddings + response: + body: + string: '{"object": "list", "data": [{"object": "embedding", "index": 0, "embedding": + "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"}], + "model": "text-embedding-ada-002-v2", "usage": {"prompt_tokens": 8, "total_tokens": + 8}}' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105791f9e21fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:35 GMT + openai-model: + - text-embedding-ada-002-v2 + openai-organization: + - nr-test-org + openai-processing-ms: + - '890' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + via: + - envoy-router-6f89878f98-2p2hk + x-engine-geography: + - JP + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_d3a4fb2835ef43519b18b9a8844fd3f7 + status: + code: 200 + message: OK +- request: + body: '{"input":[[10590]],"model":"text-embedding-ada-002","encoding_format":"base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/embeddings + response: + body: + string: '{"object": "list", "data": [{"object": "embedding", "index": 0, "embedding": + "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"}], + "model": "text-embedding-ada-002-v2", "usage": {"prompt_tokens": 1, "total_tokens": + 1}}' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a10579289b95fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:36 GMT + openai-model: + - text-embedding-ada-002-v2 + openai-organization: + - nr-test-org + openai-processing-ms: + - '275' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + via: + - envoy-router-7f6875bbd4-sf5g4 + x-engine-geography: + - US + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_156328328c824ba9bd55fc183b5467a6 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a generator of quiz questions for a seminar. + Use the following pieces of retrieved context to generate 5 multiple choice + questions (A,B,C,D) on the subject matter. Use a three sentence maximum and + keep the answer concise. Render the output as HTML\n\nWhat is 2 + 4?","role":"system"},{"content":"math","role":"user"}],"model":"gpt-3.5-turbo","stream":false}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-DtzvoS4hcv7Bg7LTG0mWqjaNXwy7l", "object": "chat.completion", + "created": 1782238476, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": "```html\n1. What is the result + of 5 x 3?\n A) 12\n B) 15\n C) 18\n D) 20\n\n2. What is the solution + to 10 / 2?\n A) 3\n B) 4\n C) 5\n D) 6\n\n3. If a rectangle has a + length of 8 and a width of 6, what is its area?\n A) 12\n B) 24\n C) + 36\n D) 48\n\n4. What is the value of 3 squared?\n A) 6\n B) 8\n C) + 9\n D) 12\n\n5. If a store sells a shirt for $20 and a customer buys 3 shirts, + how much do they owe?\n A) $40\n B) $50\n C) $60\n D) $70\n```\n", + "refusal": null, "annotations": []}, "logprobs": null, "finish_reason": "stop"}], + "usage": {"prompt_tokens": 73, "completion_tokens": 210, "total_tokens": 283, + "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": + null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105792e4950fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:38 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '2167' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_d3315cdac48d4bc8b3a4100a5e3a7648 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"What is the capital of France? Answer in one word.","role":"user"}],"model":"gpt-3.5-turbo","stream":false}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-DtzvqIvgdmZ3Xl4Op0KZDRrh8bbVO", "object": "chat.completion", + "created": 1782238478, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": "Paris", "refusal": null, "annotations": + []}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": + 19, "completion_tokens": 1, "total_tokens": 20, "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": null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105793ccc77fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:40 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '1354' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_3e6cb9ba936540e98e5b9b2183b4ffd9 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"What is the capital of France? Answer in one word.","role":"user"}],"model":"gpt-3.5-turbo","stream":true,"stream_options":{"include_usage":true}}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-DtzvsqwLwIxtxrIOiL36xI7kh3LQD","object":"chat.completion.chunk","created":1782238480,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AdFjfQDJ"} + + + data: {"id":"chatcmpl-DtzvsqwLwIxtxrIOiL36xI7kh3LQD","object":"chat.completion.chunk","created":1782238480,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Paris"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"UGyPC"} + + + data: {"id":"chatcmpl-DtzvsqwLwIxtxrIOiL36xI7kh3LQD","object":"chat.completion.chunk","created":1782238480,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"gQC9"} + + + data: {"id":"chatcmpl-DtzvsqwLwIxtxrIOiL36xI7kh3LQD","object":"chat.completion.chunk","created":1782238480,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":19,"completion_tokens":1,"total_tokens":20,"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}},"obfuscation":"UWlu5fxgM6"} + + + data: [DONE] + + + ' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a10579473ef2fefe-PDX + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 23 Jun 2026 18:14:40 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '358' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_4628d961ac97494cb42e4c2cae1533d3 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a text manipulation algorithm.","role":"system"},{"content":"Use + a tool to add an exclamation to the word \"exc\"","role":"user"}],"model":"gpt-3.5-turbo","stream":false,"tools":[{"type":"function","function":{"name":"add_exclamation","description":"Adds + an exclamation mark to the input message.","parameters":{"properties":{"message":{"type":"string"}},"required":["message"],"type":"object"}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"id": "chatcmpl-DtzvzpYJH5HLr3ByFDoBqhlP4eFQX", "object": "chat.completion", + "created": 1782238487, "model": "gpt-3.5-turbo-0125", "choices": [{"index": + 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": + "call_LFvPijUix171l9AO6wsTRf5B", "type": "function", "function": {"name": + "add_exclamation", "arguments": "{\"message\":\"exc\"}"}}], "refusal": null, + "annotations": []}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": + {"prompt_tokens": 70, "completion_tokens": 15, "total_tokens": 85, "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": null}' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a10579702a74fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:47 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '701' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_9a3bdcd22bbe467aa0ceff7b877582e8 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"content":"You are a text manipulation algorithm.","role":"system"},{"content":"Use + a tool to add an exclamation to the word \"exc\"","role":"user"}],"model":"gpt-3.5-turbo","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"add_exclamation","description":"Adds + an exclamation mark to the input message.","parameters":{"properties":{"message":{"type":"string"}},"required":["message"],"type":"object"}}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: 'data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_xV16FvF1uC0QYR1nILF4JRbd","type":"function","function":{"name":"add_exclamation","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"YF8VybGBJv9TvR1"} + + + data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"M8XIj2jNztbVY"} + + + data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"message"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"stTIEA52v"} + + + data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"q7MPAa1BEBf"} + + + data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"exc"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ZAxnAREwoi3iv"} + + + data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"NFI0Rrpw4mYAJ"} + + + data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"WNaO2vuvFMdNb5"} + + + data: {"id":"chatcmpl-Dtzw1DkeKVtsY3yqP66aMn25AQfaj","object":"chat.completion.chunk","created":1782238489,"model":"gpt-3.5-turbo-0125","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":70,"completion_tokens":15,"total_tokens":85,"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}},"obfuscation":"4GhsCOSAT"} + + + data: [DONE] + + + ' + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105797f9e85fefe-PDX + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 23 Jun 2026 18:14:49 GMT + openai-organization: + - nr-test-org + openai-processing-ms: + - '383' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_b4fa6bf848264db2965ca9db366869bf + status: + code: 200 + message: OK +- request: + body: '{"input":[[9906,1917,4999,16]],"model":"text-embedding-ada-002","encoding_format":"base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/embeddings + response: + body: + string: '{"object": "list", "data": [{"object": "embedding", "index": 0, "embedding": + "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"}], + "model": "text-embedding-ada-002-v2", "usage": {"prompt_tokens": 4, "total_tokens": + 4}}' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a10579999942fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:54 GMT + openai-model: + - text-embedding-ada-002-v2 + openai-organization: + - nr-test-org + openai-processing-ms: + - '240' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + via: + - envoy-router-57577fbcf8-lrq62 + x-engine-geography: + - US + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_c843530c15494f518a6931678ceeb902 + status: + code: 200 + message: OK +- request: + body: '{"input":[[12833,420,11914,25,22691]],"model":"text-embedding-ada-002","encoding_format":"base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - XXXXXX + connection: + - keep-alive + content-type: + - application/json + method: POST + uri: https://api.openai.com/v1/embeddings + response: + body: + string: '{"object": "list", "data": [{"object": "embedding", "index": 0, "embedding": + "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"}], + "model": "text-embedding-ada-002-v2", "usage": {"prompt_tokens": 5, "total_tokens": + 5}}' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + cf-cache-status: + - DYNAMIC + cf-ray: + - a105799bc8c5fefe-PDX + connection: + - keep-alive + content-type: + - application/json + date: + - Tue, 23 Jun 2026 18:14:54 GMT + openai-model: + - text-embedding-ada-002-v2 + openai-organization: + - nr-test-org + openai-processing-ms: + - '158' + openai-project: + - nr-test-project + openai-version: + - '2020-10-01' + server: + - cloudflare + transfer-encoding: + - chunked + via: + - envoy-router-7f6875bbd4-qwkjf + x-engine-geography: + - US + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '50000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '49999975' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_5ce7cda0ec3a4674b300079eefbea654 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/mlmodel_langchain/conftest.py b/tests/mlmodel_langchain/conftest.py index 20d2109cff..340c578729 100644 --- a/tests/mlmodel_langchain/conftest.py +++ b/tests/mlmodel_langchain/conftest.py @@ -13,26 +13,18 @@ # limitations under the License. import itertools -import json import os -from pathlib import Path import pytest +from langchain_core.messages.ai import AIMessage +from langchain_core.messages.tool import ToolMessage from langchain_openai import ChatOpenAI, OpenAIEmbeddings from testing_support.fixture.event_loop import event_loop as loop -from testing_support.fixtures import ( - collector_agent_registration_fixture, - collector_available_fixture, - override_application_settings, -) +from testing_support.fixture.vcr import * # noqa: F403 +from testing_support.fixture.vcr import VCR_IGNORED_HEADERS, VCR_REPLACE_HEADERS, VCR_TIKTOKEN_ENCODINGS +from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture from testing_support.ml_testing_utils import set_trace_info -from newrelic.api.transaction import current_transaction -from newrelic.common.object_wrapper import ObjectProxy, wrap_function_wrapper -from newrelic.common.signature import bind_args - -from ._mock_external_openai_server import MockExternalOpenAIServer, extract_shortened_prompt, simple_get - _default_settings = { "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs. "transaction_tracer.explain_threshold": 0.0, @@ -49,46 +41,74 @@ linked_applications=["Python Agent Test (mlmodel_langchain)"], ) +VCR_IGNORED_HEADERS.extend( + [ + "alt-svc", + "Cookie", + "host", + "set-cookie", + "Strict-Transport-Security", + "X-Content-Type-Options", + "x-envoy-upstream-service-time", + "x-openai-proxy-wasm", + "X-Stainless-Arch", + "X-Stainless-Async", + "X-Stainless-Lang", + "X-Stainless-OS", + "X-Stainless-Package-Version", + "X-Stainless-Raw-Response", + "x-stainless-retry-count", + "X-Stainless-Runtime-Version", + "X-Stainless-Runtime", + ] +) -OPENAI_AUDIT_LOG_FILE = Path(__file__).parent / "openai_audit.log" -OPENAI_AUDIT_LOG_CONTENTS = {} -# Intercept outgoing requests and log to file for mocking -RECORDED_HEADERS = {"x-request-id", "content-type"} -EXPECTED_AGENT_RESPONSE = 'The word "Hello" with an exclamation mark added is "Hello!"' +VCR_REPLACE_HEADERS.extend( + [ + ("openai-organization", "nr-test-org"), + ("openai-project", "nr-test-project"), + ("x-ratelimit-limit-requests", "10000"), + ("x-ratelimit-limit-tokens", "50000000"), + ("x-ratelimit-remaining-requests", "9999"), + ("x-ratelimit-remaining-tokens", "49999975"), + ("x-ratelimit-reset-requests", "6ms"), + ("x-ratelimit-reset-tokens", "0s"), + ] +) + +VCR_TIKTOKEN_ENCODINGS.extend(["cl100k_base"]) + +EXPECTED_AGENT_RESPONSE = "Hello!" EXPECTED_TOOL_OUTPUT = "Hello!" -@pytest.fixture(scope="session") -def openai_clients(MockExternalOpenAIServer): +@pytest.fixture +def openai_clients(vcr_recording): """ - This configures the openai client and returns it for openai v1 and only configures - openai for v0 since there is no client. + This configures the OpenAI client to use a ReplayApiClient which + will either record or replay responses depending on the mode. """ from newrelic.core.config import _environ_as_bool - if not _environ_as_bool("NEW_RELIC_TESTING_RECORD_OPENAI_RESPONSES", False): - with MockExternalOpenAIServer() as server: - chat = ChatOpenAI(base_url=f"http://localhost:{server.port}", api_key="NOT-A-REAL-SECRET", temperature=0.7) - embeddings = OpenAIEmbeddings( - openai_api_key="NOT-A-REAL-SECRET", openai_api_base=f"http://localhost:{server.port}" - ) - yield chat, embeddings - else: + if vcr_recording: openai_api_key = os.environ.get("OPENAI_API_KEY") if not openai_api_key: raise RuntimeError("OPENAI_API_KEY environment variable required.") - chat = ChatOpenAI(api_key=openai_api_key) - embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key) - yield chat, embeddings + else: + openai_api_key = os.environ["OPENAI_API_KEY"] = "NOT-A-REAL-SECRET" + chat = ChatOpenAI(api_key=openai_api_key) + embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key) + return chat, embeddings -@pytest.fixture(scope="session") + +@pytest.fixture def embedding_openai_client(openai_clients): _, embedding_client = openai_clients return embedding_client -@pytest.fixture(scope="session") +@pytest.fixture def chat_openai_client(openai_clients): chat_client, _ = openai_clients return chat_client @@ -101,17 +121,23 @@ def state_function_step(state): def append_function_step(state): from langchain.messages import ToolMessage - messages = state["messages"] if "messages" in state else state["model"]["messages"] + if "messages" in state: + messages = state["messages"] + elif "model" in state: + messages = state["model"]["messages"] + elif "data" in state: + messages = state["data"]["model"]["messages"] + messages.append(ToolMessage(f"The real agent said: {messages[-1].content}", tool_call_id=123)) return state -@pytest.fixture(scope="session", params=["create_agent", "StateGraph", "RunnableSeq", "RunnableSequence"]) +@pytest.fixture(params=["create_agent", "StateGraph", "RunnableSeq", "RunnableSequence"]) def agent_runnable_type(request): return request.param -@pytest.fixture(scope="session") +@pytest.fixture def create_agent_runnable(agent_runnable_type, chat_openai_client): """Create different runnable forms of the same agent and model as a fixture.""" @@ -162,7 +188,7 @@ def _create_runnable_sequence(*args, **kwargs): raise NotImplementedError -@pytest.fixture(scope="session") +@pytest.fixture def validate_agent_output(agent_runnable_type): def _unpack_messages(response): if isinstance(response, list) and not any(response): @@ -173,7 +199,20 @@ def _unpack_messages(response): # Messages are packaged into nested dicts with a "model" or "tool_call" key, a "message" key, # which contains a list with one or more messages in order. To unpack everything, # we need to unpack the dictionaries values and extract the messasges lists, then flatten them. - messages_packed = [next(iter(event.values()))["messages"] for event in response] + try: + messages_packed = [next(iter(event.values()))["messages"] for event in response] + except TypeError: + messages_packed = [] + for event in response: + for value in event.values(): + if isinstance(value, dict): + try: + dict_content = next(iter(value.values())) + if dict_content and ("messages" in dict_content): + messages_packed.append(dict_content["messages"]) + except StopIteration: + pass + return list(itertools.chain.from_iterable(messages_packed)) # invoke returns a Response object that contains the messages directly @@ -230,39 +269,137 @@ def _validate_agent_output(response): return _validate_agent_output -@pytest.fixture(scope="session", params=["invoke", "ainvoke", "stream", "astream"]) -def exercise_agent(request, loop, validate_agent_output, agent_runnable_type): +# This is used for `stream_events` and `astream_events` +@pytest.fixture +def validate_agent_event_output(agent_runnable_type): + def _unpack_messages(response): + if isinstance(response, list) and not any(response): + # Only None are returned from RunnableSeq.stream(), avoid the crash + return [] + elif isinstance(response, list): + # stream returns a list of events + # Messages are packaged into nested dicts with a "model" or "tool_call" key, a "message" key, + # which contains a list with one or more messages in order. To unpack everything, + # we need to unpack the dictionaries values and extract the messasges lists, then flatten them. + try: + messages_packed = [next(iter(event.values()))["messages"] for event in response] + except TypeError: + messages_packed = [] + for event in response: + for value in event.values(): + if isinstance(value, dict): + try: + dict_content = next(iter(value.values())) + if dict_content and ("messages" in dict_content): + messages_packed.append(dict_content["messages"]) + except StopIteration: + pass + + return list(itertools.chain.from_iterable(messages_packed)) + + # invoke returns a Response object that contains the messages directly + return response["messages"] + + def _validate_agent_event_output(response): + messages = _unpack_messages(response) + if agent_runnable_type == "create_agent": + for event in messages: + if isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "stop": + assert event.content == EXPECTED_AGENT_RESPONSE + elif isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "tool_calls": + assert event.tool_calls + elif isinstance(event, ToolMessage): + assert event.content == EXPECTED_TOOL_OUTPUT + + elif agent_runnable_type == "RunnableSeq": + # stream and astream do not directly output anything for RunnableSeq, and can't be validated. + for event in messages: + if isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "stop": + assert event.content == EXPECTED_AGENT_RESPONSE + elif isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "tool_calls": + assert event.tool_calls + elif isinstance(event, ToolMessage): + assert event.content == EXPECTED_TOOL_OUTPUT + + elif agent_runnable_type == "StateGraph": + for event in messages: + if isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "stop": + assert event.content == EXPECTED_AGENT_RESPONSE + elif isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "tool_calls": + assert event.tool_calls + elif isinstance(event, ToolMessage): + assert event.content == EXPECTED_TOOL_OUTPUT + + elif agent_runnable_type == "RunnableSequence": + for event in messages: + if isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "stop": + assert event.content == EXPECTED_AGENT_RESPONSE + elif isinstance(event, AIMessage) and event.response_metadata["finish_reason"] == "tool_calls": + assert event.tool_calls + elif isinstance(event, ToolMessage): + assert event.content == EXPECTED_TOOL_OUTPUT + + else: + raise NotImplementedError + + return _validate_agent_event_output + + +@pytest.fixture( + params=[ + {"run_method": "invoke", "version": "v1"}, + {"run_method": "invoke", "version": "v2"}, + {"run_method": "ainvoke", "version": "v1"}, + {"run_method": "ainvoke", "version": "v2"}, + {"run_method": "stream", "version": "v1"}, + {"run_method": "stream", "version": "v2"}, + {"run_method": "astream", "version": "v1"}, + {"run_method": "astream", "version": "v2"}, + {"run_method": "astream_events", "version": "v1"}, + {"run_method": "astream_events", "version": "v2"}, + ] +) +def exercise_agent(request, loop, validate_agent_output, validate_agent_event_output, agent_runnable_type): def _exercise_agent(agent, prompt): - if request.param == "invoke": - response = agent.invoke(prompt) + if method == "invoke": + response = agent.invoke(prompt, version=version) validate_agent_output(response) return response - elif request.param == "ainvoke": - response = loop.run_until_complete(agent.ainvoke(prompt)) + elif method == "ainvoke": + response = loop.run_until_complete(agent.ainvoke(prompt, version=version)) validate_agent_output(response) return response - elif request.param == "stream": - response = list(agent.stream(prompt)) + elif method == "stream": + response = list(agent.stream(prompt, version=version)) validate_agent_output(response) return response - elif request.param == "astream": + elif method == "astream": async def _exercise_agen(): - return [event async for event in agent.astream(prompt)] + return [event async for event in agent.astream(prompt, version=version)] response = loop.run_until_complete(_exercise_agen()) validate_agent_output(response) return response + elif method == "astream_events": + + async def _exercise_agen(): + return [event async for event in agent.astream_events(prompt, version=version)] + + response = loop.run_until_complete(_exercise_agen()) + validate_agent_event_output(response) + return response else: raise NotImplementedError - _exercise_agent._called_method = request.param # Used for metric names + method, version = request.param.get("run_method"), request.param.get("version") + _exercise_agent._called_method = method # Used for metric names # Expected number of events for a full run of the agent if agent_runnable_type != "RunnableSequence": _exercise_agent._expected_event_count = 11 _exercise_agent._expected_event_count_error = 5 - elif request.param in {"invoke", "ainvoke"}: + elif method in ("invoke", "ainvoke"): _exercise_agent._expected_event_count = 14 _exercise_agent._expected_event_count_error = 7 else: @@ -272,124 +409,14 @@ async def _exercise_agen(): return _exercise_agent -@pytest.fixture(scope="session") +@pytest.fixture def method_name(exercise_agent, agent_runnable_type): + method = exercise_agent._called_method + # Only AgentObjectProxy (used for create_agent) instruments `astream_events` directly. + # For StateGraph/RunnableSeq/RunnableSequence, `astream_events` delegates to `astream`, + # so the recorded metric is "astream". + if agent_runnable_type != "create_agent" and method == "astream_events": + return "astream" if agent_runnable_type == "StateGraph": - return "invoke" if exercise_agent._called_method in {"invoke", "stream"} else "ainvoke" - return exercise_agent._called_method - - -@pytest.fixture(autouse=True, scope="session") -def openai_server(wrap_httpx_client_send, wrap_stream_iter_events): - """ - This fixture will either create a mocked backend for testing purposes, or will - set up an audit log file to log responses of the real OpenAI backend to a file. - The behavior can be controlled by setting NEW_RELIC_TESTING_RECORD_OPENAI_RESPONSES=1 as - an environment variable to run using the real OpenAI backend. (Default: mocking) - """ - from newrelic.core.config import _environ_as_bool - - if _environ_as_bool("NEW_RELIC_TESTING_RECORD_OPENAI_RESPONSES", False): - wrap_function_wrapper("httpx._client", "Client.send", wrap_httpx_client_send) - wrap_function_wrapper("openai._streaming", "Stream._iter_events", wrap_stream_iter_events) - yield # Run tests - # Write responses to audit log - with OPENAI_AUDIT_LOG_FILE.open("w") as audit_log_fp: - json.dump(OPENAI_AUDIT_LOG_CONTENTS, fp=audit_log_fp, indent=4) - else: - # We are mocking openai responses so we don't need to do anything in this case. - yield - - -@pytest.fixture(scope="session") -def wrap_httpx_client_send(): - def _wrap_httpx_client_send(wrapped, instance, args, kwargs): - bound_args = bind_args(wrapped, args, kwargs) - stream = bound_args.get("stream", False) - request = bound_args["request"] - - if not request: - return wrapped(*args, **kwargs) - - params = json.loads(request.content.decode("utf-8")) - prompt = extract_shortened_prompt(params) - - # Send request - response = wrapped(*args, **kwargs) - - if response.status_code >= 400 or response.status_code < 200: - prompt = "error" - - rheaders = response.headers - - headers = dict( - filter( - lambda k: ( - k[0].lower() in RECORDED_HEADERS - or k[0].lower().startswith("openai") - or k[0].lower().startswith("x-ratelimit") - ), - rheaders.items(), - ) - ) - - # Append response data to log - if stream: - OPENAI_AUDIT_LOG_CONTENTS[prompt] = [headers, response.status_code, []] - if prompt == "error": - OPENAI_AUDIT_LOG_CONTENTS[prompt][2] = json.loads(response.read()) - else: - body = json.loads(response.content.decode("utf-8")) - OPENAI_AUDIT_LOG_CONTENTS[prompt] = headers, response.status_code, body - return response - - return _wrap_httpx_client_send - - -class LLMStreamAuditLogProxy(ObjectProxy): - def __init__(self, wrapped): - super().__init__(wrapped) - - def __iter__(self): - return self - - # Make this Proxy a pass through to our instrumentation's proxy by passing along - # get attr and set attr calls to our instrumentation's proxy. - def __getattr__(self, attr): - return self.__wrapped__.__getattr__(attr) - - def __setattr__(self, attr, value): - return self.__wrapped__.__setattr__(attr, value) - - def __next__(self): - transaction = current_transaction() - if not transaction: - return self.__wrapped__.__next__() - - try: - return_val = self.__wrapped__.__next__() - if return_val: - prompt = list(OPENAI_AUDIT_LOG_CONTENTS.keys())[-1] - if not getattr(return_val, "data", "").startswith("[DONE]"): - OPENAI_AUDIT_LOG_CONTENTS[prompt][2].append(return_val.json()) - return return_val - except Exception: - raise - - def close(self): - return self.__wrapped__.close() - - -@pytest.fixture(scope="session") -def wrap_stream_iter_events(): - def _wrap_stream_iter_events(wrapped, instance, args, kwargs): - transaction = current_transaction() - - if not transaction: - return wrapped(*args, **kwargs) - - return_val = wrapped(*args, **kwargs) - proxied_return_val = LLMStreamAuditLogProxy(return_val) - return proxied_return_val - - return _wrap_stream_iter_events + return "invoke" if method in ("invoke", "stream", "stream_events") else "ainvoke" + return method diff --git a/tests/mlmodel_langchain/test_agents.py b/tests/mlmodel_langchain/test_agents.py index 6a1c471ecd..49fd8d47a2 100644 --- a/tests/mlmodel_langchain/test_agents.py +++ b/tests/mlmodel_langchain/test_agents.py @@ -33,7 +33,11 @@ from newrelic.common.object_names import callable_name from newrelic.common.object_wrapper import transient_function_wrapper -PROMPT = {"messages": [HumanMessage('Use a tool to add an exclamation to the word "Hello"')]} +PROMPT = { + "messages": [ + HumanMessage('Use a tool to add an exclamation to the word "Hello". Answer in one word with no formatting.') + ] +} ERROR_PROMPT = {"messages": [HumanMessage('Use a tool to add an exclamation to the word "exc"')]} SYNC_METHODS = {"invoke", "stream"} diff --git a/tests/mlmodel_langchain/test_chain.py b/tests/mlmodel_langchain/test_chain.py index cd693d1a69..bad3d0971e 100644 --- a/tests/mlmodel_langchain/test_chain.py +++ b/tests/mlmodel_langchain/test_chain.py @@ -18,6 +18,7 @@ import openai import pytest from langchain_community.vectorstores.faiss import FAISS +from langchain_openai import ChatOpenAI from testing_support.fixtures import reset_core_stats_engine, validate_attributes from testing_support.ml_testing_utils import ( disabled_ai_monitoring_record_content_settings, @@ -38,18 +39,29 @@ from newrelic.common.object_names import callable_name try: - from langchain_classic.chains import create_retrieval_chain from langchain_classic.chains.combine_documents import create_stuff_documents_chain from langchain_classic.chains.openai_functions import ( create_structured_output_chain, create_structured_output_runnable, ) + from langchain_classic.chains.retrieval import create_retrieval_chain from langchain_core.output_parsers import BaseOutputParser except ImportError: - from langchain.chains import create_retrieval_chain - from langchain.chains.combine_documents import create_stuff_documents_chain - from langchain.chains.openai_functions import create_structured_output_chain, create_structured_output_runnable - from langchain.schema import BaseOutputParser + # Before Langchain v1.3.9 + try: + from langchain_classic.chains import create_retrieval_chain + from langchain_classic.chains.combine_documents import create_stuff_documents_chain + from langchain_classic.chains.openai_functions import ( + create_structured_output_chain, + create_structured_output_runnable, + ) + from langchain_core.output_parsers import BaseOutputParser + except ImportError: + # Before Langchain v1.0.0 + from langchain.chains import create_retrieval_chain + from langchain.chains.combine_documents import create_stuff_documents_chain + from langchain.chains.openai_functions import create_structured_output_chain, create_structured_output_runnable + from langchain.schema import BaseOutputParser chat_completion_recorded_events_invoke_langchain_error = [ @@ -386,298 +398,552 @@ recorded_events_retrieval_chain_response = [ [ - {"type": "LlmEmbedding"}, + {"timestamp": None, "type": "LlmEmbedding"}, { + "duration": None, "id": None, - "span_id": None, - "trace_id": "trace-id", + "ingest_source": "Python", + "input": "[[3923, 374, 220, 17, 489, 220, 19, 30]]", "request.model": "text-embedding-ada-002", "request_id": None, - "duration": None, "response.model": "text-embedding-ada-002-v2", "response.organization": "nr-test-org", "response.headers.llmVersion": "2020-10-01", "response.headers.ratelimitLimitRequests": 10000, - "response.headers.ratelimitLimitTokens": 10000000, + "response.headers.ratelimitLimitTokens": 50000000, "response.headers.ratelimitRemainingRequests": 9999, - "response.headers.ratelimitRemainingTokens": 9999992, + "response.headers.ratelimitRemainingTokens": 49999975, "response.headers.ratelimitResetRequests": "6ms", "response.headers.ratelimitResetTokens": "0s", "response.usage.total_tokens": 8, + "span_id": None, + "trace_id": "trace-id", "vendor": "openai", - "ingest_source": "Python", - "input": "[[3923, 374, 220, 17, 489, 220, 19, 30]]", }, ], [ - {"type": "LlmEmbedding"}, + {"timestamp": None, "type": "LlmEmbedding"}, { + "duration": None, "id": None, - "span_id": None, - "trace_id": "trace-id", + "ingest_source": "Python", + "input": "[[10590]]", "request.model": "text-embedding-ada-002", "request_id": None, - "duration": None, "response.model": "text-embedding-ada-002-v2", "response.organization": "nr-test-org", "response.headers.llmVersion": "2020-10-01", "response.headers.ratelimitLimitRequests": 10000, - "response.headers.ratelimitLimitTokens": 10000000, + "response.headers.ratelimitLimitTokens": 50000000, "response.headers.ratelimitRemainingRequests": 9999, - "response.headers.ratelimitRemainingTokens": 9999998, + "response.headers.ratelimitRemainingTokens": 49999975, "response.headers.ratelimitResetRequests": "6ms", "response.headers.ratelimitResetTokens": "0s", "response.usage.total_tokens": 1, + "span_id": None, + "trace_id": "trace-id", "vendor": "openai", - "ingest_source": "Python", - "input": "[[10590]]", }, ], [ - {"type": "LlmVectorSearch"}, + {"timestamp": None, "type": "LlmVectorSearch"}, { - "request.k": 4, "duration": None, + "id": None, + "ingest_source": "Python", + "request.k": 4, + "request.query": "math", "response.number_of_documents": 1, "span_id": None, "trace_id": "trace-id", - "id": None, "vendor": "langchain", - "ingest_source": "Python", - "request.query": "math", }, ], [ - {"type": "LlmVectorSearchResult"}, + {"timestamp": None, "type": "LlmVectorSearchResult"}, { "id": None, + "ingest_source": "Python", + "page_content": "What is 2 + 4?", "search_id": None, "sequence": 0, "span_id": None, "trace_id": "trace-id", "vendor": "langchain", - "ingest_source": "Python", - "page_content": "What is 2 + 4?", }, ], [ - {"type": "LlmChatCompletionMessage"}, + {"timestamp": None, "type": "LlmChatCompletionSummary"}, { + "duration": None, "id": None, - "request_id": None, + "ingest_source": "Python", + "request_id": "", + "response.number_of_messages": 2, "span_id": None, + "timestamp": None, "trace_id": "trace-id", + "vendor": "langchain", + "virtual_llm": True, + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { "completion_id": None, - "sequence": 1, + "content": "{'input': 'math'}", + "id": None, + "ingest_source": "Python", + "request_id": "", + "role": "user", + "sequence": 0, + "span_id": None, + "timestamp": None, + "trace_id": "trace-id", "vendor": "langchain", + "virtual_llm": True, + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { + "completion_id": None, + "content": "page_content='What is 2 + 4?'", + "id": None, "ingest_source": "Python", "is_response": True, + "request_id": "", "role": "assistant", + "sequence": 1, + "span_id": None, + "trace_id": "trace-id", + "vendor": "langchain", "virtual_llm": True, - "content": "page_content='What is 2 + 4?'", }, ], [ - {"type": "LlmChatCompletionSummary"}, + {"timestamp": None, "type": "LlmChatCompletionSummary"}, { + "duration": None, "id": None, - "timestamp": None, - "span_id": None, - "trace_id": "trace-id", - "request.model": "gpt-3.5-turbo", - "request.temperature": 0.7, - "vendor": "openai", "ingest_source": "Python", + "request.model": "gpt-3.5-turbo", "request_id": None, - "duration": None, "response.model": "gpt-3.5-turbo-0125", - "response.organization": "nr-test-org", "response.choices.finish_reason": "stop", "response.headers.llmVersion": "2020-10-01", "response.headers.ratelimitLimitRequests": 10000, "response.headers.ratelimitLimitTokens": 50000000, "response.headers.ratelimitRemainingRequests": 9999, - "response.headers.ratelimitRemainingTokens": 49999927, + "response.headers.ratelimitRemainingTokens": 49999975, "response.headers.ratelimitResetRequests": "6ms", "response.headers.ratelimitResetTokens": "0s", + "response.usage.completion_tokens": 210, "response.usage.prompt_tokens": 73, - "response.usage.completion_tokens": 337, - "response.usage.total_tokens": 410, + "response.usage.total_tokens": 283, "response.number_of_messages": 3, + "response.organization": "nr-test-org", + "span_id": None, + "timestamp": None, + "trace_id": "trace-id", + "vendor": "openai", }, ], [ - {"type": "LlmChatCompletionMessage"}, + {"timestamp": None, "type": "LlmChatCompletionMessage"}, { + "completion_id": None, "id": None, - "timestamp": None, + "ingest_source": "Python", "request_id": None, - "span_id": None, - "trace_id": "trace-id", + "response.model": "gpt-3.5-turbo-0125", "role": "system", - "completion_id": None, "sequence": 0, - "response.model": "gpt-3.5-turbo-0125", + "span_id": None, + "timestamp": None, + "trace_id": "trace-id", "vendor": "openai", "token_count": 0, - "ingest_source": "Python", "content": "You are a generator of quiz questions for a seminar. Use the following pieces of retrieved context to generate 5 multiple choice questions (A,B,C,D) on the subject matter. Use a three sentence maximum and keep the answer concise. Render the output as HTML\n\nWhat is 2 + 4?", }, ], [ - {"type": "LlmChatCompletionMessage"}, + {"timestamp": None, "type": "LlmChatCompletionMessage"}, { + "completion_id": None, + "content": "math", "id": None, - "timestamp": None, "request_id": None, - "span_id": None, - "trace_id": "trace-id", + "response.model": "gpt-3.5-turbo-0125", "role": "user", - "completion_id": None, "sequence": 1, - "response.model": "gpt-3.5-turbo-0125", + "span_id": None, + "timestamp": None, + "trace_id": "trace-id", "vendor": "openai", "token_count": 0, "ingest_source": "Python", - "content": "math", }, ], [ - {"type": "LlmChatCompletionMessage"}, + {"timestamp": None, "type": "LlmChatCompletionMessage"}, { + "completion_id": None, + "content": "```html\n" + "1. What is the result of 5 x 3?\n" + " A) 12\n" + " B) 15\n" + " C) 18\n" + " D) 20\n" + "\n" + "2. What is the solution to 10 / 2?\n" + " A) 3\n" + " B) 4\n" + " C) 5\n" + " D) 6\n" + "\n" + "3. If a rectangle has a length of 8 and a width of 6, what is its area?\n" + " A) 12\n" + " B) 24\n" + " C) 36\n" + " D) 48\n" + "\n" + "4. What is the value of 3 squared?\n" + " A) 6\n" + " B) 8\n" + " C) 9\n" + " D) 12\n" + "\n" + "5. If a store sells a shirt for $20 and a customer buys 3 shirts, how much " + "do they owe?\n" + " A) $40\n" + " B) $50\n" + " C) $60\n" + " D) $70\n" + "```\n", "id": None, "request_id": None, - "span_id": None, - "trace_id": "trace-id", + "response.model": "gpt-3.5-turbo-0125", "role": "assistant", - "completion_id": None, "sequence": 2, - "response.model": "gpt-3.5-turbo-0125", + "span_id": None, + "trace_id": "trace-id", "vendor": "openai", "token_count": 0, "ingest_source": "Python", "is_response": True, - "content": "```html\n\n\n\n Math Quiz\n\n\n

Math Quiz Questions

\n
    \n
  1. What is the result of 5 + 3?
  2. \n \n
  3. What is the product of 6 x 7?
  4. \n \n
  5. What is the square root of 64?
  6. \n \n
  7. What is the result of 12 / 4?
  8. \n \n
  9. What is the sum of 15 + 9?
  10. \n \n
\n\n\n```", }, ], [ - {"type": "LlmChatCompletionMessage"}, + {"timestamp": None, "type": "LlmChatCompletionSummary"}, { + "duration": None, "id": None, - "timestamp": None, - "request_id": None, + "ingest_source": "Python", + "request.model": "gpt-3.5-turbo", + "request_id": "", + "response.model": "gpt-3.5-turbo-0125", + "response.number_of_messages": 2, "span_id": None, + "timestamp": None, "trace_id": "trace-id", - "completion_id": None, - "sequence": 0, - "response.model": "gpt-3.5-turbo-0125", "vendor": "langchain", - "ingest_source": "Python", - "role": "user", "virtual_llm": True, - "content": "{'input': 'math', 'context': [Document(id='1234', metadata={}, page_content='What is 2 + 4?')]}", }, ], [ - {"type": "LlmChatCompletionMessage"}, + {"timestamp": None, "type": "LlmChatCompletionMessage"}, { + "completion_id": None, + "content": "{'input': 'math', 'context': [Document(id='1234', metadata={}, " + "page_content='What is 2 + 4?')]}", "id": None, - "request_id": None, + "ingest_source": "Python", + "request_id": "", + "response.model": "gpt-3.5-turbo-0125", + "role": "user", + "sequence": 0, "span_id": None, + "timestamp": None, "trace_id": "trace-id", - "completion_id": None, - "sequence": 1, - "response.model": "gpt-3.5-turbo-0125", "vendor": "langchain", + "virtual_llm": True, + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { + "completion_id": None, + "content": "```html\n" + "1. What is the result of 5 x 3?\n" + " A) 12\n" + " B) 15\n" + " C) 18\n" + " D) 20\n" + "\n" + "2. What is the solution to 10 / 2?\n" + " A) 3\n" + " B) 4\n" + " C) 5\n" + " D) 6\n" + "\n" + "3. If a rectangle has a length of 8 and a width of 6, what is " + "its area?\n" + " A) 12\n" + " B) 24\n" + " C) 36\n" + " D) 48\n" + "\n" + "4. What is the value of 3 squared?\n" + " A) 6\n" + " B) 8\n" + " C) 9\n" + " D) 12\n" + "\n" + "5. If a store sells a shirt for $20 and a customer buys 3 " + "shirts, how much do they owe?\n" + " A) $40\n" + " B) $50\n" + " C) $60\n" + " D) $70\n" + "```\n", + "id": None, "ingest_source": "Python", - "role": "assistant", "is_response": True, + "request_id": "", + "response.model": "gpt-3.5-turbo-0125", + "role": "assistant", + "sequence": 1, + "span_id": None, + "trace_id": "trace-id", + "vendor": "langchain", "virtual_llm": True, - "content": "```html\n\n\n\n Math Quiz\n\n\n

Math Quiz Questions

\n
    \n
  1. What is the result of 5 + 3?
  2. \n \n
  3. What is the product of 6 x 7?
  4. \n \n
  5. What is the square root of 64?
  6. \n \n
  7. What is the result of 12 / 4?
  8. \n \n
  9. What is the sum of 15 + 9?
  10. \n \n
\n\n\n```", }, ], [ - {"type": "LlmChatCompletionMessage"}, + {"timestamp": None, "type": "LlmChatCompletionSummary"}, { + "duration": None, "id": None, - "request_id": None, + "ingest_source": "Python", + "request_id": "", + "response.model": "gpt-3.5-turbo-0125", + "response.number_of_messages": 2, "span_id": None, + "timestamp": None, "trace_id": "trace-id", + "vendor": "langchain", + "virtual_llm": True, + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { "completion_id": None, - "sequence": 1, + "content": "{'input': 'math'}", + "id": None, + "ingest_source": "Python", + "request_id": "", "response.model": "gpt-3.5-turbo-0125", + "role": "user", + "sequence": 0, + "span_id": None, + "timestamp": None, + "trace_id": "trace-id", "vendor": "langchain", + "virtual_llm": True, + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { + "completion_id": None, + "content": "{'input': 'math', 'context': [Document(id='1234', metadata={}, " + "page_content='What is 2 + 4?')], 'answer': '```html\\n1. What " + "is the result of 5 x 3?\\n A) 12\\n B) 15\\n C) 18\\n " + "D) 20\\n\\n2. What is the solution to 10 / 2?\\n A) 3\\n B) " + "4\\n C) 5\\n D) 6\\n\\n3. If a rectangle has a length of 8 " + "and a width of 6, what is its area?\\n A) 12\\n B) 24\\n " + "C) 36\\n D) 48\\n\\n4. What is the value of 3 squared?\\n " + "A) 6\\n B) 8\\n C) 9\\n D) 12\\n\\n5. If a store sells a " + "shirt for $20 and a customer buys 3 shirts, how much do they " + "owe?\\n A) $40\\n B) $50\\n C) $60\\n D) $70\\n```\\n'}", + "id": None, "ingest_source": "Python", - "role": "assistant", "is_response": True, + "request_id": "", + "response.model": "gpt-3.5-turbo-0125", + "role": "assistant", + "sequence": 1, + "span_id": None, + "trace_id": "trace-id", + "vendor": "langchain", "virtual_llm": True, - "content": "{'input': 'math', 'context': [Document(id='1234', metadata={}, page_content='What is 2 + 4?')], 'answer': '```html\\n\\n\\n\\n Math Quiz\\n\\n\\n

Math Quiz Questions

\\n
    \\n
  1. What is the result of 5 + 3?
  2. \\n \\n
  3. What is the product of 6 x 7?
  4. \\n \\n
  5. What is the square root of 64?
  6. \\n \\n
  7. What is the result of 12 / 4?
  8. \\n \\n
  9. What is the sum of 15 + 9?
  10. \\n \\n
\\n\\n\\n```'}", }, ], ] chat_completion_recorded_events_str_response = [ - ( - {"type": "LlmChatCompletionSummary"}, + [ + {"timestamp": None, "type": "LlmChatCompletionSummary"}, { + "duration": None, "id": None, - "timestamp": None, + "ingest_source": "Python", + "llm.context": "attr", "llm.conversation_id": "my-awesome-id", "llm.foo": "bar", + "request.model": "gpt-3.5-turbo", + "request_id": None, + "response.choices.finish_reason": "stop", + "response.headers.llmVersion": "2020-10-01", + "response.headers.ratelimitLimitRequests": 10000, + "response.headers.ratelimitLimitTokens": 50000000, + "response.headers.ratelimitRemainingRequests": 9999, + "response.headers.ratelimitRemainingTokens": 49999975, + "response.headers.ratelimitResetRequests": "6ms", + "response.headers.ratelimitResetTokens": "0s", + "response.model": "gpt-3.5-turbo-0125", + "response.number_of_messages": 3, + "response.organization": "nr-test-org", + "response.usage.completion_tokens": 2, + "response.usage.prompt_tokens": 46, + "response.usage.total_tokens": 48, "span_id": None, + "timestamp": None, "trace_id": "trace-id", - "vendor": "langchain", + "vendor": "openai", + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { + "completion_id": None, + "content": "You are a helpful assistant who generates a random first name. A user will pass in a first letter, and " + "you should generate a name that starts with that first letter.", + "id": None, "ingest_source": "Python", - "virtual_llm": True, + "llm.context": "attr", + "llm.conversation_id": "my-awesome-id", + "llm.foo": "bar", "request_id": None, - "duration": None, - "request.model": "gpt-3.5-turbo", "response.model": "gpt-3.5-turbo-0125", - "response.number_of_messages": 2, - "metadata.id": "123", + "role": "system", + "sequence": 0, + "span_id": None, + "timestamp": None, + "token_count": 0, + "trace_id": "trace-id", + "vendor": "openai", }, - ), - ( - {"type": "LlmChatCompletionMessage"}, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, { + "completion_id": None, + "content": "M", "id": None, - "timestamp": None, + "ingest_source": "Python", + "llm.context": "attr", "llm.conversation_id": "my-awesome-id", "llm.foo": "bar", "request_id": None, + "response.model": "gpt-3.5-turbo-0125", + "role": "user", + "sequence": 1, "span_id": None, + "timestamp": None, + "token_count": 0, "trace_id": "trace-id", - "content": "{'text': 'M'}", + "vendor": "openai", + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { "completion_id": None, - "sequence": 0, - "response.model": "gpt-3.5-turbo-0125", - "vendor": "langchain", + "content": "Matilda", + "id": None, "ingest_source": "Python", - "role": "user", - "virtual_llm": True, + "is_response": True, + "llm.context": "attr", + "llm.conversation_id": "my-awesome-id", + "llm.foo": "bar", + "request_id": None, + "response.model": "gpt-3.5-turbo-0125", + "role": "assistant", + "sequence": 2, + "span_id": None, + "token_count": 0, + "trace_id": "trace-id", + "vendor": "openai", }, - ), - ( - {"type": "LlmChatCompletionMessage"}, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionSummary"}, { + "duration": None, "id": None, + "ingest_source": "Python", + "llm.context": "attr", "llm.conversation_id": "my-awesome-id", "llm.foo": "bar", - "request_id": None, + "metadata.id": "123", + "request.model": "gpt-3.5-turbo", + "request_id": "", + "response.model": "gpt-3.5-turbo-0125", + "response.number_of_messages": 2, "span_id": None, + "timestamp": None, "trace_id": "trace-id", - "content": "Milo", + "vendor": "langchain", + "virtual_llm": True, + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { "completion_id": None, - "sequence": 1, + "content": "{'text': 'M'}", + "id": None, + "ingest_source": "Python", + "llm.context": "attr", + "llm.conversation_id": "my-awesome-id", + "llm.foo": "bar", + "request_id": "", "response.model": "gpt-3.5-turbo-0125", + "role": "user", + "sequence": 0, + "span_id": None, + "timestamp": None, + "trace_id": "trace-id", "vendor": "langchain", + "virtual_llm": True, + }, + ], + [ + {"timestamp": None, "type": "LlmChatCompletionMessage"}, + { + "completion_id": None, + "content": "Matilda", + "id": None, "ingest_source": "Python", - "role": "assistant", "is_response": True, + "llm.context": "attr", + "llm.conversation_id": "my-awesome-id", + "llm.foo": "bar", + "request_id": "", + "response.model": "gpt-3.5-turbo-0125", + "role": "assistant", + "sequence": 1, + "span_id": None, + "trace_id": "trace-id", + "vendor": "langchain", "virtual_llm": True, }, - ), + ], ] + chat_completion_recorded_events_list_response = [ ( {"type": "LlmChatCompletionSummary"}, @@ -1077,7 +1343,6 @@ def _test(): ) def test_langchain_chain_error_in_openai( set_trace_info, - chat_openai_client, json_schema, prompt_openai_error, create_function, @@ -1085,6 +1350,7 @@ def test_langchain_chain_error_in_openai( call_function_args, call_function_kwargs, expected_events, + monkeypatch, ): @reset_core_stats_engine() @validate_transaction_error_event_count(1) @@ -1104,7 +1370,11 @@ def _test(): add_custom_attribute("llm.foo", "bar") add_custom_attribute("non_llm_attr", "python-agent") - runnable = create_function(json_schema, chat_openai_client, prompt_openai_error) + # Ensure the OpenAI key is set incorrectly for this test + monkeypatch.delenv("OPENAI_API_KEY") + chat_openai_client_no_api_key = ChatOpenAI(api_key="FAKE-OPENAI-KEY") + + runnable = create_function(json_schema, chat_openai_client_no_api_key, prompt_openai_error) with pytest.raises(openai.AuthenticationError): with WithLlmCustomAttributes({"context": "attr"}): @@ -1438,6 +1708,8 @@ def _test(): _test() +# NOTE: To reproduce these tests, set the `OPENAI_API_KEY` +# environment variable to an invalid key. @pytest.mark.parametrize( "create_function,call_function,call_function_args,call_function_kwargs,expected_events", ( @@ -1477,7 +1749,6 @@ def _test(): ) def test_async_langchain_chain_error_in_openai( set_trace_info, - chat_openai_client, json_schema, prompt_openai_error, create_function, @@ -1485,6 +1756,7 @@ def test_async_langchain_chain_error_in_openai( call_function_args, call_function_kwargs, expected_events, + monkeypatch, loop, ): @reset_core_stats_engine() @@ -1505,7 +1777,11 @@ def _test(): add_custom_attribute("llm.foo", "bar") add_custom_attribute("non_llm_attr", "python-agent") - runnable = create_function(json_schema, chat_openai_client, prompt_openai_error) + # Ensure the OpenAI key is set incorrectly for this test + monkeypatch.delenv("OPENAI_API_KEY") + chat_openai_client_no_api_key = ChatOpenAI(api_key="FAKE-OPENAI-KEY") + + runnable = create_function(json_schema, chat_openai_client_no_api_key, prompt_openai_error) with pytest.raises(openai.AuthenticationError): with WithLlmCustomAttributes({"context": "attr"}): diff --git a/tests/mlmodel_langchain/test_state_graph.py b/tests/mlmodel_langchain/test_state_graph.py index 4c857e2206..6f571ae10b 100644 --- a/tests/mlmodel_langchain/test_state_graph.py +++ b/tests/mlmodel_langchain/test_state_graph.py @@ -21,7 +21,11 @@ from newrelic.api.background_task import background_task CLIENT_PROMPT = {"messages": [HumanMessage("What is the capital of France? Answer in one word.")]} -AGENT_PROMPT = {"messages": [HumanMessage('Use a tool to add an exclamation to the word "Hello"')]} +AGENT_PROMPT = { + "messages": [ + HumanMessage('Use a tool to add an exclamation to the word "Hello". Answer in one word with no formatting.') + ] +} client_recorded_events = [ [ @@ -31,22 +35,21 @@ "id": None, "ingest_source": "Python", "request.model": "gpt-3.5-turbo", - "request.temperature": 0.7, - "request_id": "req_22204b237d22427fbfd99c665d8a9964", + "request_id": None, "response.choices.finish_reason": "stop", "response.headers.llmVersion": "2020-10-01", "response.headers.ratelimitLimitRequests": 10000, "response.headers.ratelimitLimitTokens": 50000000, "response.headers.ratelimitRemainingRequests": 9999, - "response.headers.ratelimitRemainingTokens": 49999985, + "response.headers.ratelimitRemainingTokens": 49999975, "response.headers.ratelimitResetRequests": "6ms", "response.headers.ratelimitResetTokens": "0s", "response.model": "gpt-3.5-turbo-0125", "response.number_of_messages": 2, "response.organization": "nr-test-org", - "response.usage.completion_tokens": 2, + "response.usage.completion_tokens": 1, "response.usage.prompt_tokens": 19, - "response.usage.total_tokens": 21, + "response.usage.total_tokens": 20, "span_id": None, "timestamp": None, "trace_id": None, @@ -58,9 +61,9 @@ { "completion_id": None, "content": "What is the capital of France? Answer in one word.", - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i-0", + "id": None, "ingest_source": "Python", - "request_id": "req_22204b237d22427fbfd99c665d8a9964", + "request_id": None, "response.model": "gpt-3.5-turbo-0125", "role": "user", "sequence": 0, @@ -76,10 +79,10 @@ { "completion_id": None, "content": "Paris", - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i-1", + "id": None, "ingest_source": "Python", "is_response": True, - "request_id": "req_22204b237d22427fbfd99c665d8a9964", + "request_id": None, "response.model": "gpt-3.5-turbo-0125", "role": "assistant", "sequence": 1, @@ -99,14 +102,13 @@ "id": None, "ingest_source": "Python", "request.model": "gpt-3.5-turbo", - "request.temperature": 0.7, - "request_id": "req_22204b237d22427fbfd99c665d8a9964", + "request_id": None, "response.choices.finish_reason": "stop", "response.headers.llmVersion": "2020-10-01", "response.headers.ratelimitLimitRequests": 10000, "response.headers.ratelimitLimitTokens": 50000000, "response.headers.ratelimitRemainingRequests": 9999, - "response.headers.ratelimitRemainingTokens": 49999985, + "response.headers.ratelimitRemainingTokens": 49999975, "response.headers.ratelimitResetRequests": "6ms", "response.headers.ratelimitResetTokens": "0s", "response.model": "gpt-3.5-turbo-0125", @@ -114,9 +116,9 @@ "response.organization": "nr-test-org", # langchain's ChatOpenAI.stream() passes stream_options={"include_usage": True} # by default, so the final usage chunk is captured and these are populated. - "response.usage.completion_tokens": 2, + "response.usage.completion_tokens": 1, "response.usage.prompt_tokens": 19, - "response.usage.total_tokens": 21, + "response.usage.total_tokens": 20, "span_id": None, "time_to_first_token": None, "timestamp": None, @@ -129,9 +131,9 @@ { "completion_id": None, "content": "What is the capital of France? Answer in one word.", - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i-0", + "id": None, "ingest_source": "Python", - "request_id": "req_22204b237d22427fbfd99c665d8a9964", + "request_id": None, "response.model": "gpt-3.5-turbo-0125", "role": "user", "sequence": 0, @@ -147,10 +149,10 @@ { "completion_id": None, "content": "Paris", - "id": "chatcmpl-DelITaJCJy951hwON0psdz2H9dF7i-1", + "id": None, "ingest_source": "Python", "is_response": True, - "request_id": "req_22204b237d22427fbfd99c665d8a9964", + "request_id": None, "response.model": "gpt-3.5-turbo-0125", "role": "assistant", "sequence": 1, @@ -171,22 +173,21 @@ "id": None, "ingest_source": "Python", "request.model": "gpt-3.5-turbo", - "request.temperature": 0.7, - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": None, "response.choices.finish_reason": "tool_calls", "response.headers.llmVersion": "2020-10-01", "response.headers.ratelimitLimitRequests": 10000, "response.headers.ratelimitLimitTokens": 50000000, "response.headers.ratelimitRemainingRequests": 9999, - "response.headers.ratelimitRemainingTokens": 49999974, + "response.headers.ratelimitRemainingTokens": 49999975, "response.headers.ratelimitResetRequests": "6ms", "response.headers.ratelimitResetTokens": "0s", "response.model": "gpt-3.5-turbo-0125", "response.number_of_messages": 2, "response.organization": "nr-test-org", "response.usage.completion_tokens": 15, - "response.usage.prompt_tokens": 70, - "response.usage.total_tokens": 85, + "response.usage.prompt_tokens": 78, + "response.usage.total_tokens": 93, "span_id": None, "timestamp": None, "trace_id": None, @@ -198,9 +199,9 @@ { "completion_id": None, "content": "You are a text manipulation algorithm.", - "id": "chatcmpl-CukvsGfSQihNO9I3FTqaNKERWtUca-0", + "id": None, "ingest_source": "Python", - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": "req_c78b73e8e4c84348bd4effe1cf6eff49", "response.model": "gpt-3.5-turbo-0125", "role": "system", "sequence": 0, @@ -215,10 +216,10 @@ {"timestamp": None, "type": "LlmChatCompletionMessage"}, { "completion_id": None, - "content": 'Use a tool to add an exclamation to the word "Hello"', - "id": "chatcmpl-CukvsGfSQihNO9I3FTqaNKERWtUca-1", + "content": 'Use a tool to add an exclamation to the word "Hello". Answer in one word with no formatting.', + "id": None, "ingest_source": "Python", - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": "req_c78b73e8e4c84348bd4effe1cf6eff49", "response.model": "gpt-3.5-turbo-0125", "role": "user", "sequence": 1, @@ -239,7 +240,7 @@ "input": "{'message': 'Hello'}", "name": "add_exclamation", "output": "Hello!", - "run_id": "call_ymnsNurMgr3atFVr7BnJ2XYK", + "run_id": None, "span_id": None, "trace_id": None, "vendor": "langchain", @@ -252,22 +253,21 @@ "id": None, "ingest_source": "Python", "request.model": "gpt-3.5-turbo", - "request.temperature": 0.7, - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": None, "response.choices.finish_reason": "stop", "response.headers.llmVersion": "2020-10-01", "response.headers.ratelimitLimitRequests": 10000, "response.headers.ratelimitLimitTokens": 50000000, "response.headers.ratelimitRemainingRequests": 9999, - "response.headers.ratelimitRemainingTokens": 49999970, + "response.headers.ratelimitRemainingTokens": 49999975, "response.headers.ratelimitResetRequests": "6ms", "response.headers.ratelimitResetTokens": "0s", "response.model": "gpt-3.5-turbo-0125", "response.number_of_messages": 5, "response.organization": "nr-test-org", - "response.usage.completion_tokens": 16, - "response.usage.prompt_tokens": 96, - "response.usage.total_tokens": 112, + "response.usage.completion_tokens": 3, + "response.usage.prompt_tokens": 107, + "response.usage.total_tokens": 110, "span_id": None, "timestamp": None, "trace_id": None, @@ -279,9 +279,9 @@ { "completion_id": None, "content": "You are a text manipulation algorithm.", - "id": "chatcmpl-CukvtgYHPS8HRHqCQiQgQrs7a2Tx1-0", + "id": None, "ingest_source": "Python", - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": "req_c78b73e8e4c84348bd4effe1cf6eff49", "response.model": "gpt-3.5-turbo-0125", "role": "system", "sequence": 0, @@ -296,10 +296,10 @@ {"timestamp": None, "type": "LlmChatCompletionMessage"}, { "completion_id": None, - "content": 'Use a tool to add an exclamation to the word "Hello"', - "id": "chatcmpl-CukvtgYHPS8HRHqCQiQgQrs7a2Tx1-1", + "content": 'Use a tool to add an exclamation to the word "Hello". Answer in one word with no formatting.', + "id": None, "ingest_source": "Python", - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": "req_c78b73e8e4c84348bd4effe1cf6eff49", "response.model": "gpt-3.5-turbo-0125", "role": "user", "sequence": 1, @@ -314,9 +314,9 @@ {"timestamp": None, "type": "LlmChatCompletionMessage"}, { "completion_id": None, - "id": "chatcmpl-CukvtgYHPS8HRHqCQiQgQrs7a2Tx1-2", + "id": None, "ingest_source": "Python", - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": None, "response.model": "gpt-3.5-turbo-0125", "role": "assistant", "sequence": 2, @@ -332,9 +332,9 @@ { "completion_id": None, "content": "Hello!", - "id": "chatcmpl-CukvtgYHPS8HRHqCQiQgQrs7a2Tx1-3", + "id": None, "ingest_source": "Python", - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": None, "response.model": "gpt-3.5-turbo-0125", "role": "tool", "sequence": 3, @@ -349,11 +349,11 @@ {"timestamp": None, "type": "LlmChatCompletionMessage"}, { "completion_id": None, - "content": 'The word "Hello" with an exclamation mark added is "Hello!"', - "id": "chatcmpl-CukvtgYHPS8HRHqCQiQgQrs7a2Tx1-4", + "content": "Hello!", + "id": None, "ingest_source": "Python", "is_response": True, - "request_id": "req_619548c272db4f1ab380b83de9fdedef", + "request_id": None, "response.model": "gpt-3.5-turbo-0125", "role": "assistant", "sequence": 4, @@ -396,7 +396,7 @@ def _build_graph(node): return builder.compile() -@pytest.fixture(scope="session") +@pytest.fixture def create_agent(chat_openai_client): def _create_agent(model="gpt-5.1", tools=None, system_prompt=None, name="my_agent"): from langchain.agents import create_agent diff --git a/tests/mlmodel_langchain/test_tools.py b/tests/mlmodel_langchain/test_tools.py index 3ad250fb45..e5a4b8dbef 100644 --- a/tests/mlmodel_langchain/test_tools.py +++ b/tests/mlmodel_langchain/test_tools.py @@ -34,7 +34,11 @@ from ._test_tools import add_exclamation, tool_method_name, tool_type -PROMPT = {"messages": [HumanMessage('Use a tool to add an exclamation to the word "Hello"')]} +PROMPT = { + "messages": [ + HumanMessage('Use a tool to add an exclamation to the word "Hello". Answer in one word with no formatting.') + ] +} ERROR_PROMPT = {"messages": [HumanMessage('Use a tool to add an exclamation to the word "exc"')]} SYNC_METHODS = {"invoke", "stream"} diff --git a/tests/testing_support/fixture/vcr.py b/tests/testing_support/fixture/vcr.py index 8d93a946aa..bf21995a62 100644 --- a/tests/testing_support/fixture/vcr.py +++ b/tests/testing_support/fixture/vcr.py @@ -79,6 +79,7 @@ raise ImportError("pytest-recording is required to use the vcr fixtures.") from exc import json +import os from pathlib import Path import pytest @@ -88,6 +89,7 @@ VCR_IGNORED_HEADERS = ["content-length", "traceparent", "tracestate", "user-agent", "x-goog-api-client"] VCR_REPLACE_HEADERS = [] # Must be tuples of (header_name, replacement_value) VCR_MATCH_ON = ["method", "scheme", "host", "port", "path", "body", "headers", "query"] +VCR_TIKTOKEN_ENCODINGS = [] # === Settings fixtures, required and overridable === @@ -217,6 +219,7 @@ def vcr_config( vcr_match_on, vcr_before_record_request, vcr_before_record_response, + vcr_cache_tiktoken_encodings, ): """ Combines the overridable settings fixtures into VCR.py's final configuration. @@ -297,3 +300,22 @@ def pytest_collection_modifyitems(items): """ for item in items: item.add_marker(pytest.mark.vcr) + + +@pytest.fixture +def vcr_cache_tiktoken_encodings(monkeypatch): + """Cache the tiktoken encodings before enabling VCR which blocks network access.""" + try: + import tiktoken + except ImportError: + return # tiktoken is not installed, skip caching + + # Set up temporary cache dir + tox_env_dir = os.environ.get("TOX_ENV_DIR", None) or Path.cwd() + cache_dir = Path(tox_env_dir) / ".tiktoken_cache" + monkeypatch.setenv("TIKTOKEN_CACHE_DIR", str(cache_dir)) + cache_dir.mkdir(parents=True, exist_ok=True) + + # Pre-fetch encodings used in tests + for encoding in VCR_TIKTOKEN_ENCODINGS: + tiktoken.get_encoding(encoding) diff --git a/tox.ini b/tox.ini index 30334daa1a..8f37351cce 100644 --- a/tox.ini +++ b/tox.ini @@ -168,9 +168,9 @@ envlist = python-framework_starlette-{py39,py310,py311,py312,py313,py314,py314t,pypy311}-starlettelatest, python-framework_strawberry-{py39,py310,py311,py312}-strawberry02352, python-framework_strawberry-{py39,py310,py311,py312,py313,py314,py314t}-strawberrylatest, - python-framework_tornado-{py39,py310,py311,py312,py313,py314,py314t}-tornadolatest, + ; python-framework_tornado-{py39,py310,py311,py312,py313,py314,py314t}-tornadolatest, ; Remove `python-framework_tornado-{py314,py314t}-tornadomaster` temporarily - python-framework_tornado-{py310,py311,py312,py313}-tornadomaster, + ; python-framework_tornado-{py310,py311,py312,py313}-tornadomaster, python-hybridagent_ariadne-{py39,py310,py311,py312,py313,py314,py314t}, python-hybridagent_dynamodb-{py39,py310,py311,py312,py313,py314,py314t}, python-hybridagent_fastapi-{py39,py310,py311,py312,py313,py314,py314t,pypy311}, @@ -214,6 +214,7 @@ deps = py39: pytest==8.4.2 WebTest==3.0.7 coverage + pytest-recording # Test Suite Dependencies adapter_asgiref-asgireflatest: asgiref @@ -399,9 +400,9 @@ deps = framework_strawberry: starlette framework_strawberry-strawberrylatest: strawberry-graphql framework_strawberry-strawberry02352: strawberry-graphql<0.236.0 - framework_tornado: pycurl - framework_tornado-tornadolatest: tornado - framework_tornado-tornadomaster: https://github.com/tornadoweb/tornado/archive/master.zip + ; framework_tornado: pycurl + ; framework_tornado-tornadolatest: tornado + ; framework_tornado-tornadomaster: https://github.com/tornadoweb/tornado/archive/master.zip hybridagent_aiopg: opentelemetry-api hybridagent_aiopg: opentelemetry-instrumentation-aiopg hybridagent_aiopg: aiopg @@ -469,6 +470,8 @@ deps = mlmodel_langchain: langchain-core mlmodel_langchain: langchain-community mlmodel_langchain: langchain-openai + mlmodel_langchain: langchain-classic + mlmodel_langchain: aiohttp<3.14.0 mlmodel_langchain: langgraph mlmodel_langchain: pypdf mlmodel_langchain: tiktoken @@ -500,9 +503,9 @@ setenv = without_extensions: NEW_RELIC_EXTENSIONS = false agent_features: NEW_RELIC_APDEX_T = 1000 framework_grpc: PYTHONPATH={toxinidir}/tests/:{toxinidir}/tests/framework_grpc/sample_application - framework_tornado: PYCURL_SSL_LIBRARY=openssl - framework_tornado: LDFLAGS=-L/usr/local/opt/openssl/lib - framework_tornado: CPPFLAGS=-I/usr/local/opt/openssl/include + ; framework_tornado: PYCURL_SSL_LIBRARY=openssl + ; framework_tornado: LDFLAGS=-L/usr/local/opt/openssl/lib + ; framework_tornado: CPPFLAGS=-I/usr/local/opt/openssl/include passenv = NEW_RELIC_DEVELOPER_MODE @@ -602,7 +605,7 @@ changedir = framework_sanic: tests/framework_sanic framework_starlette: tests/framework_starlette framework_strawberry: tests/framework_strawberry - framework_tornado: tests/framework_tornado + ; framework_tornado: tests/framework_tornado hybridagent_aiopg: tests/hybridagent_aiopg hybridagent_ariadne: tests/hybridagent_ariadne hybridagent_dynamodb: tests/hybridagent_dynamodb