diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7a7c6087d5..bc4a5324a1 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -11,4 +11,12 @@ RUN mkdir -p ${HOME} && \ groupadd --gid ${GID} vscode && \ useradd --uid ${UID} --gid ${GID} --home ${HOME} vscode && \ chown -R ${UID}:${GID} /home/vscode + +# Move pyenv installation +ENV PYENV_ROOT="${HOME}/.pyenv" +ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:${PATH}" +RUN mv /root/.pyenv /home/vscode/.pyenv && \ + chown -R vscode:vscode /home/vscode/.pyenv + +# Set user USER ${UID}:${GID} diff --git a/.github/actions/update-rpm-config/action.yml b/.github/actions/update-rpm-config/action.yml new file mode 100644 index 0000000000..9d19ebba0b --- /dev/null +++ b/.github/actions/update-rpm-config/action.yml @@ -0,0 +1,109 @@ +name: "update-rpm-config" +description: "Set current version of agent in rpm config using API." +inputs: + agent-language: + description: "Language agent to configure (eg. python)" + required: true + default: "python" + target-system: + description: "Target System: prod|staging|all" + required: true + default: "all" + agent-version: + description: "3-4 digit agent version number (eg. 1.2.3) with optional leading v (ignored)" + required: true + dry-run: + description: "Dry Run" + required: true + default: "false" + production-api-key: + description: "API key for New Relic Production" + required: false + staging-api-key: + description: "API key for New Relic Staging" + required: false + +runs: + using: "composite" + steps: + - name: Trim potential leading v from agent version + shell: bash + run: | + AGENT_VERSION=${{ inputs.agent-version }} + echo "AGENT_VERSION=${AGENT_VERSION#"v"}" >> $GITHUB_ENV + + - name: Generate Payload + shell: bash + run: | + echo "PAYLOAD='{ \"system_configuration\": { \"key\": \"${{ inputs.agent-language }}_agent_version\", \"value\": \"${{ env.AGENT_VERSION }}\" } }'" >> $GITHUB_ENV + + - name: Generate Content-Type + shell: bash + run: | + echo "CONTENT_TYPE='Content-Type: application/json'" >> $GITHUB_ENV + + - name: Update Staging system configuration page + shell: bash + if: ${{ inputs.dry-run == 'false' && (inputs.target-system == 'staging' || inputs.target-system == 'all') }} + run: | + curl -X POST 'https://staging-api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:${{ inputs.staging-api-key }}" -i \ + -H ${{ env.CONTENT_TYPE }} \ + -d ${{ env.PAYLOAD }} + + - name: Update Production system configuration page + shell: bash + if: ${{ inputs.dry-run == 'false' && (inputs.target-system == 'prod' || inputs.target-system == 'all') }} + run: | + curl -X POST 'https://api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:${{ inputs.production-api-key }}" -i \ + -H ${{ env.CONTENT_TYPE }} \ + -d ${{ env.PAYLOAD }} + + - name: Verify Staging system configuration update + shell: bash + if: ${{ inputs.dry-run == 'false' && (inputs.target-system == 'staging' || inputs.target-system == 'all') }} + run: | + STAGING_VERSION=$(curl -X GET 'https://staging-api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:${{ inputs.staging-api-key }}" \ + -H "${{ env.CONTENT_TYPE }}" | jq ".system_configurations | from_entries | .${{inputs.agent-language}}_agent_version") + + if [ "${{ env.AGENT_VERSION }}" != "$STAGING_VERSION" ]; then + echo "Staging version mismatch: $STAGING_VERSION" + exit 1 + fi + + - name: Verify Production system configuration update + shell: bash + if: ${{ inputs.dry-run == 'false' && (inputs.target-system == 'prod' || inputs.target-system == 'all') }} + run: | + PROD_VERSION=$(curl -X GET 'https://api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:${{ inputs.production-api-key }}" \ + -H "${{ env.CONTENT_TYPE }}" | jq ".system_configurations | from_entries | .${{inputs.agent-language}}_agent_version") + + if [ "${{ env.AGENT_VERSION }}" != "$PROD_VERSION" ]; then + echo "Production version mismatch: $PROD_VERSION" + exit 1 + fi + + - name: (dry-run) Update Staging system configuration page + shell: bash + if: ${{ inputs.dry-run != 'false' && (inputs.target-system == 'staging' || inputs.target-system == 'all') }} + run: | + cat << EOF + curl -X POST 'https://staging-api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:**REDACTED**" -i \ + -H ${{ env.CONTENT_TYPE }} \ + -d ${{ env.PAYLOAD }} + EOF + + - name: (dry-run) Update Production system configuration page + shell: bash + if: ${{ inputs.dry-run != 'false' && (inputs.target-system == 'prod' || inputs.target-system == 'all') }} + run: | + cat << EOF + curl -X POST 'https://api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:**REDACTED**" -i \ + -H ${{ env.CONTENT_TYPE }} \ + -d ${{ env.PAYLOAD }} + EOF diff --git a/.github/workflows/deploy-python.yml b/.github/workflows/deploy-python.yml index fe16ee4854..ca908b8250 100644 --- a/.github/workflows/deploy-python.yml +++ b/.github/workflows/deploy-python.yml @@ -80,3 +80,13 @@ jobs: env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + + - name: Update RPM Config + uses: ./.github/actions/update-rpm-config + with: + agent-language: "python" + target-system: "all" + agent-version: "${{ github.ref_name }}" + dry-run: "false" + production-api-key: ${{ secrets.NEW_RELIC_API_KEY_PRODUCTION }}" + staging-api-key: ${{ secrets.NEW_RELIC_API_KEY_STAGING }}" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e3b264a9fc..402d0c629c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -62,7 +62,6 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 with: python-version: "3.10" diff --git a/newrelic/common/package_version_utils.py b/newrelic/common/package_version_utils.py index 3152342b4d..68320b897f 100644 --- a/newrelic/common/package_version_utils.py +++ b/newrelic/common/package_version_utils.py @@ -14,6 +14,44 @@ import sys +try: + from functools import cache as _cache_package_versions +except ImportError: + from functools import wraps + from threading import Lock + + _package_version_cache = {} + _package_version_cache_lock = Lock() + + def _cache_package_versions(wrapped): + """ + Threadsafe implementation of caching for _get_package_version. + + Python 2.7 does not have the @functools.cache decorator, and + must be reimplemented with support for clearing the cache. + """ + + @wraps(wrapped) + def _wrapper(name): + if name in _package_version_cache: + return _package_version_cache[name] + + with _package_version_cache_lock: + if name in _package_version_cache: + return _package_version_cache[name] + + version = _package_version_cache[name] = wrapped(name) + return version + + def cache_clear(): + """Cache clear function to mimic @functools.cache""" + with _package_version_cache_lock: + _package_version_cache.clear() + + _wrapper.cache_clear = cache_clear + return _wrapper + + # Need to account for 4 possible variations of version declaration specified in (rejected) PEP 396 VERSION_ATTRS = ("__version__", "version", "__version_tuple__", "version_tuple") # nosec NULL_VERSIONS = frozenset((None, "", "0", "0.0", "0.0.0", "0.0.0.0", (0,), (0, 0), (0, 0, 0), (0, 0, 0, 0))) # nosec @@ -67,6 +105,7 @@ def int_or_str(value): return version +@_cache_package_versions def _get_package_version(name): module = sys.modules.get(name, None) version = None @@ -75,7 +114,7 @@ def _get_package_version(name): if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"): try: # In Python3.10+ packages_distribution can be checked for as well - if hasattr(sys.modules["importlib"].metadata, "packages_distributions"): # pylint: disable=E1101 + if hasattr(sys.modules["importlib"].metadata, "packages_distributions"): # pylint: disable=E1101 distributions = sys.modules["importlib"].metadata.packages_distributions() # pylint: disable=E1101 distribution_name = distributions.get(name, name) else: diff --git a/newrelic/config.py b/newrelic/config.py index 4e6a850fab..6fe19705f2 100644 --- a/newrelic/config.py +++ b/newrelic/config.py @@ -2463,11 +2463,6 @@ def _process_module_builtin_defaults(): "newrelic.hooks.messagebroker_kafkapython", "instrument_kafka_heartbeat", ) - _process_module_definition( - "kafka.consumer.group", - "newrelic.hooks.messagebroker_kafkapython", - "instrument_kafka_consumer_group", - ) _process_module_definition( "logging", diff --git a/newrelic/hooks/datastore_redis.py b/newrelic/hooks/datastore_redis.py index 6ba1920029..bbc517586d 100644 --- a/newrelic/hooks/datastore_redis.py +++ b/newrelic/hooks/datastore_redis.py @@ -14,10 +14,11 @@ import re -from newrelic.api.datastore_trace import DatastoreTrace +from newrelic.api.datastore_trace import DatastoreTrace, DatastoreTraceWrapper, wrap_datastore_trace from newrelic.api.time_trace import current_trace from newrelic.api.transaction import current_transaction -from newrelic.common.object_wrapper import function_wrapper, wrap_function_wrapper +from newrelic.common.object_wrapper import wrap_function_wrapper +from newrelic.common.async_wrapper import coroutine_wrapper, async_generator_wrapper, generator_wrapper _redis_client_sync_methods = { "acl_dryrun", @@ -136,6 +137,7 @@ "client_no_evict", "client_pause", "client_reply", + "client_setinfo", "client_setname", "client_tracking", "client_trackinginfo", @@ -162,7 +164,6 @@ "cluster_reset", "cluster_save_config", "cluster_set_config_epoch", - "client_setinfo", "cluster_setslot", "cluster_slaves", "cluster_slots", @@ -248,7 +249,7 @@ "hmset_dict", "hmset", "hrandfield", - "hscan_inter", + "hscan_iter", "hscan", "hset", "hsetnx", @@ -399,8 +400,8 @@ "syndump", "synupdate", "tagvals", - "tfcall", "tfcall_async", + "tfcall", "tfunction_delete", "tfunction_list", "tfunction_load", @@ -473,6 +474,13 @@ "zunionstore", } +_redis_client_gen_methods = { + "scan_iter", + "hscan_iter", + "sscan_iter", + "zscan_iter", +} + _redis_client_methods = _redis_client_sync_methods.union(_redis_client_async_methods) _redis_multipart_commands = set(["client", "cluster", "command", "config", "debug", "sentinel", "slowlog", "script"]) @@ -498,50 +506,31 @@ def _instance_info(kwargs): def _wrap_Redis_method_wrapper_(module, instance_class_name, operation): - def _nr_wrapper_Redis_method_(wrapped, instance, args, kwargs): - transaction = current_transaction() - - if transaction is None: - return wrapped(*args, **kwargs) - - dt = DatastoreTrace(product="Redis", target=None, operation=operation, source=wrapped) - - transaction._nr_datastore_instance_info = (None, None, None) - - with dt: - result = wrapped(*args, **kwargs) - - host, port_path_or_id, db = transaction._nr_datastore_instance_info - dt.host = host - dt.port_path_or_id = port_path_or_id - dt.database_name = db - - return result - name = "%s.%s" % (instance_class_name, operation) - wrap_function_wrapper(module, name, _nr_wrapper_Redis_method_) + if operation in _redis_client_gen_methods: + async_wrapper = generator_wrapper + else: + async_wrapper = None + wrap_datastore_trace(module, name, product="Redis", target=None, operation=operation, async_wrapper=async_wrapper) -def _wrap_asyncio_Redis_method_wrapper(module, instance_class_name, operation): - @function_wrapper - async def _nr_wrapper_asyncio_Redis_async_method_(wrapped, instance, args, kwargs): - transaction = current_transaction() - if transaction is None: - return await wrapped(*args, **kwargs) - - with DatastoreTrace(product="Redis", target=None, operation=operation): - return await wrapped(*args, **kwargs) +def _wrap_asyncio_Redis_method_wrapper(module, instance_class_name, operation): def _nr_wrapper_asyncio_Redis_method_(wrapped, instance, args, kwargs): from redis.asyncio.client import Pipeline if isinstance(instance, Pipeline): return wrapped(*args, **kwargs) - # Method should be run when awaited, therefore we wrap in an async wrapper. - return _nr_wrapper_asyncio_Redis_async_method_(wrapped)(*args, **kwargs) + # Method should be run when awaited or iterated, therefore we wrap in an async wrapper. + return DatastoreTraceWrapper(wrapped, product="Redis", target=None, operation=operation, async_wrapper=async_wrapper)(*args, **kwargs) name = "%s.%s" % (instance_class_name, operation) + if operation in _redis_client_gen_methods: + async_wrapper = async_generator_wrapper + else: + async_wrapper = coroutine_wrapper + wrap_function_wrapper(module, name, _nr_wrapper_asyncio_Redis_method_) @@ -614,7 +603,15 @@ def _nr_Connection_send_command_wrapper_(wrapped, instance, args, kwargs): except: pass - transaction._nr_datastore_instance_info = (host, port_path_or_id, db) + # Find DatastoreTrace no matter how many other traces are inbetween + trace = current_trace() + while trace is not None and not isinstance(trace, DatastoreTrace): + trace = getattr(trace, "parent", None) + + if trace is not None: + trace.host = host + trace.port_path_or_id = port_path_or_id + trace.database_name = db # Older Redis clients would when sending multi part commands pass # them in as separate arguments to send_command(). Need to therefore @@ -666,7 +663,6 @@ def instrument_asyncio_redis_client(module): if hasattr(class_, operation): _wrap_asyncio_Redis_method_wrapper(module, "Redis", operation) - def instrument_redis_commands_core(module): _instrument_redis_commands_module(module, "CoreCommands") diff --git a/newrelic/hooks/external_botocore.py b/newrelic/hooks/external_botocore.py index 7d49fbd031..ba4fd34f73 100644 --- a/newrelic/hooks/external_botocore.py +++ b/newrelic/hooks/external_botocore.py @@ -12,15 +12,34 @@ # See the License for the specific language governing permissions and # limitations under the License. -from newrelic.api.message_trace import message_trace +import json +import logging +import uuid +from io import BytesIO + +from botocore.response import StreamingBody + from newrelic.api.datastore_trace import datastore_trace from newrelic.api.external_trace import ExternalTrace -from newrelic.common.object_wrapper import wrap_function_wrapper +from newrelic.api.function_trace import FunctionTrace +from newrelic.api.message_trace import message_trace +from newrelic.api.time_trace import get_trace_linking_metadata +from newrelic.api.transaction import current_transaction +from newrelic.common.object_names import callable_name +from newrelic.common.object_wrapper import function_wrapper, wrap_function_wrapper +from newrelic.common.package_version_utils import get_package_version +from newrelic.core.config import global_settings + +BOTOCORE_VERSION = get_package_version("botocore") + + +_logger = logging.getLogger(__name__) +UNSUPPORTED_MODEL_WARNING_SENT = False def extract_sqs(*args, **kwargs): - queue_value = kwargs.get('QueueUrl', 'Unknown') - return queue_value.rsplit('/', 1)[-1] + queue_value = kwargs.get("QueueUrl", "Unknown") + return queue_value.rsplit("/", 1)[-1] def extract(argument_names, default=None): @@ -40,43 +59,399 @@ def extractor_string(*args, **kwargs): return extractor_list +def bedrock_error_attributes(exception, request_args, client, extractor): + response = getattr(exception, "response", None) + if not response: + return {} + + request_body = request_args.get("body", "") + error_attributes = extractor(request_body)[1] + + error_attributes.update( + { + "request_id": response.get("ResponseMetadata", {}).get("RequestId", ""), + "api_key_last_four_digits": client._request_signer._credentials.access_key[-4:], + "request.model": request_args.get("modelId", ""), + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": response.get("ResponseMetadata", "").get("HTTPStatusCode", ""), + "error.message": response.get("Error", "").get("Message", ""), + "error.code": response.get("Error", "").get("Code", ""), + } + ) + return error_attributes + + +def create_chat_completion_message_event( + transaction, + app_name, + message_list, + chat_completion_id, + span_id, + trace_id, + request_model, + request_id, + conversation_id, + response_id="", +): + if not transaction: + return + + for index, message in enumerate(message_list): + if response_id: + id_ = "%s-%d" % (response_id, index) # Response ID was set, append message index to it. + else: + id_ = str(uuid.uuid4()) # No response IDs, use random UUID + + chat_completion_message_dict = { + "id": id_, + "appName": app_name, + "conversation_id": conversation_id, + "request_id": request_id, + "span_id": span_id, + "trace_id": trace_id, + "transaction_id": transaction._transaction_id, + "content": message.get("content", ""), + "role": message.get("role"), + "completion_id": chat_completion_id, + "sequence": index, + "response.model": request_model, + "vendor": "bedrock", + "ingest_source": "Python", + } + transaction.record_ml_event("LlmChatCompletionMessage", chat_completion_message_dict) + + +def extract_bedrock_titan_text_model(request_body, response_body=None): + request_body = json.loads(request_body) + if response_body: + response_body = json.loads(response_body) + + request_config = request_body.get("textGenerationConfig", {}) + + chat_completion_summary_dict = { + "request.max_tokens": request_config.get("maxTokenCount", ""), + "request.temperature": request_config.get("temperature", ""), + } + + if response_body: + input_tokens = response_body["inputTextTokenCount"] + completion_tokens = sum(result["tokenCount"] for result in response_body.get("results", [])) + total_tokens = input_tokens + completion_tokens + + message_list = [{"role": "user", "content": request_body.get("inputText", "")}] + message_list.extend( + {"role": "assistant", "content": result["outputText"]} for result in response_body.get("results", []) + ) + + chat_completion_summary_dict.update( + { + "response.choices.finish_reason": response_body["results"][0]["completionReason"], + "response.usage.completion_tokens": completion_tokens, + "response.usage.prompt_tokens": input_tokens, + "response.usage.total_tokens": total_tokens, + "response.number_of_messages": len(message_list), + } + ) + else: + message_list = [] + + return message_list, chat_completion_summary_dict + + +def extract_bedrock_titan_embedding_model(request_body, response_body=None): + if not response_body: + return [], {} # No extracted information necessary for embedding + + request_body = json.loads(request_body) + response_body = json.loads(response_body) + + input_tokens = response_body.get("inputTextTokenCount", None) + + embedding_dict = { + "input": request_body.get("inputText", ""), + "response.usage.prompt_tokens": input_tokens, + "response.usage.total_tokens": input_tokens, + } + return [], embedding_dict + + +def extract_bedrock_ai21_j2_model(request_body, response_body=None): + request_body = json.loads(request_body) + if response_body: + response_body = json.loads(response_body) + + chat_completion_summary_dict = { + "request.max_tokens": request_body.get("maxTokens", ""), + "request.temperature": request_body.get("temperature", ""), + } + + if response_body: + message_list = [{"role": "user", "content": request_body.get("prompt", "")}] + message_list.extend( + {"role": "assistant", "content": result["data"]["text"]} for result in response_body.get("completions", []) + ) + + chat_completion_summary_dict.update( + { + "response.choices.finish_reason": response_body["completions"][0]["finishReason"]["reason"], + "response.number_of_messages": len(message_list), + "response_id": str(response_body.get("id", "")), + } + ) + else: + message_list = [] + + return message_list, chat_completion_summary_dict + + +def extract_bedrock_claude_model(request_body, response_body=None): + request_body = json.loads(request_body) + if response_body: + response_body = json.loads(response_body) + + chat_completion_summary_dict = { + "request.max_tokens": request_body.get("max_tokens_to_sample", ""), + "request.temperature": request_body.get("temperature", ""), + } + + if response_body: + message_list = [ + {"role": "user", "content": request_body.get("prompt", "")}, + {"role": "assistant", "content": response_body.get("completion", "")}, + ] + + chat_completion_summary_dict.update( + { + "response.choices.finish_reason": response_body.get("stop_reason", ""), + "response.number_of_messages": len(message_list), + } + ) + else: + message_list = [] + + return message_list, chat_completion_summary_dict + + +def extract_bedrock_cohere_model(request_body, response_body=None): + request_body = json.loads(request_body) + if response_body: + response_body = json.loads(response_body) + + chat_completion_summary_dict = { + "request.max_tokens": request_body.get("max_tokens", ""), + "request.temperature": request_body.get("temperature", ""), + } + + if response_body: + message_list = [{"role": "user", "content": request_body.get("prompt", "")}] + message_list.extend( + {"role": "assistant", "content": result["text"]} for result in response_body.get("generations", []) + ) + + chat_completion_summary_dict.update( + { + "request.max_tokens": request_body.get("max_tokens", ""), + "request.temperature": request_body.get("temperature", ""), + "response.choices.finish_reason": response_body["generations"][0]["finish_reason"], + "response.number_of_messages": len(message_list), + "response_id": str(response_body.get("id", "")), + } + ) + else: + message_list = [] + + return message_list, chat_completion_summary_dict + + +MODEL_EXTRACTORS = [ # Order is important here, avoiding dictionaries + ("amazon.titan-embed", extract_bedrock_titan_embedding_model), + ("amazon.titan", extract_bedrock_titan_text_model), + ("ai21.j2", extract_bedrock_ai21_j2_model), + ("cohere", extract_bedrock_cohere_model), + ("anthropic.claude", extract_bedrock_claude_model), +] + + +@function_wrapper +def wrap_bedrock_runtime_invoke_model(wrapped, instance, args, kwargs): + # Wrapped function only takes keyword arguments, no need for binding + + transaction = current_transaction() + + if not transaction: + return wrapped(*args, **kwargs) + + transaction.add_ml_model_info("Botocore", BOTOCORE_VERSION) + + # Read and replace request file stream bodies + request_body = kwargs["body"] + if hasattr(request_body, "read"): + request_body = request_body.read() + kwargs["body"] = request_body + + # Determine model to be used with extractor + model = kwargs.get("modelId") + if not model: + return wrapped(*args, **kwargs) + + # Determine extractor by model type + for extractor_name, extractor in MODEL_EXTRACTORS: + if model.startswith(extractor_name): + break + else: + # Model was not found in extractor list + global UNSUPPORTED_MODEL_WARNING_SENT + if not UNSUPPORTED_MODEL_WARNING_SENT: + # Only send warning once to avoid spam + _logger.warning( + "Unsupported Amazon Bedrock model in use (%s). Upgrade to a newer version of the agent, and contact New Relic support if the issue persists.", + model, + ) + UNSUPPORTED_MODEL_WARNING_SENT = True + + extractor = lambda *args: ([], {}) # Empty extractor that returns nothing + + ft_name = callable_name(wrapped) + with FunctionTrace(ft_name) as ft: + try: + response = wrapped(*args, **kwargs) + except Exception as exc: + try: + error_attributes = extractor(request_body) + error_attributes = bedrock_error_attributes(exc, kwargs, instance, extractor) + ft.notice_error( + attributes=error_attributes, + ) + finally: + raise + + if not response: + return response + + # Read and replace response streaming bodies + response_body = response["body"].read() + response["body"] = StreamingBody(BytesIO(response_body), len(response_body)) + response_headers = response["ResponseMetadata"]["HTTPHeaders"] + + if model.startswith("amazon.titan-embed"): # Only available embedding models + handle_embedding_event( + instance, transaction, extractor, model, response_body, response_headers, request_body, ft.duration + ) + else: + handle_chat_completion_event( + instance, transaction, extractor, model, response_body, response_headers, request_body, ft.duration + ) + + return response + + +def handle_embedding_event( + client, transaction, extractor, model, response_body, response_headers, request_body, duration +): + embedding_id = str(uuid.uuid4()) + available_metadata = get_trace_linking_metadata() + span_id = available_metadata.get("span.id", "") + trace_id = available_metadata.get("trace.id", "") + + request_id = response_headers.get("x-amzn-requestid", "") + settings = transaction.settings if transaction.settings is not None else global_settings() + + _, embedding_dict = extractor(request_body, response_body) + + embedding_dict.update( + { + "vendor": "bedrock", + "ingest_source": "Python", + "id": embedding_id, + "appName": settings.app_name, + "span_id": span_id, + "trace_id": trace_id, + "request_id": request_id, + "transaction_id": transaction._transaction_id, + "api_key_last_four_digits": client._request_signer._credentials.access_key[-4:], + "duration": duration, + "request.model": model, + "response.model": model, + } + ) + + transaction.record_ml_event("LlmEmbedding", embedding_dict) + + +def handle_chat_completion_event( + client, transaction, extractor, model, response_body, response_headers, request_body, duration +): + custom_attrs_dict = transaction._custom_params + conversation_id = custom_attrs_dict.get("conversation_id", "") + + chat_completion_id = str(uuid.uuid4()) + available_metadata = get_trace_linking_metadata() + span_id = available_metadata.get("span.id", "") + trace_id = available_metadata.get("trace.id", "") + + request_id = response_headers.get("x-amzn-requestid", "") + settings = transaction.settings if transaction.settings is not None else global_settings() + + message_list, chat_completion_summary_dict = extractor(request_body, response_body) + response_id = chat_completion_summary_dict.get("response_id", "") + chat_completion_summary_dict.update( + { + "vendor": "bedrock", + "ingest_source": "Python", + "api_key_last_four_digits": client._request_signer._credentials.access_key[-4:], + "id": chat_completion_id, + "appName": settings.app_name, + "conversation_id": conversation_id, + "span_id": span_id, + "trace_id": trace_id, + "transaction_id": transaction._transaction_id, + "request_id": request_id, + "duration": duration, + "request.model": model, + "response.model": model, # Duplicate data required by the UI + } + ) + + transaction.record_ml_event("LlmChatCompletionSummary", chat_completion_summary_dict) + + create_chat_completion_message_event( + transaction=transaction, + app_name=settings.app_name, + message_list=message_list, + chat_completion_id=chat_completion_id, + span_id=span_id, + trace_id=trace_id, + request_model=model, + request_id=request_id, + conversation_id=conversation_id, + response_id=response_id, + ) + + CUSTOM_TRACE_POINTS = { - ('sns', 'publish'): message_trace( - 'SNS', 'Produce', 'Topic', - extract(('TopicArn', 'TargetArn'), 'PhoneNumber')), - ('dynamodb', 'put_item'): datastore_trace( - 'DynamoDB', extract('TableName'), 'put_item'), - ('dynamodb', 'get_item'): datastore_trace( - 'DynamoDB', extract('TableName'), 'get_item'), - ('dynamodb', 'update_item'): datastore_trace( - 'DynamoDB', extract('TableName'), 'update_item'), - ('dynamodb', 'delete_item'): datastore_trace( - 'DynamoDB', extract('TableName'), 'delete_item'), - ('dynamodb', 'create_table'): datastore_trace( - 'DynamoDB', extract('TableName'), 'create_table'), - ('dynamodb', 'delete_table'): datastore_trace( - 'DynamoDB', extract('TableName'), 'delete_table'), - ('dynamodb', 'query'): datastore_trace( - 'DynamoDB', extract('TableName'), 'query'), - ('dynamodb', 'scan'): datastore_trace( - 'DynamoDB', extract('TableName'), 'scan'), - ('sqs', 'send_message'): message_trace( - 'SQS', 'Produce', 'Queue', extract_sqs), - ('sqs', 'send_message_batch'): message_trace( - 'SQS', 'Produce', 'Queue', extract_sqs), - ('sqs', 'receive_message'): message_trace( - 'SQS', 'Consume', 'Queue', extract_sqs), + ("sns", "publish"): message_trace("SNS", "Produce", "Topic", extract(("TopicArn", "TargetArn"), "PhoneNumber")), + ("dynamodb", "put_item"): datastore_trace("DynamoDB", extract("TableName"), "put_item"), + ("dynamodb", "get_item"): datastore_trace("DynamoDB", extract("TableName"), "get_item"), + ("dynamodb", "update_item"): datastore_trace("DynamoDB", extract("TableName"), "update_item"), + ("dynamodb", "delete_item"): datastore_trace("DynamoDB", extract("TableName"), "delete_item"), + ("dynamodb", "create_table"): datastore_trace("DynamoDB", extract("TableName"), "create_table"), + ("dynamodb", "delete_table"): datastore_trace("DynamoDB", extract("TableName"), "delete_table"), + ("dynamodb", "query"): datastore_trace("DynamoDB", extract("TableName"), "query"), + ("dynamodb", "scan"): datastore_trace("DynamoDB", extract("TableName"), "scan"), + ("sqs", "send_message"): message_trace("SQS", "Produce", "Queue", extract_sqs), + ("sqs", "send_message_batch"): message_trace("SQS", "Produce", "Queue", extract_sqs), + ("sqs", "receive_message"): message_trace("SQS", "Consume", "Queue", extract_sqs), + ("bedrock-runtime", "invoke_model"): wrap_bedrock_runtime_invoke_model, } -def bind__create_api_method(py_operation_name, operation_name, service_model, - *args, **kwargs): +def bind__create_api_method(py_operation_name, operation_name, service_model, *args, **kwargs): return (py_operation_name, service_model) def _nr_clientcreator__create_api_method_(wrapped, instance, args, kwargs): - (py_operation_name, service_model) = \ - bind__create_api_method(*args, **kwargs) + (py_operation_name, service_model) = bind__create_api_method(*args, **kwargs) service_name = service_model.service_name.lower() tracer = CUSTOM_TRACE_POINTS.get((service_name, py_operation_name)) @@ -95,30 +470,27 @@ def _bind_make_request_params(operation_model, request_dict, *args, **kwargs): def _nr_endpoint_make_request_(wrapped, instance, args, kwargs): operation_model, request_dict = _bind_make_request_params(*args, **kwargs) - url = request_dict.get('url', '') - method = request_dict.get('method', None) - - with ExternalTrace(library='botocore', url=url, method=method, source=wrapped) as trace: + url = request_dict.get("url", "") + method = request_dict.get("method", None) + with ExternalTrace(library="botocore", url=url, method=method, source=wrapped) as trace: try: - trace._add_agent_attribute('aws.operation', operation_model.name) + trace._add_agent_attribute("aws.operation", operation_model.name) except: pass result = wrapped(*args, **kwargs) try: - request_id = result[1]['ResponseMetadata']['RequestId'] - trace._add_agent_attribute('aws.requestId', request_id) + request_id = result[1]["ResponseMetadata"]["RequestId"] + trace._add_agent_attribute("aws.requestId", request_id) except: pass return result def instrument_botocore_endpoint(module): - wrap_function_wrapper(module, 'Endpoint.make_request', - _nr_endpoint_make_request_) + wrap_function_wrapper(module, "Endpoint.make_request", _nr_endpoint_make_request_) def instrument_botocore_client(module): - wrap_function_wrapper(module, 'ClientCreator._create_api_method', - _nr_clientcreator__create_api_method_) + wrap_function_wrapper(module, "ClientCreator._create_api_method", _nr_clientcreator__create_api_method_) diff --git a/tests/agent_unittests/test_package_version_utils.py b/tests/agent_unittests/test_package_version_utils.py index 30c22cff18..5ed689ea2a 100644 --- a/tests/agent_unittests/test_package_version_utils.py +++ b/tests/agent_unittests/test_package_version_utils.py @@ -20,6 +20,7 @@ from newrelic.common.package_version_utils import ( NULL_VERSIONS, VERSION_ATTRS, + _get_package_version, get_package_version, get_package_version_tuple, ) @@ -31,7 +32,7 @@ # such as distribution_packages and removed pkg_resources. IS_PY38_PLUS = sys.version_info[:2] >= (3, 8) -IS_PY310_PLUS = sys.version_info[:2] >= (3,10) +IS_PY310_PLUS = sys.version_info[:2] >= (3, 10) SKIP_IF_NOT_IMPORTLIB_METADATA = pytest.mark.skipif(not IS_PY38_PLUS, reason="importlib.metadata is not supported.") SKIP_IF_IMPORTLIB_METADATA = pytest.mark.skipif( IS_PY38_PLUS, reason="importlib.metadata is preferred over pkg_resources." @@ -46,7 +47,13 @@ def patched_pytest_module(monkeypatch): monkeypatch.delattr(pytest, attr) yield pytest - + + +@pytest.fixture(scope="function", autouse=True) +def cleared_package_version_cache(): + """Ensure cache is empty before every test to exercise code paths.""" + _get_package_version.cache_clear() + # This test only works on Python 3.7 @SKIP_IF_IMPORTLIB_METADATA @@ -123,3 +130,16 @@ def test_mapping_import_to_distribution_packages(): def test_pkg_resources_metadata(): version = get_package_version("pytest") assert version not in NULL_VERSIONS, version + + +def test_version_caching(monkeypatch): + # Add fake module to be deleted later + sys.modules["mymodule"] = sys.modules["pytest"] + setattr(pytest, "__version__", "1.0.0") + version = get_package_version("mymodule") + assert version not in NULL_VERSIONS, version + + # Ensure after deleting that the call to _get_package_version still completes because of caching + del sys.modules["mymodule"] + version = get_package_version("mymodule") + assert version not in NULL_VERSIONS, version diff --git a/tests/datastore_redis/conftest.py b/tests/datastore_redis/conftest.py index 53ff2658de..6747039b47 100644 --- a/tests/datastore_redis/conftest.py +++ b/tests/datastore_redis/conftest.py @@ -15,6 +15,7 @@ import pytest from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611 +from testing_support.fixture.event_loop import event_loop as loop # noqa: F401; pylint: disable=W0611 _default_settings = { diff --git a/tests/datastore_redis/test_generators.py b/tests/datastore_redis/test_generators.py new file mode 100644 index 0000000000..f747838e19 --- /dev/null +++ b/tests/datastore_redis/test_generators.py @@ -0,0 +1,258 @@ +# 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 pytest +import redis +from testing_support.db_settings import redis_settings +from testing_support.fixtures import override_application_settings +from testing_support.util import instance_hostname +from testing_support.validators.validate_transaction_metrics import ( + validate_transaction_metrics, +) + +from newrelic.api.background_task import background_task +from newrelic.api.datastore_trace import DatastoreTrace +from newrelic.api.time_trace import current_trace +from newrelic.common.package_version_utils import get_package_version_tuple + +DB_SETTINGS = redis_settings()[0] +REDIS_PY_VERSION = get_package_version_tuple("redis") + +# Settings + +_enable_instance_settings = { + "datastore_tracer.instance_reporting.enabled": True, +} +_disable_instance_settings = { + "datastore_tracer.instance_reporting.enabled": False, +} + +# Metrics + +_base_scoped_metrics = ( + ("Datastore/operation/Redis/scan_iter", 1), + ("Datastore/operation/Redis/sscan_iter", 1), + ("Datastore/operation/Redis/zscan_iter", 1), + ("Datastore/operation/Redis/hscan_iter", 1), + ("Datastore/operation/Redis/set", 1), + ("Datastore/operation/Redis/sadd", 1), + ("Datastore/operation/Redis/zadd", 1), + ("Datastore/operation/Redis/hset", 1), +) + +_base_rollup_metrics = ( + ("Datastore/all", 8), + ("Datastore/allOther", 8), + ("Datastore/Redis/all", 8), + ("Datastore/Redis/allOther", 8), + ("Datastore/operation/Redis/scan_iter", 1), + ("Datastore/operation/Redis/sscan_iter", 1), + ("Datastore/operation/Redis/zscan_iter", 1), + ("Datastore/operation/Redis/hscan_iter", 1), + ("Datastore/operation/Redis/set", 1), + ("Datastore/operation/Redis/sadd", 1), + ("Datastore/operation/Redis/zadd", 1), + ("Datastore/operation/Redis/hset", 1), +) + +_disable_rollup_metrics = list(_base_rollup_metrics) +_enable_rollup_metrics = list(_base_rollup_metrics) + +_host = instance_hostname(DB_SETTINGS["host"]) +_port = DB_SETTINGS["port"] + +_instance_metric_name = "Datastore/instance/Redis/%s/%s" % (_host, _port) + +_enable_rollup_metrics.append((_instance_metric_name, 8)) + +_disable_rollup_metrics.append((_instance_metric_name, None)) + +# Operations + + +def exercise_redis(client): + """ + Exercise client generators by iterating on various methods and ensuring they are + non-empty, and that traces are started and stopped with the generator. + """ + + # Set existing values + client.set("scan-key", "value") + client.sadd("sscan-key", "value") + client.zadd("zscan-key", {"value": 1}) + client.hset("hscan-key", "field", "value") + + # Check generators + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + for k in client.scan_iter("scan-*"): + assert k == b"scan-key" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + for k in client.sscan_iter("sscan-key"): + assert k == b"value" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + for k, _ in client.zscan_iter("zscan-key"): + assert k == b"value" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + for f, v in client.hscan_iter("hscan-key"): + assert f == b"field" + assert v == b"value" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + +async def exercise_redis_async(client): + """ + Exercise client generators by iterating on various methods and ensuring they are + non-empty, and that traces are started and stopped with the generator. + """ + + # Set existing values + await client.set("scan-key", "value") + await client.sadd("sscan-key", "value") + await client.zadd("zscan-key", {"value": 1}) + await client.hset("hscan-key", "field", "value") + + # Check generators + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + async for k in client.scan_iter("scan-*"): + assert k == b"scan-key" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + async for k in client.sscan_iter("sscan-key"): + assert k == b"value" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + async for k, _ in client.zscan_iter("zscan-key"): + assert k == b"value" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + flag = False + assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace + async for f, v in client.hscan_iter("hscan-key"): + assert f == b"field" + assert v == b"value" + assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active + flag = True + assert flag + + +# Tests + + +@override_application_settings(_enable_instance_settings) +@validate_transaction_metrics( + "test_generators:test_strict_redis_generator_enable_instance", + scoped_metrics=_base_scoped_metrics, + rollup_metrics=_enable_rollup_metrics, + background_task=True, +) +@background_task() +def test_strict_redis_generator_enable_instance(): + client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) + exercise_redis(client) + + +@override_application_settings(_disable_instance_settings) +@validate_transaction_metrics( + "test_generators:test_strict_redis_generator_disable_instance", + scoped_metrics=_base_scoped_metrics, + rollup_metrics=_disable_rollup_metrics, + background_task=True, +) +@background_task() +def test_strict_redis_generator_disable_instance(): + client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) + exercise_redis(client) + + +@override_application_settings(_enable_instance_settings) +@validate_transaction_metrics( + "test_generators:test_redis_generator_enable_instance", + scoped_metrics=_base_scoped_metrics, + rollup_metrics=_enable_rollup_metrics, + background_task=True, +) +@background_task() +def test_redis_generator_enable_instance(): + client = redis.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) + exercise_redis(client) + + +@override_application_settings(_disable_instance_settings) +@validate_transaction_metrics( + "test_generators:test_redis_generator_disable_instance", + scoped_metrics=_base_scoped_metrics, + rollup_metrics=_disable_rollup_metrics, + background_task=True, +) +@background_task() +def test_redis_generator_disable_instance(): + client = redis.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) + exercise_redis(client) + + +@pytest.mark.skipif(REDIS_PY_VERSION < (4, 2), reason="Redis.asyncio was not added until v4.2") +@override_application_settings(_enable_instance_settings) +@validate_transaction_metrics( + "test_generators:test_redis_async_generator_enable_instance", + scoped_metrics=_base_scoped_metrics, + rollup_metrics=_enable_rollup_metrics, + background_task=True, +) +@background_task() +def test_redis_async_generator_enable_instance(loop): + client = redis.asyncio.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) + loop.run_until_complete(exercise_redis_async(client)) + + +@pytest.mark.skipif(REDIS_PY_VERSION < (4, 2), reason="Redis.asyncio was not added until v4.2") +@override_application_settings(_disable_instance_settings) +@validate_transaction_metrics( + "test_generators:test_redis_async_generator_disable_instance", + scoped_metrics=_base_scoped_metrics, + rollup_metrics=_disable_rollup_metrics, + background_task=True, +) +@background_task() +def test_redis_async_generator_disable_instance(loop): + client = redis.asyncio.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) + loop.run_until_complete(exercise_redis_async(client)) diff --git a/tests/datastore_redis/test_uninstrumented_methods.py b/tests/datastore_redis/test_uninstrumented_methods.py index d86f4de955..c0be684b2f 100644 --- a/tests/datastore_redis/test_uninstrumented_methods.py +++ b/tests/datastore_redis/test_uninstrumented_methods.py @@ -65,7 +65,6 @@ "get_property", "get_relation", "get_retry", - "hscan_iter", "index_name", "labels", "list_keys", diff --git a/tests/external_boto3/conftest.py b/tests/external_boto3/conftest.py deleted file mode 100644 index 90d82f0072..0000000000 --- a/tests/external_boto3/conftest.py +++ /dev/null @@ -1,30 +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 pytest - -from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611 - - -_default_settings = { - 'transaction_tracer.explain_threshold': 0.0, - 'transaction_tracer.transaction_threshold': 0.0, - 'transaction_tracer.stack_trace_threshold': 0.0, - 'debug.log_data_collector_payloads': True, - 'debug.record_transaction_failure': True, -} - -collector_agent_registration = collector_agent_registration_fixture( - app_name='Python Agent Test (external_boto3)', - default_settings=_default_settings) diff --git a/tests/external_botocore/_mock_external_bedrock_server.py b/tests/external_botocore/_mock_external_bedrock_server.py new file mode 100644 index 0000000000..da5ff68dd9 --- /dev/null +++ b/tests/external_botocore/_mock_external_bedrock_server.py @@ -0,0 +1,3461 @@ +# 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 re + +from testing_support.mock_external_http_server import MockExternalHTTPServer + +# This defines an external server test apps can make requests to instead of +# the real Bedrock 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. + +RESPONSES = { + "ai21.j2-mid-v1::What is 212 degrees Fahrenheit converted to Celsius?": [ + {"Content-Type": "application/json", "x-amzn-RequestId": "c863d9fc-888b-421c-a175-ac5256baec62"}, + 200, + { + "id": 1234, + "prompt": { + "text": "What is 212 degrees Fahrenheit converted to Celsius?", + "tokens": [ + { + "generatedToken": { + "token": "▁What▁is", + "logprob": -7.446773529052734, + "raw_logprob": -7.446773529052734, + }, + "topTokens": None, + "textRange": {"start": 0, "end": 7}, + }, + { + "generatedToken": { + "token": "▁", + "logprob": -3.8046724796295166, + "raw_logprob": -3.8046724796295166, + }, + "topTokens": None, + "textRange": {"start": 7, "end": 8}, + }, + { + "generatedToken": { + "token": "212", + "logprob": -9.287349700927734, + "raw_logprob": -9.287349700927734, + }, + "topTokens": None, + "textRange": {"start": 8, "end": 11}, + }, + { + "generatedToken": { + "token": "▁degrees▁Fahrenheit", + "logprob": -7.953181743621826, + "raw_logprob": -7.953181743621826, + }, + "topTokens": None, + "textRange": {"start": 11, "end": 30}, + }, + { + "generatedToken": { + "token": "▁converted▁to", + "logprob": -6.168096542358398, + "raw_logprob": -6.168096542358398, + }, + "topTokens": None, + "textRange": {"start": 30, "end": 43}, + }, + { + "generatedToken": { + "token": "▁Celsius", + "logprob": -0.09790332615375519, + "raw_logprob": -0.09790332615375519, + }, + "topTokens": None, + "textRange": {"start": 43, "end": 51}, + }, + { + "generatedToken": { + "token": "?", + "logprob": -6.5795369148254395, + "raw_logprob": -6.5795369148254395, + }, + "topTokens": None, + "textRange": {"start": 51, "end": 52}, + }, + ], + }, + "completions": [ + { + "data": { + "text": "\n212 degrees Fahrenheit is equal to 100 degrees Celsius.", + "tokens": [ + { + "generatedToken": { + "token": "<|newline|>", + "logprob": -1.6689286894688848e-06, + "raw_logprob": -0.00015984688070602715, + }, + "topTokens": None, + "textRange": {"start": 0, "end": 1}, + }, + { + "generatedToken": { + "token": "▁", + "logprob": -0.03473362699151039, + "raw_logprob": -0.11261807382106781, + }, + "topTokens": None, + "textRange": {"start": 1, "end": 1}, + }, + { + "generatedToken": { + "token": "212", + "logprob": -0.003316262038424611, + "raw_logprob": -0.019686665385961533, + }, + "topTokens": None, + "textRange": {"start": 1, "end": 4}, + }, + { + "generatedToken": { + "token": "▁degrees▁Fahrenheit", + "logprob": -0.003579758107662201, + "raw_logprob": -0.03144374489784241, + }, + "topTokens": None, + "textRange": {"start": 4, "end": 23}, + }, + { + "generatedToken": { + "token": "▁is▁equal▁to", + "logprob": -0.0027733694296330214, + "raw_logprob": -0.027207009494304657, + }, + "topTokens": None, + "textRange": {"start": 23, "end": 35}, + }, + { + "generatedToken": { + "token": "▁", + "logprob": -0.0003392120997887105, + "raw_logprob": -0.005458095110952854, + }, + "topTokens": None, + "textRange": {"start": 35, "end": 36}, + }, + { + "generatedToken": { + "token": "100", + "logprob": -2.145764938177308e-06, + "raw_logprob": -0.00012730741582345217, + }, + "topTokens": None, + "textRange": {"start": 36, "end": 39}, + }, + { + "generatedToken": { + "token": "▁degrees▁Celsius", + "logprob": -0.31207239627838135, + "raw_logprob": -0.402545303106308, + }, + "topTokens": None, + "textRange": {"start": 39, "end": 55}, + }, + { + "generatedToken": { + "token": ".", + "logprob": -0.023684674873948097, + "raw_logprob": -0.0769972875714302, + }, + "topTokens": None, + "textRange": {"start": 55, "end": 56}, + }, + { + "generatedToken": { + "token": "<|endoftext|>", + "logprob": -0.0073706600815057755, + "raw_logprob": -0.06265579164028168, + }, + "topTokens": None, + "textRange": {"start": 56, "end": 56}, + }, + ], + }, + "finishReason": {"reason": "endoftext"}, + } + ], + }, + ], + "amazon.titan-embed-g1-text-02::This is an embedding test.": [ + {"Content-Type": "application/json", "x-amzn-RequestId": "b10ac895-eae3-4f07-b926-10b2866c55ed"}, + 200, + { + "embedding": [ + -0.14160156, + 0.034423828, + 0.54296875, + 0.10986328, + 0.053466797, + 0.3515625, + 0.12988281, + -0.0002708435, + -0.21484375, + 0.060302734, + 0.58984375, + -0.5859375, + 0.52734375, + 0.82421875, + -0.91015625, + -0.19628906, + 0.45703125, + 0.609375, + -0.67578125, + 0.39453125, + -0.46875, + -0.25390625, + -0.21191406, + 0.114746094, + 0.31640625, + -0.41015625, + -0.32617188, + -0.43554688, + 0.4765625, + -0.4921875, + 0.40429688, + 0.06542969, + 0.859375, + -0.008056641, + -0.19921875, + 0.072753906, + 0.33203125, + 0.69921875, + 0.39453125, + 0.15527344, + 0.08886719, + -0.25, + 0.859375, + 0.22949219, + -0.19042969, + 0.13769531, + -0.078125, + 0.41210938, + 0.875, + 0.5234375, + 0.59765625, + -0.22949219, + -0.22558594, + -0.47460938, + 0.37695312, + 0.51953125, + -0.5703125, + 0.46679688, + 0.43554688, + 0.17480469, + -0.080566406, + -0.16699219, + -0.734375, + -1.0625, + -0.33984375, + 0.390625, + -0.18847656, + -0.5234375, + -0.48828125, + 0.44921875, + -0.09814453, + -0.3359375, + 0.087402344, + 0.36914062, + 1.3203125, + 0.25585938, + 0.14746094, + -0.059570312, + -0.15820312, + -0.037353516, + -0.61328125, + -0.6484375, + -0.35351562, + 0.55078125, + -0.26953125, + 0.90234375, + 0.3671875, + 0.31054688, + 0.00014019012, + -0.171875, + 0.025512695, + 0.5078125, + 0.11621094, + 0.33203125, + 0.8125, + -0.3046875, + -1.078125, + -0.5703125, + 0.26171875, + -0.4609375, + 0.203125, + 0.44726562, + -0.5078125, + 0.41601562, + -0.1953125, + 0.028930664, + -0.57421875, + 0.2265625, + 0.13574219, + -0.040039062, + -0.22949219, + -0.515625, + -0.19042969, + -0.30078125, + 0.10058594, + -0.66796875, + 0.6015625, + 0.296875, + -0.765625, + -0.87109375, + 0.2265625, + 0.068847656, + -0.088378906, + -0.1328125, + -0.796875, + -0.37304688, + 0.47460938, + -0.3515625, + -0.8125, + -0.32226562, + 0.265625, + 0.3203125, + -0.4140625, + -0.49023438, + 0.859375, + -0.19140625, + -0.6328125, + 0.10546875, + -0.5625, + 0.66015625, + 0.26171875, + -0.2109375, + 0.421875, + -0.82421875, + 0.29296875, + 0.17773438, + 0.24023438, + 0.5078125, + -0.49804688, + -0.10205078, + 0.10498047, + -0.36132812, + -0.47460938, + -0.20996094, + 0.010070801, + -0.546875, + 0.66796875, + -0.123046875, + -0.75390625, + 0.19628906, + 0.17480469, + 0.18261719, + -0.96875, + -0.26171875, + 0.4921875, + -0.40039062, + 0.296875, + 0.1640625, + -0.20507812, + -0.36132812, + 0.76171875, + -1.234375, + -0.625, + 0.060058594, + -0.09375, + -0.14746094, + 1.09375, + 0.057861328, + 0.22460938, + -0.703125, + 0.07470703, + 0.23828125, + -0.083984375, + -0.54296875, + 0.5546875, + -0.5, + -0.390625, + 0.106933594, + 0.6640625, + 0.27734375, + -0.953125, + 0.35351562, + -0.7734375, + -0.77734375, + 0.16503906, + -0.42382812, + 0.36914062, + 0.020141602, + -1.3515625, + 0.18847656, + 0.13476562, + -0.034179688, + -0.03930664, + -0.03857422, + -0.027954102, + 0.73828125, + -0.18945312, + -0.09814453, + -0.46289062, + 0.36914062, + 0.033203125, + 0.020874023, + -0.703125, + 0.91796875, + 0.38671875, + 0.625, + -0.19335938, + -0.16796875, + -0.58203125, + 0.21386719, + -0.032470703, + -0.296875, + -0.15625, + -0.1640625, + -0.74609375, + 0.328125, + 0.5546875, + -0.1953125, + 1.0546875, + 0.171875, + -0.099609375, + 0.5234375, + 0.05078125, + -0.35742188, + -0.2734375, + -1.3203125, + -0.8515625, + -0.16015625, + 0.01574707, + 0.29296875, + 0.18457031, + -0.265625, + 0.048339844, + 0.045654297, + -0.32226562, + 0.087890625, + -0.0047302246, + 0.38671875, + 0.10644531, + -0.06225586, + 1.03125, + 0.94140625, + -0.3203125, + 0.20800781, + -1.171875, + 0.48046875, + -0.091796875, + 0.20800781, + -0.1328125, + -0.20507812, + 0.28125, + -0.47070312, + -0.09033203, + 0.0013809204, + -0.08203125, + 0.43359375, + -0.03100586, + -0.060791016, + -0.53515625, + -1.46875, + 0.000101566315, + 0.515625, + 0.40625, + -0.10498047, + -0.15820312, + -0.009460449, + -0.77734375, + -0.5859375, + 0.9765625, + 0.099609375, + 0.51953125, + 0.38085938, + -0.09667969, + -0.100097656, + -0.5, + -1.3125, + -0.18066406, + -0.099121094, + 0.26171875, + -0.14453125, + -0.546875, + 0.17578125, + 0.484375, + 0.765625, + 0.45703125, + 0.2734375, + 0.0028076172, + 0.17089844, + -0.32421875, + -0.37695312, + 0.30664062, + -0.48046875, + 0.07128906, + 0.031982422, + -0.31054688, + -0.055419922, + -0.29296875, + 0.3359375, + -0.296875, + 0.47851562, + -0.05126953, + 0.18457031, + -0.01953125, + -0.35742188, + 0.017944336, + -0.25, + 0.10595703, + 0.17382812, + -0.73828125, + 0.36914062, + -0.15234375, + -0.8125, + 0.17382812, + 0.048095703, + 0.5625, + -0.33789062, + 0.023071289, + -0.21972656, + 0.16015625, + 0.032958984, + -1.1171875, + -0.984375, + 0.83984375, + 0.009033203, + -0.042236328, + -0.46484375, + -0.08203125, + 0.44726562, + -0.765625, + -0.3984375, + -0.40820312, + -0.234375, + 0.044189453, + 0.119628906, + -0.7578125, + -0.55078125, + -0.4453125, + 0.7578125, + 0.34960938, + 0.96484375, + 0.35742188, + 0.36914062, + -0.35351562, + -0.36132812, + 1.109375, + 0.5859375, + 0.85546875, + -0.10644531, + -0.6953125, + -0.0066833496, + 0.042236328, + -0.06689453, + 0.36914062, + 0.9765625, + -0.3046875, + 0.59765625, + -0.6640625, + 0.21484375, + -0.07128906, + 1.1328125, + -0.51953125, + 0.86328125, + -0.11328125, + 0.15722656, + -0.36328125, + -0.04638672, + 1.4375, + 0.18457031, + -0.18359375, + 0.10595703, + -0.49023438, + -0.07324219, + -0.73046875, + -0.119140625, + 0.021118164, + 0.4921875, + -0.46875, + 0.28710938, + 0.3359375, + 0.11767578, + -0.2109375, + -0.14550781, + 0.39648438, + -0.27734375, + 0.48046875, + 0.12988281, + 0.45507812, + -0.375, + -0.84765625, + 0.25585938, + -0.36523438, + 0.8046875, + 0.42382812, + -0.24511719, + 0.54296875, + 0.71875, + 0.010009766, + -0.04296875, + 0.083984375, + -0.52734375, + 0.13964844, + -0.27539062, + -0.30273438, + 1.1484375, + -0.515625, + -0.19335938, + 0.58984375, + 0.049072266, + 0.703125, + -0.04272461, + 0.5078125, + 0.34960938, + -0.3359375, + -0.47460938, + 0.049316406, + 0.36523438, + 0.7578125, + -0.022827148, + -0.71484375, + 0.21972656, + 0.09716797, + -0.203125, + -0.36914062, + 1.34375, + 0.34179688, + 0.46679688, + 1.078125, + 0.26171875, + 0.41992188, + 0.22363281, + -0.515625, + -0.5703125, + 0.13378906, + 0.26757812, + -0.22558594, + -0.5234375, + 0.06689453, + 0.08251953, + -0.625, + 0.16796875, + 0.43164062, + -0.55859375, + 0.28125, + 0.078125, + 0.6328125, + 0.23242188, + -0.064941406, + -0.004486084, + -0.20703125, + 0.2734375, + 0.453125, + -0.734375, + 0.04272461, + 0.36132812, + -0.19628906, + -0.12402344, + 1.3515625, + 0.25585938, + 0.4921875, + -0.29296875, + -0.58984375, + 0.021240234, + -0.044677734, + 0.7578125, + -0.7890625, + 0.10253906, + -0.15820312, + -0.5078125, + -0.39453125, + -0.453125, + 0.35742188, + 0.921875, + 0.44335938, + -0.49804688, + 0.44335938, + 0.31445312, + 0.58984375, + -1.0078125, + -0.22460938, + 0.24121094, + 0.87890625, + 0.66015625, + -0.390625, + -0.05053711, + 0.059570312, + 0.36132812, + -0.00038719177, + -0.017089844, + 0.62890625, + 0.203125, + 0.17480469, + 0.025512695, + 0.47460938, + 0.3125, + 1.140625, + 0.32421875, + -0.057861328, + 0.36914062, + -0.7265625, + -0.51953125, + 0.26953125, + 0.42773438, + 0.064453125, + 0.6328125, + 0.27148438, + -0.11767578, + 0.66796875, + -0.38671875, + 0.5234375, + -0.59375, + 0.5078125, + 0.008239746, + -0.34179688, + -0.27539062, + 0.5234375, + 1.296875, + 0.29492188, + -0.010986328, + -0.41210938, + 0.59375, + 0.061767578, + -0.33398438, + -2.03125, + 0.87890625, + -0.010620117, + 0.53125, + 0.14257812, + -0.515625, + -1.03125, + 0.578125, + 0.1875, + 0.44335938, + -0.33203125, + -0.36328125, + -0.3203125, + 0.29296875, + -0.8203125, + 0.41015625, + -0.48242188, + 0.66015625, + 0.5625, + -0.16503906, + -0.54296875, + -0.38085938, + 0.26171875, + 0.62109375, + 0.29101562, + -0.31054688, + 0.23730469, + -0.8515625, + 0.5234375, + 0.15332031, + 0.52734375, + -0.079589844, + -0.080566406, + -0.15527344, + -0.022827148, + 0.030517578, + -0.1640625, + -0.421875, + 0.09716797, + 0.03930664, + -0.055908203, + -0.546875, + -0.47851562, + 0.091796875, + 0.32226562, + -0.94140625, + -0.04638672, + -1.203125, + -0.39648438, + 0.45507812, + 0.296875, + -0.45703125, + 0.37890625, + -0.122558594, + 0.28320312, + -0.01965332, + -0.11669922, + -0.34570312, + -0.53515625, + -0.091308594, + -0.9375, + -0.32617188, + 0.095214844, + -0.4765625, + 0.37890625, + -0.859375, + 1.1015625, + -0.08935547, + 0.46484375, + -0.19238281, + 0.7109375, + 0.040039062, + -0.5390625, + 0.22363281, + -0.70703125, + 0.4921875, + -0.119140625, + -0.26757812, + -0.08496094, + 0.0859375, + -0.00390625, + -0.013366699, + -0.03955078, + 0.07421875, + -0.13085938, + 0.29101562, + -0.12109375, + 0.45703125, + 0.021728516, + 0.38671875, + -0.3671875, + -0.52734375, + -0.115722656, + 0.125, + 0.5703125, + -1.234375, + 0.06298828, + -0.55859375, + 0.60546875, + 0.8125, + -0.0032958984, + -0.068359375, + -0.21191406, + 0.56640625, + 0.17285156, + -0.3515625, + 0.36328125, + -0.99609375, + 0.43554688, + -0.1015625, + 0.07080078, + -0.66796875, + 1.359375, + 0.41601562, + 0.15917969, + 0.17773438, + -0.28710938, + 0.021850586, + -0.46289062, + 0.17578125, + -0.03955078, + -0.026855469, + 0.5078125, + -0.65625, + 0.0012512207, + 0.044433594, + -0.18652344, + 0.4921875, + -0.75390625, + 0.0072021484, + 0.4375, + -0.31445312, + 0.20214844, + 0.15039062, + -0.63671875, + -0.296875, + -0.375, + -0.027709961, + 0.013427734, + 0.17089844, + 0.89453125, + 0.11621094, + -0.43945312, + -0.30859375, + 0.02709961, + 0.23242188, + -0.64453125, + -0.859375, + 0.22167969, + -0.023071289, + -0.052734375, + 0.3671875, + -0.18359375, + 0.81640625, + -0.11816406, + 0.028320312, + 0.19042969, + 0.012817383, + -0.43164062, + 0.55859375, + -0.27929688, + 0.14257812, + -0.140625, + -0.048583984, + -0.014526367, + 0.35742188, + 0.22753906, + 0.13183594, + 0.04638672, + 0.03930664, + -0.29296875, + -0.2109375, + -0.16308594, + -0.48046875, + -0.13378906, + -0.39257812, + 0.29296875, + -0.047851562, + -0.5546875, + 0.08300781, + -0.14941406, + -0.07080078, + 0.12451172, + 0.1953125, + -0.51171875, + -0.048095703, + 0.1953125, + -0.37695312, + 0.46875, + -0.084472656, + 0.19042969, + -0.39453125, + 0.69921875, + -0.0065307617, + 0.25390625, + -0.16992188, + -0.5078125, + 0.016845703, + 0.27929688, + -0.22070312, + 0.671875, + 0.18652344, + 0.25, + -0.046875, + -0.012023926, + -0.36523438, + 0.36523438, + -0.11279297, + 0.421875, + 0.079589844, + -0.100097656, + 0.37304688, + 0.29882812, + -0.10546875, + -0.36523438, + 0.040039062, + 0.546875, + 0.12890625, + -0.06542969, + -0.38085938, + -0.35742188, + -0.6484375, + -0.28515625, + 0.0107421875, + -0.055664062, + 0.45703125, + 0.33984375, + 0.26367188, + -0.23144531, + 0.012878418, + -0.875, + 0.11035156, + 0.33984375, + 0.203125, + 0.38867188, + 0.24902344, + -0.37304688, + -0.98046875, + -0.122558594, + -0.17871094, + -0.09277344, + 0.1796875, + 0.4453125, + -0.66796875, + 0.78515625, + 0.12988281, + 0.35546875, + 0.44140625, + 0.58984375, + 0.29492188, + 0.7734375, + -0.21972656, + -0.40234375, + -0.22265625, + 0.18359375, + 0.54296875, + 0.17382812, + 0.59375, + -0.390625, + -0.92578125, + -0.017456055, + -0.25, + 0.73828125, + 0.7578125, + -0.3828125, + -0.25976562, + 0.049072266, + 0.046875, + -0.3515625, + 0.30078125, + -1.03125, + -0.48828125, + 0.0017929077, + -0.26171875, + 0.20214844, + 0.29882812, + 0.064941406, + 0.21484375, + -0.55078125, + -0.021362305, + 0.12988281, + 0.27148438, + 0.38867188, + -0.19726562, + -0.55078125, + 0.1640625, + 0.32226562, + -0.72265625, + 0.36132812, + 1.21875, + -0.22070312, + -0.32421875, + -0.29882812, + 0.0024414062, + 0.19921875, + 0.734375, + 0.16210938, + 0.17871094, + -0.19140625, + 0.38476562, + -0.06591797, + -0.47070312, + -0.040039062, + -0.33007812, + -0.07910156, + -0.2890625, + 0.00970459, + 0.12695312, + -0.12060547, + -0.18847656, + 1.015625, + -0.032958984, + 0.12451172, + -0.38476562, + 0.063964844, + 1.0859375, + 0.067871094, + -0.24511719, + 0.125, + 0.10546875, + -0.22460938, + -0.29101562, + 0.24414062, + -0.017944336, + -0.15625, + -0.60546875, + -0.25195312, + -0.46875, + 0.80859375, + -0.34960938, + 0.42382812, + 0.796875, + 0.296875, + -0.067871094, + 0.39453125, + 0.07470703, + 0.033935547, + 0.24414062, + 0.32617188, + 0.023925781, + 0.73046875, + 0.2109375, + -0.43164062, + 0.14453125, + 0.63671875, + 0.21972656, + -0.1875, + -0.18066406, + -0.22167969, + -1.3359375, + 0.52734375, + -0.40625, + -0.12988281, + 0.17480469, + -0.18066406, + 0.58984375, + -0.32421875, + -0.13476562, + 0.39257812, + -0.19238281, + 0.068359375, + 0.7265625, + -0.7109375, + -0.125, + 0.328125, + 0.34179688, + -0.48828125, + -0.10058594, + -0.83984375, + 0.30273438, + 0.008239746, + -1.390625, + 0.171875, + 0.34960938, + 0.44921875, + 0.22167969, + 0.60546875, + -0.36914062, + -0.028808594, + -0.19921875, + 0.6875, + 0.52734375, + -0.07421875, + 0.35546875, + 0.546875, + 0.08691406, + 0.23339844, + -0.984375, + -0.20507812, + 0.08544922, + 0.453125, + -0.07421875, + -0.953125, + 0.74609375, + -0.796875, + 0.47851562, + 0.81640625, + -0.44921875, + -0.33398438, + -0.54296875, + 0.46484375, + -0.390625, + -0.24121094, + -0.0115356445, + 1.1328125, + 1.0390625, + 0.6484375, + 0.35742188, + -0.29492188, + -0.0007095337, + -0.060302734, + 0.21777344, + 0.15136719, + -0.6171875, + 0.11328125, + -0.025878906, + 0.19238281, + 0.140625, + 0.171875, + 0.25195312, + 0.10546875, + 0.0008354187, + -0.13476562, + -0.26953125, + 0.025024414, + -0.28320312, + -0.107910156, + 1.015625, + 0.05493164, + -0.12988281, + 0.30859375, + 0.22558594, + -0.60546875, + 0.11328125, + -1.203125, + 0.6484375, + 0.087402344, + 0.32226562, + 0.63671875, + -0.07714844, + -1.390625, + -0.71875, + -0.34179688, + -0.10546875, + -0.37304688, + -0.09863281, + -0.41210938, + -0.14941406, + 0.41210938, + -0.20898438, + 0.18261719, + 0.67578125, + 0.41601562, + 0.32617188, + 0.2421875, + -0.14257812, + -0.6796875, + 0.01953125, + 0.34179688, + 0.20800781, + -0.123046875, + 0.087402344, + 0.85546875, + 0.33984375, + 0.33203125, + -0.68359375, + 0.44921875, + 0.50390625, + 0.083496094, + 0.10888672, + -0.09863281, + 0.55078125, + 0.09765625, + -0.50390625, + 0.13378906, + -0.29882812, + 0.030761719, + -0.64453125, + 0.22949219, + 0.43945312, + 0.16503906, + 0.10888672, + -0.12792969, + -0.039794922, + -0.111328125, + -0.35742188, + 0.053222656, + -0.78125, + -0.4375, + 0.359375, + -0.88671875, + -0.21972656, + -0.053710938, + 0.91796875, + -0.10644531, + 0.55859375, + -0.7734375, + 0.5078125, + 0.46484375, + 0.32226562, + 0.16796875, + -0.28515625, + 0.045410156, + -0.45117188, + 0.38867188, + -0.33398438, + -0.5234375, + 0.296875, + 0.6015625, + 0.3515625, + -0.734375, + 0.3984375, + -0.08251953, + 0.359375, + -0.28515625, + -0.88671875, + 0.0051879883, + 0.045166016, + -0.7421875, + -0.36523438, + 0.140625, + 0.18066406, + -0.171875, + -0.15625, + -0.53515625, + 0.2421875, + -0.19140625, + -0.18066406, + 0.25390625, + 0.6875, + -0.01965332, + -0.33203125, + 0.29492188, + 0.107421875, + -0.048339844, + -0.82421875, + 0.52734375, + 0.78125, + 0.8203125, + -0.90625, + 0.765625, + 0.0390625, + 0.045410156, + 0.26367188, + -0.14355469, + -0.26367188, + 0.390625, + -0.10888672, + 0.33007812, + -0.5625, + 0.08105469, + -0.13769531, + 0.8515625, + -0.14453125, + 0.77734375, + -0.48046875, + -0.3515625, + -0.25390625, + -0.09277344, + 0.23925781, + -0.022338867, + -0.45898438, + 0.36132812, + -0.23828125, + 0.265625, + -0.48632812, + -0.46875, + -0.75390625, + 1.3125, + 0.78125, + -0.63671875, + -1.21875, + 0.5078125, + -0.27734375, + -0.118652344, + 0.041992188, + -0.14648438, + -0.8046875, + 0.21679688, + -0.79296875, + 0.28320312, + -0.09667969, + 0.42773438, + 0.49414062, + 0.44726562, + 0.21972656, + -0.02746582, + -0.03540039, + -0.14941406, + -0.515625, + -0.27929688, + 0.9609375, + -0.007598877, + 0.34765625, + -0.060546875, + -0.44726562, + 0.7421875, + 0.15332031, + 0.45117188, + -0.4921875, + 0.07080078, + 0.5625, + 0.3984375, + -0.20019531, + 0.014892578, + 0.63671875, + -0.0071411133, + 0.016357422, + 1.0625, + 0.049316406, + 0.18066406, + 0.09814453, + -0.52734375, + -0.359375, + -0.072265625, + -0.41992188, + 0.39648438, + 0.38671875, + -0.30273438, + -0.056640625, + -0.640625, + -0.44921875, + 0.49414062, + 0.29101562, + 0.49609375, + 0.40429688, + -0.10205078, + 0.49414062, + -0.28125, + -0.12695312, + -0.0022735596, + -0.37304688, + 0.122558594, + 0.07519531, + -0.12597656, + -0.38085938, + -0.19824219, + -0.40039062, + 0.56640625, + -1.140625, + -0.515625, + -0.17578125, + -0.765625, + -0.43945312, + 0.3359375, + -0.24707031, + 0.32617188, + -0.45117188, + -0.37109375, + 0.45117188, + -0.27539062, + -0.38867188, + 0.09082031, + 0.17675781, + 0.49414062, + 0.19921875, + 0.17480469, + 0.8515625, + -0.23046875, + -0.234375, + -0.28515625, + 0.10253906, + 0.29101562, + -0.3359375, + -0.203125, + 0.6484375, + 0.11767578, + -0.20214844, + -0.42382812, + 0.26367188, + 0.6328125, + 0.0059509277, + 0.08691406, + -1.5625, + -0.43554688, + 0.17675781, + 0.091796875, + -0.5234375, + -0.09863281, + 0.20605469, + 0.16601562, + -0.578125, + 0.017700195, + 0.41015625, + 1.03125, + -0.55078125, + 0.21289062, + -0.35351562, + 0.24316406, + -0.123535156, + 0.11035156, + -0.48242188, + -0.34179688, + 0.45117188, + 0.3125, + -0.071777344, + 0.12792969, + 0.55859375, + 0.063964844, + -0.21191406, + 0.01965332, + -1.359375, + -0.21582031, + -0.019042969, + 0.16308594, + -0.3671875, + -0.40625, + -1.0234375, + -0.21289062, + 0.24023438, + -0.28125, + 0.26953125, + -0.14550781, + -0.087890625, + 0.16113281, + -0.49804688, + -0.17675781, + -0.890625, + 0.27929688, + 0.484375, + 0.27148438, + 0.11816406, + 0.83984375, + 0.029052734, + -0.890625, + 0.66796875, + 0.78515625, + -0.953125, + 0.49414062, + -0.546875, + 0.106933594, + -0.08251953, + 0.2890625, + -0.1484375, + -0.85546875, + 0.32421875, + -0.0040893555, + -0.16601562, + -0.16699219, + 0.24414062, + -0.5078125, + 0.25390625, + -0.10253906, + 0.15625, + 0.140625, + -0.27539062, + -0.546875, + -0.5546875, + -0.71875, + 0.37304688, + 0.060058594, + -0.076171875, + 0.44921875, + 0.06933594, + -0.28710938, + -0.22949219, + 0.17578125, + 0.09814453, + 0.4765625, + -0.95703125, + -0.03540039, + 0.21289062, + -0.7578125, + -0.07373047, + 0.10546875, + 0.07128906, + 0.76171875, + 0.4296875, + -0.09375, + 0.27539062, + -0.55078125, + 0.29882812, + -0.42382812, + 0.32617188, + -0.39648438, + 0.12451172, + 0.16503906, + -0.22460938, + -0.65625, + -0.022094727, + 0.61328125, + -0.024780273, + 0.62109375, + -0.033447266, + 0.515625, + 0.12890625, + -0.21875, + -0.08642578, + 0.49804688, + -0.2265625, + -0.29296875, + 0.19238281, + 0.3515625, + -1.265625, + 0.57421875, + 0.20117188, + -0.28320312, + 0.1953125, + -0.30664062, + 0.2265625, + -0.11230469, + 0.83984375, + 0.111328125, + 0.265625, + 0.71484375, + -0.625, + 0.38867188, + 0.47070312, + -0.32617188, + -0.171875, + 1.0078125, + 0.19726562, + -0.118652344, + 0.63671875, + -0.068359375, + -0.25585938, + 0.4140625, + -0.29296875, + 0.21386719, + -0.064453125, + 0.15820312, + -0.89453125, + -0.16308594, + 0.48046875, + 0.14648438, + -0.5703125, + 0.84765625, + -0.19042969, + 0.03515625, + 0.42578125, + -0.27539062, + -0.5390625, + 0.95703125, + 0.2734375, + 0.16699219, + -0.328125, + 0.11279297, + 0.003250122, + 0.47265625, + -0.31640625, + 0.546875, + 0.55859375, + 0.06933594, + -0.61328125, + -0.16210938, + -0.375, + 0.100097656, + -0.088378906, + 0.12695312, + 0.079589844, + 0.123535156, + -1.0078125, + 0.6875, + 0.022949219, + -0.40039062, + -0.09863281, + 0.29101562, + -1.2890625, + -0.20996094, + 0.36328125, + -0.3515625, + 0.7890625, + 0.12207031, + 0.48046875, + -0.13671875, + -0.041015625, + 0.19824219, + 0.19921875, + 0.01171875, + -0.37695312, + -0.62890625, + 0.9375, + -0.671875, + 0.24609375, + 0.6484375, + -0.29101562, + 0.076171875, + 0.62109375, + -0.5546875, + 0.36523438, + 0.75390625, + -0.19140625, + -0.875, + -0.8203125, + -0.24414062, + -0.625, + 0.1796875, + -0.40039062, + 0.25390625, + -0.14550781, + -0.21679688, + -0.828125, + 0.3359375, + 0.43554688, + 0.55078125, + -0.44921875, + -0.28710938, + 0.24023438, + 0.18066406, + -0.6953125, + 0.020385742, + -0.11376953, + 0.13867188, + -0.92578125, + 0.33398438, + -0.328125, + 0.78125, + -0.45507812, + -0.07470703, + 0.34179688, + 0.07080078, + 0.76171875, + 0.37890625, + -0.10644531, + 0.90234375, + -0.21875, + -0.15917969, + -0.36132812, + 0.2109375, + -0.45703125, + -0.76953125, + 0.21289062, + 0.26367188, + 0.49804688, + 0.35742188, + -0.20019531, + 0.31054688, + 0.34179688, + 0.17089844, + -0.15429688, + 0.39648438, + -0.5859375, + 0.20996094, + -0.40039062, + 0.5703125, + -0.515625, + 0.5234375, + 0.049560547, + 0.328125, + 0.24804688, + 0.42578125, + 0.609375, + 0.19238281, + 0.27929688, + 0.19335938, + 0.78125, + -0.9921875, + 0.23925781, + -1.3828125, + -0.22949219, + -0.578125, + -0.13964844, + -0.17382812, + -0.011169434, + 0.26171875, + -0.73046875, + -1.4375, + 0.6953125, + -0.7421875, + 0.052246094, + 0.12207031, + 1.3046875, + 0.38867188, + 0.040283203, + -0.546875, + -0.0021514893, + 0.18457031, + -0.5546875, + -0.51171875, + -0.16308594, + -0.104003906, + -0.38867188, + -0.20996094, + -0.8984375, + 0.6015625, + -0.30078125, + -0.13769531, + 0.16113281, + 0.58203125, + -0.23730469, + -0.125, + -1.0234375, + 0.875, + -0.7109375, + 0.29101562, + 0.09667969, + -0.3203125, + -0.48046875, + 0.37890625, + 0.734375, + -0.28710938, + -0.29882812, + -0.05493164, + 0.34765625, + -0.84375, + 0.65625, + 0.578125, + -0.20019531, + 0.13769531, + 0.10058594, + -0.37109375, + 0.36523438, + -0.22167969, + 0.72265625, + ], + "inputTextTokenCount": 6, + }, + ], + "amazon.titan-embed-text-v1::This is an embedding test.": [ + {"Content-Type": "application/json", "x-amzn-RequestId": "11233989-07e8-4ecb-9ba6-79601ba6d8cc"}, + 200, + { + "embedding": [ + -0.14160156, + 0.034423828, + 0.54296875, + 0.10986328, + 0.053466797, + 0.3515625, + 0.12988281, + -0.0002708435, + -0.21484375, + 0.060302734, + 0.58984375, + -0.5859375, + 0.52734375, + 0.82421875, + -0.91015625, + -0.19628906, + 0.45703125, + 0.609375, + -0.67578125, + 0.39453125, + -0.46875, + -0.25390625, + -0.21191406, + 0.114746094, + 0.31640625, + -0.41015625, + -0.32617188, + -0.43554688, + 0.4765625, + -0.4921875, + 0.40429688, + 0.06542969, + 0.859375, + -0.008056641, + -0.19921875, + 0.072753906, + 0.33203125, + 0.69921875, + 0.39453125, + 0.15527344, + 0.08886719, + -0.25, + 0.859375, + 0.22949219, + -0.19042969, + 0.13769531, + -0.078125, + 0.41210938, + 0.875, + 0.5234375, + 0.59765625, + -0.22949219, + -0.22558594, + -0.47460938, + 0.37695312, + 0.51953125, + -0.5703125, + 0.46679688, + 0.43554688, + 0.17480469, + -0.080566406, + -0.16699219, + -0.734375, + -1.0625, + -0.33984375, + 0.390625, + -0.18847656, + -0.5234375, + -0.48828125, + 0.44921875, + -0.09814453, + -0.3359375, + 0.087402344, + 0.36914062, + 1.3203125, + 0.25585938, + 0.14746094, + -0.059570312, + -0.15820312, + -0.037353516, + -0.61328125, + -0.6484375, + -0.35351562, + 0.55078125, + -0.26953125, + 0.90234375, + 0.3671875, + 0.31054688, + 0.00014019012, + -0.171875, + 0.025512695, + 0.5078125, + 0.11621094, + 0.33203125, + 0.8125, + -0.3046875, + -1.078125, + -0.5703125, + 0.26171875, + -0.4609375, + 0.203125, + 0.44726562, + -0.5078125, + 0.41601562, + -0.1953125, + 0.028930664, + -0.57421875, + 0.2265625, + 0.13574219, + -0.040039062, + -0.22949219, + -0.515625, + -0.19042969, + -0.30078125, + 0.10058594, + -0.66796875, + 0.6015625, + 0.296875, + -0.765625, + -0.87109375, + 0.2265625, + 0.068847656, + -0.088378906, + -0.1328125, + -0.796875, + -0.37304688, + 0.47460938, + -0.3515625, + -0.8125, + -0.32226562, + 0.265625, + 0.3203125, + -0.4140625, + -0.49023438, + 0.859375, + -0.19140625, + -0.6328125, + 0.10546875, + -0.5625, + 0.66015625, + 0.26171875, + -0.2109375, + 0.421875, + -0.82421875, + 0.29296875, + 0.17773438, + 0.24023438, + 0.5078125, + -0.49804688, + -0.10205078, + 0.10498047, + -0.36132812, + -0.47460938, + -0.20996094, + 0.010070801, + -0.546875, + 0.66796875, + -0.123046875, + -0.75390625, + 0.19628906, + 0.17480469, + 0.18261719, + -0.96875, + -0.26171875, + 0.4921875, + -0.40039062, + 0.296875, + 0.1640625, + -0.20507812, + -0.36132812, + 0.76171875, + -1.234375, + -0.625, + 0.060058594, + -0.09375, + -0.14746094, + 1.09375, + 0.057861328, + 0.22460938, + -0.703125, + 0.07470703, + 0.23828125, + -0.083984375, + -0.54296875, + 0.5546875, + -0.5, + -0.390625, + 0.106933594, + 0.6640625, + 0.27734375, + -0.953125, + 0.35351562, + -0.7734375, + -0.77734375, + 0.16503906, + -0.42382812, + 0.36914062, + 0.020141602, + -1.3515625, + 0.18847656, + 0.13476562, + -0.034179688, + -0.03930664, + -0.03857422, + -0.027954102, + 0.73828125, + -0.18945312, + -0.09814453, + -0.46289062, + 0.36914062, + 0.033203125, + 0.020874023, + -0.703125, + 0.91796875, + 0.38671875, + 0.625, + -0.19335938, + -0.16796875, + -0.58203125, + 0.21386719, + -0.032470703, + -0.296875, + -0.15625, + -0.1640625, + -0.74609375, + 0.328125, + 0.5546875, + -0.1953125, + 1.0546875, + 0.171875, + -0.099609375, + 0.5234375, + 0.05078125, + -0.35742188, + -0.2734375, + -1.3203125, + -0.8515625, + -0.16015625, + 0.01574707, + 0.29296875, + 0.18457031, + -0.265625, + 0.048339844, + 0.045654297, + -0.32226562, + 0.087890625, + -0.0047302246, + 0.38671875, + 0.10644531, + -0.06225586, + 1.03125, + 0.94140625, + -0.3203125, + 0.20800781, + -1.171875, + 0.48046875, + -0.091796875, + 0.20800781, + -0.1328125, + -0.20507812, + 0.28125, + -0.47070312, + -0.09033203, + 0.0013809204, + -0.08203125, + 0.43359375, + -0.03100586, + -0.060791016, + -0.53515625, + -1.46875, + 0.000101566315, + 0.515625, + 0.40625, + -0.10498047, + -0.15820312, + -0.009460449, + -0.77734375, + -0.5859375, + 0.9765625, + 0.099609375, + 0.51953125, + 0.38085938, + -0.09667969, + -0.100097656, + -0.5, + -1.3125, + -0.18066406, + -0.099121094, + 0.26171875, + -0.14453125, + -0.546875, + 0.17578125, + 0.484375, + 0.765625, + 0.45703125, + 0.2734375, + 0.0028076172, + 0.17089844, + -0.32421875, + -0.37695312, + 0.30664062, + -0.48046875, + 0.07128906, + 0.031982422, + -0.31054688, + -0.055419922, + -0.29296875, + 0.3359375, + -0.296875, + 0.47851562, + -0.05126953, + 0.18457031, + -0.01953125, + -0.35742188, + 0.017944336, + -0.25, + 0.10595703, + 0.17382812, + -0.73828125, + 0.36914062, + -0.15234375, + -0.8125, + 0.17382812, + 0.048095703, + 0.5625, + -0.33789062, + 0.023071289, + -0.21972656, + 0.16015625, + 0.032958984, + -1.1171875, + -0.984375, + 0.83984375, + 0.009033203, + -0.042236328, + -0.46484375, + -0.08203125, + 0.44726562, + -0.765625, + -0.3984375, + -0.40820312, + -0.234375, + 0.044189453, + 0.119628906, + -0.7578125, + -0.55078125, + -0.4453125, + 0.7578125, + 0.34960938, + 0.96484375, + 0.35742188, + 0.36914062, + -0.35351562, + -0.36132812, + 1.109375, + 0.5859375, + 0.85546875, + -0.10644531, + -0.6953125, + -0.0066833496, + 0.042236328, + -0.06689453, + 0.36914062, + 0.9765625, + -0.3046875, + 0.59765625, + -0.6640625, + 0.21484375, + -0.07128906, + 1.1328125, + -0.51953125, + 0.86328125, + -0.11328125, + 0.15722656, + -0.36328125, + -0.04638672, + 1.4375, + 0.18457031, + -0.18359375, + 0.10595703, + -0.49023438, + -0.07324219, + -0.73046875, + -0.119140625, + 0.021118164, + 0.4921875, + -0.46875, + 0.28710938, + 0.3359375, + 0.11767578, + -0.2109375, + -0.14550781, + 0.39648438, + -0.27734375, + 0.48046875, + 0.12988281, + 0.45507812, + -0.375, + -0.84765625, + 0.25585938, + -0.36523438, + 0.8046875, + 0.42382812, + -0.24511719, + 0.54296875, + 0.71875, + 0.010009766, + -0.04296875, + 0.083984375, + -0.52734375, + 0.13964844, + -0.27539062, + -0.30273438, + 1.1484375, + -0.515625, + -0.19335938, + 0.58984375, + 0.049072266, + 0.703125, + -0.04272461, + 0.5078125, + 0.34960938, + -0.3359375, + -0.47460938, + 0.049316406, + 0.36523438, + 0.7578125, + -0.022827148, + -0.71484375, + 0.21972656, + 0.09716797, + -0.203125, + -0.36914062, + 1.34375, + 0.34179688, + 0.46679688, + 1.078125, + 0.26171875, + 0.41992188, + 0.22363281, + -0.515625, + -0.5703125, + 0.13378906, + 0.26757812, + -0.22558594, + -0.5234375, + 0.06689453, + 0.08251953, + -0.625, + 0.16796875, + 0.43164062, + -0.55859375, + 0.28125, + 0.078125, + 0.6328125, + 0.23242188, + -0.064941406, + -0.004486084, + -0.20703125, + 0.2734375, + 0.453125, + -0.734375, + 0.04272461, + 0.36132812, + -0.19628906, + -0.12402344, + 1.3515625, + 0.25585938, + 0.4921875, + -0.29296875, + -0.58984375, + 0.021240234, + -0.044677734, + 0.7578125, + -0.7890625, + 0.10253906, + -0.15820312, + -0.5078125, + -0.39453125, + -0.453125, + 0.35742188, + 0.921875, + 0.44335938, + -0.49804688, + 0.44335938, + 0.31445312, + 0.58984375, + -1.0078125, + -0.22460938, + 0.24121094, + 0.87890625, + 0.66015625, + -0.390625, + -0.05053711, + 0.059570312, + 0.36132812, + -0.00038719177, + -0.017089844, + 0.62890625, + 0.203125, + 0.17480469, + 0.025512695, + 0.47460938, + 0.3125, + 1.140625, + 0.32421875, + -0.057861328, + 0.36914062, + -0.7265625, + -0.51953125, + 0.26953125, + 0.42773438, + 0.064453125, + 0.6328125, + 0.27148438, + -0.11767578, + 0.66796875, + -0.38671875, + 0.5234375, + -0.59375, + 0.5078125, + 0.008239746, + -0.34179688, + -0.27539062, + 0.5234375, + 1.296875, + 0.29492188, + -0.010986328, + -0.41210938, + 0.59375, + 0.061767578, + -0.33398438, + -2.03125, + 0.87890625, + -0.010620117, + 0.53125, + 0.14257812, + -0.515625, + -1.03125, + 0.578125, + 0.1875, + 0.44335938, + -0.33203125, + -0.36328125, + -0.3203125, + 0.29296875, + -0.8203125, + 0.41015625, + -0.48242188, + 0.66015625, + 0.5625, + -0.16503906, + -0.54296875, + -0.38085938, + 0.26171875, + 0.62109375, + 0.29101562, + -0.31054688, + 0.23730469, + -0.8515625, + 0.5234375, + 0.15332031, + 0.52734375, + -0.079589844, + -0.080566406, + -0.15527344, + -0.022827148, + 0.030517578, + -0.1640625, + -0.421875, + 0.09716797, + 0.03930664, + -0.055908203, + -0.546875, + -0.47851562, + 0.091796875, + 0.32226562, + -0.94140625, + -0.04638672, + -1.203125, + -0.39648438, + 0.45507812, + 0.296875, + -0.45703125, + 0.37890625, + -0.122558594, + 0.28320312, + -0.01965332, + -0.11669922, + -0.34570312, + -0.53515625, + -0.091308594, + -0.9375, + -0.32617188, + 0.095214844, + -0.4765625, + 0.37890625, + -0.859375, + 1.1015625, + -0.08935547, + 0.46484375, + -0.19238281, + 0.7109375, + 0.040039062, + -0.5390625, + 0.22363281, + -0.70703125, + 0.4921875, + -0.119140625, + -0.26757812, + -0.08496094, + 0.0859375, + -0.00390625, + -0.013366699, + -0.03955078, + 0.07421875, + -0.13085938, + 0.29101562, + -0.12109375, + 0.45703125, + 0.021728516, + 0.38671875, + -0.3671875, + -0.52734375, + -0.115722656, + 0.125, + 0.5703125, + -1.234375, + 0.06298828, + -0.55859375, + 0.60546875, + 0.8125, + -0.0032958984, + -0.068359375, + -0.21191406, + 0.56640625, + 0.17285156, + -0.3515625, + 0.36328125, + -0.99609375, + 0.43554688, + -0.1015625, + 0.07080078, + -0.66796875, + 1.359375, + 0.41601562, + 0.15917969, + 0.17773438, + -0.28710938, + 0.021850586, + -0.46289062, + 0.17578125, + -0.03955078, + -0.026855469, + 0.5078125, + -0.65625, + 0.0012512207, + 0.044433594, + -0.18652344, + 0.4921875, + -0.75390625, + 0.0072021484, + 0.4375, + -0.31445312, + 0.20214844, + 0.15039062, + -0.63671875, + -0.296875, + -0.375, + -0.027709961, + 0.013427734, + 0.17089844, + 0.89453125, + 0.11621094, + -0.43945312, + -0.30859375, + 0.02709961, + 0.23242188, + -0.64453125, + -0.859375, + 0.22167969, + -0.023071289, + -0.052734375, + 0.3671875, + -0.18359375, + 0.81640625, + -0.11816406, + 0.028320312, + 0.19042969, + 0.012817383, + -0.43164062, + 0.55859375, + -0.27929688, + 0.14257812, + -0.140625, + -0.048583984, + -0.014526367, + 0.35742188, + 0.22753906, + 0.13183594, + 0.04638672, + 0.03930664, + -0.29296875, + -0.2109375, + -0.16308594, + -0.48046875, + -0.13378906, + -0.39257812, + 0.29296875, + -0.047851562, + -0.5546875, + 0.08300781, + -0.14941406, + -0.07080078, + 0.12451172, + 0.1953125, + -0.51171875, + -0.048095703, + 0.1953125, + -0.37695312, + 0.46875, + -0.084472656, + 0.19042969, + -0.39453125, + 0.69921875, + -0.0065307617, + 0.25390625, + -0.16992188, + -0.5078125, + 0.016845703, + 0.27929688, + -0.22070312, + 0.671875, + 0.18652344, + 0.25, + -0.046875, + -0.012023926, + -0.36523438, + 0.36523438, + -0.11279297, + 0.421875, + 0.079589844, + -0.100097656, + 0.37304688, + 0.29882812, + -0.10546875, + -0.36523438, + 0.040039062, + 0.546875, + 0.12890625, + -0.06542969, + -0.38085938, + -0.35742188, + -0.6484375, + -0.28515625, + 0.0107421875, + -0.055664062, + 0.45703125, + 0.33984375, + 0.26367188, + -0.23144531, + 0.012878418, + -0.875, + 0.11035156, + 0.33984375, + 0.203125, + 0.38867188, + 0.24902344, + -0.37304688, + -0.98046875, + -0.122558594, + -0.17871094, + -0.09277344, + 0.1796875, + 0.4453125, + -0.66796875, + 0.78515625, + 0.12988281, + 0.35546875, + 0.44140625, + 0.58984375, + 0.29492188, + 0.7734375, + -0.21972656, + -0.40234375, + -0.22265625, + 0.18359375, + 0.54296875, + 0.17382812, + 0.59375, + -0.390625, + -0.92578125, + -0.017456055, + -0.25, + 0.73828125, + 0.7578125, + -0.3828125, + -0.25976562, + 0.049072266, + 0.046875, + -0.3515625, + 0.30078125, + -1.03125, + -0.48828125, + 0.0017929077, + -0.26171875, + 0.20214844, + 0.29882812, + 0.064941406, + 0.21484375, + -0.55078125, + -0.021362305, + 0.12988281, + 0.27148438, + 0.38867188, + -0.19726562, + -0.55078125, + 0.1640625, + 0.32226562, + -0.72265625, + 0.36132812, + 1.21875, + -0.22070312, + -0.32421875, + -0.29882812, + 0.0024414062, + 0.19921875, + 0.734375, + 0.16210938, + 0.17871094, + -0.19140625, + 0.38476562, + -0.06591797, + -0.47070312, + -0.040039062, + -0.33007812, + -0.07910156, + -0.2890625, + 0.00970459, + 0.12695312, + -0.12060547, + -0.18847656, + 1.015625, + -0.032958984, + 0.12451172, + -0.38476562, + 0.063964844, + 1.0859375, + 0.067871094, + -0.24511719, + 0.125, + 0.10546875, + -0.22460938, + -0.29101562, + 0.24414062, + -0.017944336, + -0.15625, + -0.60546875, + -0.25195312, + -0.46875, + 0.80859375, + -0.34960938, + 0.42382812, + 0.796875, + 0.296875, + -0.067871094, + 0.39453125, + 0.07470703, + 0.033935547, + 0.24414062, + 0.32617188, + 0.023925781, + 0.73046875, + 0.2109375, + -0.43164062, + 0.14453125, + 0.63671875, + 0.21972656, + -0.1875, + -0.18066406, + -0.22167969, + -1.3359375, + 0.52734375, + -0.40625, + -0.12988281, + 0.17480469, + -0.18066406, + 0.58984375, + -0.32421875, + -0.13476562, + 0.39257812, + -0.19238281, + 0.068359375, + 0.7265625, + -0.7109375, + -0.125, + 0.328125, + 0.34179688, + -0.48828125, + -0.10058594, + -0.83984375, + 0.30273438, + 0.008239746, + -1.390625, + 0.171875, + 0.34960938, + 0.44921875, + 0.22167969, + 0.60546875, + -0.36914062, + -0.028808594, + -0.19921875, + 0.6875, + 0.52734375, + -0.07421875, + 0.35546875, + 0.546875, + 0.08691406, + 0.23339844, + -0.984375, + -0.20507812, + 0.08544922, + 0.453125, + -0.07421875, + -0.953125, + 0.74609375, + -0.796875, + 0.47851562, + 0.81640625, + -0.44921875, + -0.33398438, + -0.54296875, + 0.46484375, + -0.390625, + -0.24121094, + -0.0115356445, + 1.1328125, + 1.0390625, + 0.6484375, + 0.35742188, + -0.29492188, + -0.0007095337, + -0.060302734, + 0.21777344, + 0.15136719, + -0.6171875, + 0.11328125, + -0.025878906, + 0.19238281, + 0.140625, + 0.171875, + 0.25195312, + 0.10546875, + 0.0008354187, + -0.13476562, + -0.26953125, + 0.025024414, + -0.28320312, + -0.107910156, + 1.015625, + 0.05493164, + -0.12988281, + 0.30859375, + 0.22558594, + -0.60546875, + 0.11328125, + -1.203125, + 0.6484375, + 0.087402344, + 0.32226562, + 0.63671875, + -0.07714844, + -1.390625, + -0.71875, + -0.34179688, + -0.10546875, + -0.37304688, + -0.09863281, + -0.41210938, + -0.14941406, + 0.41210938, + -0.20898438, + 0.18261719, + 0.67578125, + 0.41601562, + 0.32617188, + 0.2421875, + -0.14257812, + -0.6796875, + 0.01953125, + 0.34179688, + 0.20800781, + -0.123046875, + 0.087402344, + 0.85546875, + 0.33984375, + 0.33203125, + -0.68359375, + 0.44921875, + 0.50390625, + 0.083496094, + 0.10888672, + -0.09863281, + 0.55078125, + 0.09765625, + -0.50390625, + 0.13378906, + -0.29882812, + 0.030761719, + -0.64453125, + 0.22949219, + 0.43945312, + 0.16503906, + 0.10888672, + -0.12792969, + -0.039794922, + -0.111328125, + -0.35742188, + 0.053222656, + -0.78125, + -0.4375, + 0.359375, + -0.88671875, + -0.21972656, + -0.053710938, + 0.91796875, + -0.10644531, + 0.55859375, + -0.7734375, + 0.5078125, + 0.46484375, + 0.32226562, + 0.16796875, + -0.28515625, + 0.045410156, + -0.45117188, + 0.38867188, + -0.33398438, + -0.5234375, + 0.296875, + 0.6015625, + 0.3515625, + -0.734375, + 0.3984375, + -0.08251953, + 0.359375, + -0.28515625, + -0.88671875, + 0.0051879883, + 0.045166016, + -0.7421875, + -0.36523438, + 0.140625, + 0.18066406, + -0.171875, + -0.15625, + -0.53515625, + 0.2421875, + -0.19140625, + -0.18066406, + 0.25390625, + 0.6875, + -0.01965332, + -0.33203125, + 0.29492188, + 0.107421875, + -0.048339844, + -0.82421875, + 0.52734375, + 0.78125, + 0.8203125, + -0.90625, + 0.765625, + 0.0390625, + 0.045410156, + 0.26367188, + -0.14355469, + -0.26367188, + 0.390625, + -0.10888672, + 0.33007812, + -0.5625, + 0.08105469, + -0.13769531, + 0.8515625, + -0.14453125, + 0.77734375, + -0.48046875, + -0.3515625, + -0.25390625, + -0.09277344, + 0.23925781, + -0.022338867, + -0.45898438, + 0.36132812, + -0.23828125, + 0.265625, + -0.48632812, + -0.46875, + -0.75390625, + 1.3125, + 0.78125, + -0.63671875, + -1.21875, + 0.5078125, + -0.27734375, + -0.118652344, + 0.041992188, + -0.14648438, + -0.8046875, + 0.21679688, + -0.79296875, + 0.28320312, + -0.09667969, + 0.42773438, + 0.49414062, + 0.44726562, + 0.21972656, + -0.02746582, + -0.03540039, + -0.14941406, + -0.515625, + -0.27929688, + 0.9609375, + -0.007598877, + 0.34765625, + -0.060546875, + -0.44726562, + 0.7421875, + 0.15332031, + 0.45117188, + -0.4921875, + 0.07080078, + 0.5625, + 0.3984375, + -0.20019531, + 0.014892578, + 0.63671875, + -0.0071411133, + 0.016357422, + 1.0625, + 0.049316406, + 0.18066406, + 0.09814453, + -0.52734375, + -0.359375, + -0.072265625, + -0.41992188, + 0.39648438, + 0.38671875, + -0.30273438, + -0.056640625, + -0.640625, + -0.44921875, + 0.49414062, + 0.29101562, + 0.49609375, + 0.40429688, + -0.10205078, + 0.49414062, + -0.28125, + -0.12695312, + -0.0022735596, + -0.37304688, + 0.122558594, + 0.07519531, + -0.12597656, + -0.38085938, + -0.19824219, + -0.40039062, + 0.56640625, + -1.140625, + -0.515625, + -0.17578125, + -0.765625, + -0.43945312, + 0.3359375, + -0.24707031, + 0.32617188, + -0.45117188, + -0.37109375, + 0.45117188, + -0.27539062, + -0.38867188, + 0.09082031, + 0.17675781, + 0.49414062, + 0.19921875, + 0.17480469, + 0.8515625, + -0.23046875, + -0.234375, + -0.28515625, + 0.10253906, + 0.29101562, + -0.3359375, + -0.203125, + 0.6484375, + 0.11767578, + -0.20214844, + -0.42382812, + 0.26367188, + 0.6328125, + 0.0059509277, + 0.08691406, + -1.5625, + -0.43554688, + 0.17675781, + 0.091796875, + -0.5234375, + -0.09863281, + 0.20605469, + 0.16601562, + -0.578125, + 0.017700195, + 0.41015625, + 1.03125, + -0.55078125, + 0.21289062, + -0.35351562, + 0.24316406, + -0.123535156, + 0.11035156, + -0.48242188, + -0.34179688, + 0.45117188, + 0.3125, + -0.071777344, + 0.12792969, + 0.55859375, + 0.063964844, + -0.21191406, + 0.01965332, + -1.359375, + -0.21582031, + -0.019042969, + 0.16308594, + -0.3671875, + -0.40625, + -1.0234375, + -0.21289062, + 0.24023438, + -0.28125, + 0.26953125, + -0.14550781, + -0.087890625, + 0.16113281, + -0.49804688, + -0.17675781, + -0.890625, + 0.27929688, + 0.484375, + 0.27148438, + 0.11816406, + 0.83984375, + 0.029052734, + -0.890625, + 0.66796875, + 0.78515625, + -0.953125, + 0.49414062, + -0.546875, + 0.106933594, + -0.08251953, + 0.2890625, + -0.1484375, + -0.85546875, + 0.32421875, + -0.0040893555, + -0.16601562, + -0.16699219, + 0.24414062, + -0.5078125, + 0.25390625, + -0.10253906, + 0.15625, + 0.140625, + -0.27539062, + -0.546875, + -0.5546875, + -0.71875, + 0.37304688, + 0.060058594, + -0.076171875, + 0.44921875, + 0.06933594, + -0.28710938, + -0.22949219, + 0.17578125, + 0.09814453, + 0.4765625, + -0.95703125, + -0.03540039, + 0.21289062, + -0.7578125, + -0.07373047, + 0.10546875, + 0.07128906, + 0.76171875, + 0.4296875, + -0.09375, + 0.27539062, + -0.55078125, + 0.29882812, + -0.42382812, + 0.32617188, + -0.39648438, + 0.12451172, + 0.16503906, + -0.22460938, + -0.65625, + -0.022094727, + 0.61328125, + -0.024780273, + 0.62109375, + -0.033447266, + 0.515625, + 0.12890625, + -0.21875, + -0.08642578, + 0.49804688, + -0.2265625, + -0.29296875, + 0.19238281, + 0.3515625, + -1.265625, + 0.57421875, + 0.20117188, + -0.28320312, + 0.1953125, + -0.30664062, + 0.2265625, + -0.11230469, + 0.83984375, + 0.111328125, + 0.265625, + 0.71484375, + -0.625, + 0.38867188, + 0.47070312, + -0.32617188, + -0.171875, + 1.0078125, + 0.19726562, + -0.118652344, + 0.63671875, + -0.068359375, + -0.25585938, + 0.4140625, + -0.29296875, + 0.21386719, + -0.064453125, + 0.15820312, + -0.89453125, + -0.16308594, + 0.48046875, + 0.14648438, + -0.5703125, + 0.84765625, + -0.19042969, + 0.03515625, + 0.42578125, + -0.27539062, + -0.5390625, + 0.95703125, + 0.2734375, + 0.16699219, + -0.328125, + 0.11279297, + 0.003250122, + 0.47265625, + -0.31640625, + 0.546875, + 0.55859375, + 0.06933594, + -0.61328125, + -0.16210938, + -0.375, + 0.100097656, + -0.088378906, + 0.12695312, + 0.079589844, + 0.123535156, + -1.0078125, + 0.6875, + 0.022949219, + -0.40039062, + -0.09863281, + 0.29101562, + -1.2890625, + -0.20996094, + 0.36328125, + -0.3515625, + 0.7890625, + 0.12207031, + 0.48046875, + -0.13671875, + -0.041015625, + 0.19824219, + 0.19921875, + 0.01171875, + -0.37695312, + -0.62890625, + 0.9375, + -0.671875, + 0.24609375, + 0.6484375, + -0.29101562, + 0.076171875, + 0.62109375, + -0.5546875, + 0.36523438, + 0.75390625, + -0.19140625, + -0.875, + -0.8203125, + -0.24414062, + -0.625, + 0.1796875, + -0.40039062, + 0.25390625, + -0.14550781, + -0.21679688, + -0.828125, + 0.3359375, + 0.43554688, + 0.55078125, + -0.44921875, + -0.28710938, + 0.24023438, + 0.18066406, + -0.6953125, + 0.020385742, + -0.11376953, + 0.13867188, + -0.92578125, + 0.33398438, + -0.328125, + 0.78125, + -0.45507812, + -0.07470703, + 0.34179688, + 0.07080078, + 0.76171875, + 0.37890625, + -0.10644531, + 0.90234375, + -0.21875, + -0.15917969, + -0.36132812, + 0.2109375, + -0.45703125, + -0.76953125, + 0.21289062, + 0.26367188, + 0.49804688, + 0.35742188, + -0.20019531, + 0.31054688, + 0.34179688, + 0.17089844, + -0.15429688, + 0.39648438, + -0.5859375, + 0.20996094, + -0.40039062, + 0.5703125, + -0.515625, + 0.5234375, + 0.049560547, + 0.328125, + 0.24804688, + 0.42578125, + 0.609375, + 0.19238281, + 0.27929688, + 0.19335938, + 0.78125, + -0.9921875, + 0.23925781, + -1.3828125, + -0.22949219, + -0.578125, + -0.13964844, + -0.17382812, + -0.011169434, + 0.26171875, + -0.73046875, + -1.4375, + 0.6953125, + -0.7421875, + 0.052246094, + 0.12207031, + 1.3046875, + 0.38867188, + 0.040283203, + -0.546875, + -0.0021514893, + 0.18457031, + -0.5546875, + -0.51171875, + -0.16308594, + -0.104003906, + -0.38867188, + -0.20996094, + -0.8984375, + 0.6015625, + -0.30078125, + -0.13769531, + 0.16113281, + 0.58203125, + -0.23730469, + -0.125, + -1.0234375, + 0.875, + -0.7109375, + 0.29101562, + 0.09667969, + -0.3203125, + -0.48046875, + 0.37890625, + 0.734375, + -0.28710938, + -0.29882812, + -0.05493164, + 0.34765625, + -0.84375, + 0.65625, + 0.578125, + -0.20019531, + 0.13769531, + 0.10058594, + -0.37109375, + 0.36523438, + -0.22167969, + 0.72265625, + ], + "inputTextTokenCount": 6, + }, + ], + "amazon.titan-text-express-v1::What is 212 degrees Fahrenheit converted to Celsius?": [ + {"Content-Type": "application/json", "x-amzn-RequestId": "03524118-8d77-430f-9e08-63b5c03a40cf"}, + 200, + { + "inputTextTokenCount": 12, + "results": [ + { + "tokenCount": 75, + "outputText": "\nUse the formula,\n°C = (°F - 32) x 5/9\n= 212 x 5/9\n= 100 degrees Celsius\n212 degrees Fahrenheit is 100 degrees Celsius.", + "completionReason": "FINISH", + } + ], + }, + ], + "anthropic.claude-instant-v1::Human: What is 212 degrees Fahrenheit converted to Celsius? Assistant:": [ + {"Content-Type": "application/json", "x-amzn-RequestId": "7b0b37c6-85fb-4664-8f5b-361ca7b1aa18"}, + 200, + { + "completion": " Okay, here are the conversion steps:\n212 degrees Fahrenheit\n- Subtract 32 from 212 to get 180 (to convert from Fahrenheit to Celsius scale)\n- Multiply by 5/9 (because the formula is °C = (°F - 32) × 5/9)\n- 180 × 5/9 = 100\n\nSo 212 degrees Fahrenheit converted to Celsius is 100 degrees Celsius.", + "stop_reason": "stop_sequence", + "stop": "\n\nHuman:", + }, + ], + "cohere.command-text-v14::What is 212 degrees Fahrenheit converted to Celsius?": [ + {"Content-Type": "application/json", "x-amzn-RequestId": "e77422c8-fbbf-4e17-afeb-c758425c9f97"}, + 200, + { + "generations": [ + { + "finish_reason": "MAX_TOKENS", + "id": "d20c06b0-aafe-4230-b2c7-200f4069355e", + "text": " 212°F is equivalent to 100°C. \n\nFahrenheit and Celsius are two temperature scales commonly used in everyday life. The Fahrenheit scale is based on 32°F for the freezing point of water and 212°F for the boiling point of water. On the other hand, the Celsius scale uses 0°C and 100°C as the freezing and boiling points of water, respectively. \n\nTo convert from Fahrenheit to Celsius, we subtract 32 from the Fahrenheit temperature and multiply the result", + } + ], + "id": "e77422c8-fbbf-4e17-afeb-c758425c9f97", + "prompt": "What is 212 degrees Fahrenheit converted to Celsius?", + }, + ], + "does-not-exist::": [ + { + "Content-Type": "application/json", + "x-amzn-RequestId": "f4908827-3db9-4742-9103-2bbc34578b03", + "x-amzn-ErrorType": "ValidationException:http://internal.amazon.com/coral/com.amazon.bedrock/", + }, + 400, + {"message": "The provided model identifier is invalid."}, + ], + "ai21.j2-mid-v1::Invalid Token": [ + { + "Content-Type": "application/json", + "x-amzn-RequestId": "9021791d-3797-493d-9277-e33aa6f6d544", + "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/", + }, + 403, + {"message": "The security token included in the request is invalid."}, + ], + "amazon.titan-embed-g1-text-02::Invalid Token": [ + { + "Content-Type": "application/json", + "x-amzn-RequestId": "73328313-506e-4da8-af0f-51017fa6ca3f", + "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/", + }, + 403, + {"message": "The security token included in the request is invalid."}, + ], + "amazon.titan-embed-text-v1::Invalid Token": [ + { + "Content-Type": "application/json", + "x-amzn-RequestId": "aece6ad7-e2ff-443b-a953-ba7d385fd0cc", + "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/", + }, + 403, + {"message": "The security token included in the request is invalid."}, + ], + "amazon.titan-text-express-v1::Invalid Token": [ + { + "Content-Type": "application/json", + "x-amzn-RequestId": "15b39c8b-8e85-42c9-9623-06720301bda3", + "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/", + }, + 403, + {"message": "The security token included in the request is invalid."}, + ], + "anthropic.claude-instant-v1::Human: Invalid Token Assistant:": [ + { + "Content-Type": "application/json", + "x-amzn-RequestId": "37396f55-b721-4bae-9461-4c369f5a080d", + "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/", + }, + 403, + {"message": "The security token included in the request is invalid."}, + ], + "cohere.command-text-v14::Invalid Token": [ + { + "Content-Type": "application/json", + "x-amzn-RequestId": "22476490-a0d6-42db-b5ea-32d0b8a7f751", + "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/", + }, + 403, + {"message": "The security token included in the request is invalid."}, + ], +} + +MODEL_PATH_RE = re.compile(r"/model/([^/]+)/invoke") + + +def simple_get(self): + content_len = int(self.headers.get("content-length")) + content = json.loads(self.rfile.read(content_len).decode("utf-8")) + + model = MODEL_PATH_RE.match(self.path).group(1) + prompt = extract_shortened_prompt(content, model) + if not prompt: + self.send_response(500) + self.end_headers() + self.wfile.write("Could not parse prompt.".encode("utf-8")) + return + + headers, response = ({}, "") + for k, v in RESPONSES.items(): + if prompt.startswith(k): + headers, status_code, response = v + break + else: # If no matches found + self.send_response(500) + self.end_headers() + self.wfile.write(("Unknown Prompt:\n%s" % prompt).encode("utf-8")) + 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 + self.wfile.write(json.dumps(response).encode("utf-8")) + return + + +def extract_shortened_prompt(content, model): + prompt = content.get("inputText", "") or content.get("prompt", "") + prompt = "::".join((model, prompt)) # Prepend model name to prompt key to keep separate copies + return prompt.lstrip().split("\n")[0] + + +class MockExternalBedrockServer(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(MockExternalBedrockServer, self).__init__(handler=handler, port=port, *args, **kwargs) + + +if __name__ == "__main__": + # Use this to sort dict for easier future incremental updates + print("RESPONSES = %s" % dict(sorted(RESPONSES.items(), key=lambda i: (i[1][1], i[0])))) + + with MockExternalBedrockServer() as server: + print("MockExternalBedrockServer serving on port %s" % str(server.port)) + while True: + pass # Serve forever diff --git a/tests/external_botocore/_test_bedrock_chat_completion.py b/tests/external_botocore/_test_bedrock_chat_completion.py new file mode 100644 index 0000000000..9abdca83cf --- /dev/null +++ b/tests/external_botocore/_test_bedrock_chat_completion.py @@ -0,0 +1,317 @@ +chat_completion_payload_templates = { + "amazon.titan-text-express-v1": '{ "inputText": "%s", "textGenerationConfig": {"temperature": %f, "maxTokenCount": %d }}', + "ai21.j2-mid-v1": '{"prompt": "%s", "temperature": %f, "maxTokens": %d}', + "anthropic.claude-instant-v1": '{"prompt": "Human: %s Assistant:", "temperature": %f, "max_tokens_to_sample": %d}', + "cohere.command-text-v14": '{"prompt": "%s", "temperature": %f, "max_tokens": %d}', +} + +chat_completion_expected_events = { + "amazon.titan-text-express-v1": [ + ( + {"type": "LlmChatCompletionSummary"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "transaction_id": None, + "span_id": "span-id", + "trace_id": "trace-id", + "request_id": "03524118-8d77-430f-9e08-63b5c03a40cf", + "api_key_last_four_digits": "CRET", + "duration": None, # Response time varies each test run + "request.model": "amazon.titan-text-express-v1", + "response.model": "amazon.titan-text-express-v1", + "response.usage.completion_tokens": 75, + "response.usage.total_tokens": 87, + "response.usage.prompt_tokens": 12, + "request.temperature": 0.7, + "request.max_tokens": 100, + "response.choices.finish_reason": "FINISH", + "vendor": "bedrock", + "ingest_source": "Python", + "response.number_of_messages": 2, + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "03524118-8d77-430f-9e08-63b5c03a40cf", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": "What is 212 degrees Fahrenheit converted to Celsius?", + "role": "user", + "completion_id": None, + "sequence": 0, + "response.model": "amazon.titan-text-express-v1", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "03524118-8d77-430f-9e08-63b5c03a40cf", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": "\nUse the formula,\n°C = (°F - 32) x 5/9\n= 212 x 5/9\n= 100 degrees Celsius\n212 degrees Fahrenheit is 100 degrees Celsius.", + "role": "assistant", + "completion_id": None, + "sequence": 1, + "response.model": "amazon.titan-text-express-v1", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ], + "ai21.j2-mid-v1": [ + ( + {"type": "LlmChatCompletionSummary"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "transaction_id": None, + "span_id": "span-id", + "trace_id": "trace-id", + "request_id": "c863d9fc-888b-421c-a175-ac5256baec62", + "response_id": "1234", + "api_key_last_four_digits": "CRET", + "duration": None, # Response time varies each test run + "request.model": "ai21.j2-mid-v1", + "response.model": "ai21.j2-mid-v1", + "request.temperature": 0.7, + "request.max_tokens": 100, + "response.choices.finish_reason": "endoftext", + "vendor": "bedrock", + "ingest_source": "Python", + "response.number_of_messages": 2, + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": "1234-0", + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "c863d9fc-888b-421c-a175-ac5256baec62", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": "What is 212 degrees Fahrenheit converted to Celsius?", + "role": "user", + "completion_id": None, + "sequence": 0, + "response.model": "ai21.j2-mid-v1", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": "1234-1", + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "c863d9fc-888b-421c-a175-ac5256baec62", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": "\n212 degrees Fahrenheit is equal to 100 degrees Celsius.", + "role": "assistant", + "completion_id": None, + "sequence": 1, + "response.model": "ai21.j2-mid-v1", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ], + "anthropic.claude-instant-v1": [ + ( + {"type": "LlmChatCompletionSummary"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "transaction_id": None, + "span_id": "span-id", + "trace_id": "trace-id", + "request_id": "7b0b37c6-85fb-4664-8f5b-361ca7b1aa18", + "api_key_last_four_digits": "CRET", + "duration": None, # Response time varies each test run + "request.model": "anthropic.claude-instant-v1", + "response.model": "anthropic.claude-instant-v1", + "request.temperature": 0.7, + "request.max_tokens": 100, + "response.choices.finish_reason": "stop_sequence", + "vendor": "bedrock", + "ingest_source": "Python", + "response.number_of_messages": 2, + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "7b0b37c6-85fb-4664-8f5b-361ca7b1aa18", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": "Human: What is 212 degrees Fahrenheit converted to Celsius? Assistant:", + "role": "user", + "completion_id": None, + "sequence": 0, + "response.model": "anthropic.claude-instant-v1", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "7b0b37c6-85fb-4664-8f5b-361ca7b1aa18", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": " Okay, here are the conversion steps:\n212 degrees Fahrenheit\n- Subtract 32 from 212 to get 180 (to convert from Fahrenheit to Celsius scale)\n- Multiply by 5/9 (because the formula is °C = (°F - 32) × 5/9)\n- 180 × 5/9 = 100\n\nSo 212 degrees Fahrenheit converted to Celsius is 100 degrees Celsius.", + "role": "assistant", + "completion_id": None, + "sequence": 1, + "response.model": "anthropic.claude-instant-v1", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ], + "cohere.command-text-v14": [ + ( + {"type": "LlmChatCompletionSummary"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "transaction_id": None, + "span_id": "span-id", + "trace_id": "trace-id", + "request_id": "e77422c8-fbbf-4e17-afeb-c758425c9f97", + "response_id": None, # UUID that varies with each run + "api_key_last_four_digits": "CRET", + "duration": None, # Response time varies each test run + "request.model": "cohere.command-text-v14", + "response.model": "cohere.command-text-v14", + "request.temperature": 0.7, + "request.max_tokens": 100, + "response.choices.finish_reason": "MAX_TOKENS", + "vendor": "bedrock", + "ingest_source": "Python", + "response.number_of_messages": 2, + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "e77422c8-fbbf-4e17-afeb-c758425c9f97", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": "What is 212 degrees Fahrenheit converted to Celsius?", + "role": "user", + "completion_id": None, + "sequence": 0, + "response.model": "cohere.command-text-v14", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ( + {"type": "LlmChatCompletionMessage"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "conversation_id": "my-awesome-id", + "request_id": "e77422c8-fbbf-4e17-afeb-c758425c9f97", + "span_id": "span-id", + "trace_id": "trace-id", + "transaction_id": None, + "content": " 212°F is equivalent to 100°C. \n\nFahrenheit and Celsius are two temperature scales commonly used in everyday life. The Fahrenheit scale is based on 32°F for the freezing point of water and 212°F for the boiling point of water. On the other hand, the Celsius scale uses 0°C and 100°C as the freezing and boiling points of water, respectively. \n\nTo convert from Fahrenheit to Celsius, we subtract 32 from the Fahrenheit temperature and multiply the result", + "role": "assistant", + "completion_id": None, + "sequence": 1, + "response.model": "cohere.command-text-v14", + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ], +} + +chat_completion_expected_client_errors = { + "amazon.titan-text-express-v1": { + "conversation_id": "my-awesome-id", + "request_id": "15b39c8b-8e85-42c9-9623-06720301bda3", + "api_key_last_four_digits": "-KEY", + "request.model": "amazon.titan-text-express-v1", + "request.temperature": 0.7, + "request.max_tokens": 100, + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": 403, + "error.message": "The security token included in the request is invalid.", + "error.code": "UnrecognizedClientException", + }, + "ai21.j2-mid-v1": { + "conversation_id": "my-awesome-id", + "request_id": "9021791d-3797-493d-9277-e33aa6f6d544", + "api_key_last_four_digits": "-KEY", + "request.model": "ai21.j2-mid-v1", + "request.temperature": 0.7, + "request.max_tokens": 100, + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": 403, + "error.message": "The security token included in the request is invalid.", + "error.code": "UnrecognizedClientException", + }, + "anthropic.claude-instant-v1": { + "conversation_id": "my-awesome-id", + "request_id": "37396f55-b721-4bae-9461-4c369f5a080d", + "api_key_last_four_digits": "-KEY", + "request.model": "anthropic.claude-instant-v1", + "request.temperature": 0.7, + "request.max_tokens": 100, + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": 403, + "error.message": "The security token included in the request is invalid.", + "error.code": "UnrecognizedClientException", + }, + "cohere.command-text-v14": { + "conversation_id": "my-awesome-id", + "request_id": "22476490-a0d6-42db-b5ea-32d0b8a7f751", + "api_key_last_four_digits": "-KEY", + "request.model": "cohere.command-text-v14", + "request.temperature": 0.7, + "request.max_tokens": 100, + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": 403, + "error.message": "The security token included in the request is invalid.", + "error.code": "UnrecognizedClientException", + }, +} diff --git a/tests/external_botocore/_test_bedrock_embeddings.py b/tests/external_botocore/_test_bedrock_embeddings.py new file mode 100644 index 0000000000..8fb2ceecee --- /dev/null +++ b/tests/external_botocore/_test_bedrock_embeddings.py @@ -0,0 +1,74 @@ +embedding_payload_templates = { + "amazon.titan-embed-text-v1": '{ "inputText": "%s" }', + "amazon.titan-embed-g1-text-02": '{ "inputText": "%s" }', +} + +embedding_expected_events = { + "amazon.titan-embed-text-v1": [ + ( + {"type": "LlmEmbedding"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "transaction_id": None, + "span_id": "span-id", + "trace_id": "trace-id", + "input": "This is an embedding test.", + "api_key_last_four_digits": "CRET", + "duration": None, # Response time varies each test run + "response.model": "amazon.titan-embed-text-v1", + "request.model": "amazon.titan-embed-text-v1", + "request_id": "11233989-07e8-4ecb-9ba6-79601ba6d8cc", + "response.usage.total_tokens": 6, + "response.usage.prompt_tokens": 6, + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ], + "amazon.titan-embed-g1-text-02": [ + ( + {"type": "LlmEmbedding"}, + { + "id": None, # UUID that varies with each run + "appName": "Python Agent Test (external_botocore)", + "transaction_id": None, + "span_id": "span-id", + "trace_id": "trace-id", + "input": "This is an embedding test.", + "api_key_last_four_digits": "CRET", + "duration": None, # Response time varies each test run + "response.model": "amazon.titan-embed-g1-text-02", + "request.model": "amazon.titan-embed-g1-text-02", + "request_id": "b10ac895-eae3-4f07-b926-10b2866c55ed", + "response.usage.total_tokens": 6, + "response.usage.prompt_tokens": 6, + "vendor": "bedrock", + "ingest_source": "Python", + }, + ), + ] +} + +embedding_expected_client_errors = { + "amazon.titan-embed-text-v1": { + "request_id": "aece6ad7-e2ff-443b-a953-ba7d385fd0cc", + "api_key_last_four_digits": "-KEY", + "request.model": "amazon.titan-embed-text-v1", + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": 403, + "error.message": "The security token included in the request is invalid.", + "error.code": "UnrecognizedClientException", + }, + "amazon.titan-embed-g1-text-02": { + "request_id": "73328313-506e-4da8-af0f-51017fa6ca3f", + "api_key_last_four_digits": "-KEY", + "request.model": "amazon.titan-embed-g1-text-02", + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": 403, + "error.message": "The security token included in the request is invalid.", + "error.code": "UnrecognizedClientException", + }, +} diff --git a/tests/external_botocore/conftest.py b/tests/external_botocore/conftest.py index e5cf155336..6dbf20ef42 100644 --- a/tests/external_botocore/conftest.py +++ b/tests/external_botocore/conftest.py @@ -12,19 +12,152 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json +import os +import re + import pytest +from _mock_external_bedrock_server import ( + MockExternalBedrockServer, + extract_shortened_prompt, +) +from testing_support.fixtures import ( # noqa: F401, pylint: disable=W0611 + collector_agent_registration_fixture, + collector_available_fixture, +) -from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611 +from newrelic.api.time_trace import current_trace +from newrelic.api.transaction import current_transaction +from newrelic.common.object_wrapper import wrap_function_wrapper +from newrelic.common.package_version_utils import ( + get_package_version, + get_package_version_tuple, +) +BOTOCORE_VERSION = get_package_version("botocore") _default_settings = { - 'transaction_tracer.explain_threshold': 0.0, - 'transaction_tracer.transaction_threshold': 0.0, - 'transaction_tracer.stack_trace_threshold': 0.0, - 'debug.log_data_collector_payloads': True, - 'debug.record_transaction_failure': True, + "transaction_tracer.explain_threshold": 0.0, + "transaction_tracer.transaction_threshold": 0.0, + "transaction_tracer.stack_trace_threshold": 0.0, + "debug.log_data_collector_payloads": True, + "debug.record_transaction_failure": True, + "ml_insights_events.enabled": True, } - collector_agent_registration = collector_agent_registration_fixture( - app_name='Python Agent Test (external_botocore)', - default_settings=_default_settings) + app_name="Python Agent Test (external_botocore)", + default_settings=_default_settings, + linked_applications=["Python Agent Test (external_botocore)"], +) + + +# Bedrock Fixtures + +BEDROCK_AUDIT_LOG_FILE = os.path.join(os.path.realpath(os.path.dirname(__file__)), "bedrock_audit.log") +BEDROCK_AUDIT_LOG_CONTENTS = {} + + +@pytest.fixture(scope="session") +def bedrock_server(): + """ + 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 Bedrock backend to a file. + The behavior can be controlled by setting NEW_RELIC_TESTING_RECORD_BEDROCK_RESPONSES=1 as + an environment variable to run using the real Bedrock backend. (Default: mocking) + """ + import boto3 + + from newrelic.core.config import _environ_as_bool + + if get_package_version_tuple("botocore") < (1, 31, 57): + pytest.skip(reason="Bedrock Runtime not available.") + + if not _environ_as_bool("NEW_RELIC_TESTING_RECORD_BEDROCK_RESPONSES", False): + # Use mocked Bedrock backend and prerecorded responses + with MockExternalBedrockServer() as server: + client = boto3.client( # nosec + "bedrock-runtime", + "us-east-1", + endpoint_url="http://localhost:%d" % server.port, + aws_access_key_id="NOT-A-REAL-SECRET", + aws_secret_access_key="NOT-A-REAL-SECRET", + ) + + yield client + else: + # Use real Bedrock backend and record responses + assert ( + os.environ["AWS_ACCESS_KEY_ID"] and os.environ["AWS_SECRET_ACCESS_KEY"] + ), "AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required." + + # Construct real client + client = boto3.client( + "bedrock-runtime", + "us-east-1", + ) + + # Apply function wrappers to record data + wrap_function_wrapper( + "botocore.endpoint", "Endpoint._do_get_response", wrap_botocore_endpoint_Endpoint__do_get_response + ) + yield client # Run tests + + # Write responses to audit log + bedrock_audit_log_contents = dict(sorted(BEDROCK_AUDIT_LOG_CONTENTS.items(), key=lambda i: (i[1][1], i[0]))) + with open(BEDROCK_AUDIT_LOG_FILE, "w") as audit_log_fp: + json.dump(bedrock_audit_log_contents, fp=audit_log_fp, indent=4) + + +# Intercept outgoing requests and log to file for mocking +RECORDED_HEADERS = set(["x-amzn-requestid", "x-amzn-errortype", "content-type"]) + + +def wrap_botocore_endpoint_Endpoint__do_get_response(wrapped, instance, args, kwargs): + request = bind__do_get_response(*args, **kwargs) + if not request: + return wrapped(*args, **kwargs) + + body = json.loads(request.body) + + match = re.search(r"/model/([0-9a-zA-Z.-]+)/", request.url) + model = match.group(1) + prompt = extract_shortened_prompt(body, model) + + # Send request + result = wrapped(*args, **kwargs) + + # Unpack response + success, exception = result + response = (success or exception)[0] + + # Clean up data + data = json.loads(response.content.decode("utf-8")) + headers = dict(response.headers.items()) + headers = dict( + filter( + lambda k: k[0].lower() in RECORDED_HEADERS or k[0].startswith("x-ratelimit"), + headers.items(), + ) + ) + status_code = response.status_code + + # Log response + BEDROCK_AUDIT_LOG_CONTENTS[prompt] = headers, status_code, data # Append response data to audit log + return result + + +def bind__do_get_response(request, operation_model, context): + return request + + +@pytest.fixture(scope="session") +def set_trace_info(): + def _set_trace_info(): + txn = current_transaction() + if txn: + txn._trace_id = "trace-id" + trace = current_trace() + if trace: + trace.guid = "span-id" + + return _set_trace_info diff --git a/tests/external_botocore/test_bedrock_chat_completion.py b/tests/external_botocore/test_bedrock_chat_completion.py new file mode 100644 index 0000000000..ec4ba08643 --- /dev/null +++ b/tests/external_botocore/test_bedrock_chat_completion.py @@ -0,0 +1,233 @@ +# 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 copy +import json +from io import BytesIO + +import botocore.exceptions +import pytest +from _test_bedrock_chat_completion import ( + chat_completion_expected_client_errors, + chat_completion_expected_events, + chat_completion_payload_templates, +) +from conftest import BOTOCORE_VERSION +from testing_support.fixtures import ( + dt_enabled, + override_application_settings, + reset_core_stats_engine, +) +from testing_support.validators.validate_error_trace_attributes import ( + validate_error_trace_attributes, +) +from testing_support.validators.validate_ml_event_count import validate_ml_event_count +from testing_support.validators.validate_ml_events import validate_ml_events +from testing_support.validators.validate_transaction_metrics import ( + validate_transaction_metrics, +) + +from newrelic.api.background_task import background_task +from newrelic.api.transaction import add_custom_attribute +from newrelic.common.object_names import callable_name + + +@pytest.fixture(scope="session", params=[False, True], ids=["Bytes", "Stream"]) +def is_file_payload(request): + return request.param + + +@pytest.fixture( + scope="module", + params=[ + "amazon.titan-text-express-v1", + "ai21.j2-mid-v1", + "anthropic.claude-instant-v1", + "cohere.command-text-v14", + ], +) +def model_id(request): + return request.param + + +@pytest.fixture(scope="module") +def exercise_model(bedrock_server, model_id, is_file_payload): + payload_template = chat_completion_payload_templates[model_id] + + def _exercise_model(prompt, temperature=0.7, max_tokens=100): + body = (payload_template % (prompt, temperature, max_tokens)).encode("utf-8") + if is_file_payload: + body = BytesIO(body) + + response = bedrock_server.invoke_model( + body=body, + modelId=model_id, + accept="application/json", + contentType="application/json", + ) + response_body = json.loads(response.get("body").read()) + assert response_body + + return _exercise_model + + +@pytest.fixture(scope="module") +def expected_events(model_id): + return chat_completion_expected_events[model_id] + + +@pytest.fixture(scope="module") +def expected_events_no_convo_id(model_id): + events = copy.deepcopy(chat_completion_expected_events[model_id]) + for event in events: + event[1]["conversation_id"] = "" + return events + + +@pytest.fixture(scope="module") +def expected_client_error(model_id): + return chat_completion_expected_client_errors[model_id] + + +_test_bedrock_chat_completion_prompt = "What is 212 degrees Fahrenheit converted to Celsius?" + + +# not working with claude +@reset_core_stats_engine() +def test_bedrock_chat_completion_in_txn_with_convo_id(set_trace_info, exercise_model, expected_events): + @validate_ml_events(expected_events) + # One summary event, one user message, and one response message from the assistant + @validate_ml_event_count(count=3) + @validate_transaction_metrics( + name="test_bedrock_chat_completion_in_txn_with_convo_id", + custom_metrics=[ + ("Python/ML/Botocore/%s" % BOTOCORE_VERSION, 1), + ], + background_task=True, + ) + @background_task(name="test_bedrock_chat_completion_in_txn_with_convo_id") + def _test(): + set_trace_info() + add_custom_attribute("conversation_id", "my-awesome-id") + exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100) + + _test() + + +# not working with claude +@reset_core_stats_engine() +def test_bedrock_chat_completion_in_txn_no_convo_id(set_trace_info, exercise_model, expected_events_no_convo_id): + @validate_ml_events(expected_events_no_convo_id) + # One summary event, one user message, and one response message from the assistant + @validate_ml_event_count(count=3) + @validate_transaction_metrics( + name="test_bedrock_chat_completion_in_txn_no_convo_id", + custom_metrics=[ + ("Python/ML/Botocore/%s" % BOTOCORE_VERSION, 1), + ], + background_task=True, + ) + @background_task(name="test_bedrock_chat_completion_in_txn_no_convo_id") + def _test(): + set_trace_info() + exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100) + + _test() + + +@reset_core_stats_engine() +@validate_ml_event_count(count=0) +def test_bedrock_chat_completion_outside_txn(set_trace_info, exercise_model): + set_trace_info() + add_custom_attribute("conversation_id", "my-awesome-id") + exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100) + + +disabled_ml_settings = {"machine_learning.enabled": False, "ml_insights_events.enabled": False} + + +@override_application_settings(disabled_ml_settings) +@reset_core_stats_engine() +@validate_ml_event_count(count=0) +@validate_transaction_metrics( + name="test_bedrock_chat_completion_disabled_settings", + custom_metrics=[ + ("Python/ML/Botocore/%s" % BOTOCORE_VERSION, 1), + ], + background_task=True, +) +@background_task(name="test_bedrock_chat_completion_disabled_settings") +def test_bedrock_chat_completion_disabled_settings(set_trace_info, exercise_model): + set_trace_info() + exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100) + + +_client_error = botocore.exceptions.ClientError +_client_error_name = callable_name(_client_error) + + +@validate_error_trace_attributes( + "botocore.errorfactory:ValidationException", + exact_attrs={ + "agent": {}, + "intrinsic": {}, + "user": { + "conversation_id": "my-awesome-id", + "request_id": "f4908827-3db9-4742-9103-2bbc34578b03", + "api_key_last_four_digits": "CRET", + "request.model": "does-not-exist", + "vendor": "Bedrock", + "ingest_source": "Python", + "http.statusCode": 400, + "error.message": "The provided model identifier is invalid.", + "error.code": "ValidationException", + }, + }, +) +@background_task() +def test_bedrock_chat_completion_error_invalid_model(bedrock_server, set_trace_info): + set_trace_info() + add_custom_attribute("conversation_id", "my-awesome-id") + with pytest.raises(_client_error): + bedrock_server.invoke_model( + body=b"{}", + modelId="does-not-exist", + accept="application/json", + contentType="application/json", + ) + + +@dt_enabled +@reset_core_stats_engine() +def test_bedrock_chat_completion_error_incorrect_access_key( + monkeypatch, bedrock_server, exercise_model, set_trace_info, expected_client_error +): + @validate_error_trace_attributes( + _client_error_name, + exact_attrs={ + "agent": {}, + "intrinsic": {}, + "user": expected_client_error, + }, + ) + @background_task() + def _test(): + monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY") + + with pytest.raises(_client_error): # not sure where this exception actually comes from + set_trace_info() + add_custom_attribute("conversation_id", "my-awesome-id") + exercise_model(prompt="Invalid Token", temperature=0.7, max_tokens=100) + + _test() diff --git a/tests/external_botocore/test_bedrock_embeddings.py b/tests/external_botocore/test_bedrock_embeddings.py new file mode 100644 index 0000000000..b9be995816 --- /dev/null +++ b/tests/external_botocore/test_bedrock_embeddings.py @@ -0,0 +1,159 @@ +# 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 +from io import BytesIO + +import botocore.exceptions +import pytest +from _test_bedrock_embeddings import ( + embedding_expected_client_errors, + embedding_expected_events, + embedding_payload_templates, +) +from conftest import BOTOCORE_VERSION +from testing_support.fixtures import ( # override_application_settings, + dt_enabled, + override_application_settings, + reset_core_stats_engine, +) +from testing_support.validators.validate_error_trace_attributes import ( + validate_error_trace_attributes, +) +from testing_support.validators.validate_ml_event_count import validate_ml_event_count +from testing_support.validators.validate_ml_events import validate_ml_events +from testing_support.validators.validate_transaction_metrics import ( + validate_transaction_metrics, +) + +from newrelic.api.background_task import background_task +from newrelic.common.object_names import callable_name + +disabled_ml_insights_settings = {"ml_insights_events.enabled": False} + + +@pytest.fixture(scope="session", params=[False, True], ids=["Bytes", "Stream"]) +def is_file_payload(request): + return request.param + + +@pytest.fixture( + scope="module", + params=[ + "amazon.titan-embed-text-v1", + "amazon.titan-embed-g1-text-02", + ], +) +def model_id(request): + return request.param + + +@pytest.fixture(scope="module") +def exercise_model(bedrock_server, model_id, is_file_payload): + payload_template = embedding_payload_templates[model_id] + + def _exercise_model(prompt, temperature=0.7, max_tokens=100): + body = (payload_template % prompt).encode("utf-8") + if is_file_payload: + body = BytesIO(body) + + response = bedrock_server.invoke_model( + body=body, + modelId=model_id, + accept="application/json", + contentType="application/json", + ) + response_body = json.loads(response.get("body").read()) + assert response_body + + return _exercise_model + + +@pytest.fixture(scope="module") +def expected_events(model_id): + return embedding_expected_events[model_id] + + +@pytest.fixture(scope="module") +def expected_client_error(model_id): + return embedding_expected_client_errors[model_id] + + +@reset_core_stats_engine() +def test_bedrock_embedding(set_trace_info, exercise_model, expected_events): + @validate_ml_events(expected_events) + @validate_ml_event_count(count=1) + @validate_transaction_metrics( + name="test_bedrock_embedding", + custom_metrics=[ + ("Python/ML/Botocore/%s" % BOTOCORE_VERSION, 1), + ], + background_task=True, + ) + @background_task(name="test_bedrock_embedding") + def _test(): + set_trace_info() + exercise_model(prompt="This is an embedding test.") + + _test() + + +@reset_core_stats_engine() +@validate_ml_event_count(count=0) +def test_bedrock_embedding_outside_txn(exercise_model): + exercise_model(prompt="This is an embedding test.") + + +_client_error = botocore.exceptions.ClientError +_client_error_name = callable_name(_client_error) + + +@override_application_settings(disabled_ml_insights_settings) +@reset_core_stats_engine() +@validate_ml_event_count(count=0) +@validate_transaction_metrics( + name="test_bedrock_embeddings:test_bedrock_embedding_disabled_settings", + custom_metrics=[ + ("Python/ML/Botocore/%s" % BOTOCORE_VERSION, 1), + ], + background_task=True, +) +@background_task() +def test_bedrock_embedding_disabled_settings(set_trace_info, exercise_model): + set_trace_info() + exercise_model(prompt="This is an embedding test.") + + +@dt_enabled +@reset_core_stats_engine() +def test_bedrock_embedding_error_incorrect_access_key( + monkeypatch, bedrock_server, exercise_model, set_trace_info, expected_client_error +): + @validate_error_trace_attributes( + _client_error_name, + exact_attrs={ + "agent": {}, + "intrinsic": {}, + "user": expected_client_error, + }, + ) + @background_task() + def _test(): + monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY") + + with pytest.raises(_client_error): # not sure where this exception actually comes from + set_trace_info() + exercise_model(prompt="Invalid Token", temperature=0.7, max_tokens=100) + + _test() diff --git a/tests/external_boto3/test_boto3_iam.py b/tests/external_botocore/test_boto3_iam.py similarity index 95% rename from tests/external_boto3/test_boto3_iam.py rename to tests/external_botocore/test_boto3_iam.py index a2237dc936..3d672f3751 100644 --- a/tests/external_boto3/test_boto3_iam.py +++ b/tests/external_botocore/test_boto3_iam.py @@ -17,7 +17,7 @@ import boto3 import moto -from testing_support.fixtures import override_application_settings +from testing_support.fixtures import dt_enabled from testing_support.validators.validate_span_events import validate_span_events from testing_support.validators.validate_transaction_metrics import ( validate_transaction_metrics, @@ -53,7 +53,7 @@ ] -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(exact_agents={"http.url": "https://iam.amazonaws.com/"}, count=3) @validate_span_events(expected_agents=("aws.requestId",), count=3) @validate_span_events(exact_agents={"aws.operation": "CreateUser"}, count=1) diff --git a/tests/external_boto3/test_boto3_s3.py b/tests/external_botocore/test_boto3_s3.py similarity index 97% rename from tests/external_boto3/test_boto3_s3.py rename to tests/external_botocore/test_boto3_s3.py index a7ecf034ab..b6299d9f6e 100644 --- a/tests/external_boto3/test_boto3_s3.py +++ b/tests/external_botocore/test_boto3_s3.py @@ -18,7 +18,7 @@ import boto3 import botocore import moto -from testing_support.fixtures import override_application_settings +from testing_support.fixtures import dt_enabled from testing_support.validators.validate_span_events import validate_span_events from testing_support.validators.validate_transaction_metrics import ( validate_transaction_metrics, @@ -73,7 +73,7 @@ ] -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(exact_agents={"aws.operation": "CreateBucket"}, count=1) @validate_span_events(exact_agents={"aws.operation": "PutObject"}, count=1) @validate_span_events(exact_agents={"aws.operation": "ListObjects"}, count=1) diff --git a/tests/external_boto3/test_boto3_sns.py b/tests/external_botocore/test_boto3_sns.py similarity index 94% rename from tests/external_boto3/test_boto3_sns.py rename to tests/external_botocore/test_boto3_sns.py index bafe68611d..5e6c7c4b4e 100644 --- a/tests/external_boto3/test_boto3_sns.py +++ b/tests/external_botocore/test_boto3_sns.py @@ -17,7 +17,7 @@ import boto3 import moto import pytest -from testing_support.fixtures import override_application_settings +from testing_support.fixtures import dt_enabled from testing_support.validators.validate_span_events import validate_span_events from testing_support.validators.validate_transaction_metrics import ( validate_transaction_metrics, @@ -45,7 +45,7 @@ sns_metrics_phone = [("MessageBroker/SNS/Topic" "/Produce/Named/PhoneNumber", 1)] -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(expected_agents=("aws.requestId",), count=2) @validate_span_events(exact_agents={"aws.operation": "CreateTopic"}, count=1) @validate_span_events(exact_agents={"aws.operation": "Publish"}, count=1) @@ -74,7 +74,7 @@ def test_publish_to_sns_topic(topic_argument): assert "MessageId" in published_message -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(expected_agents=("aws.requestId",), count=3) @validate_span_events(exact_agents={"aws.operation": "CreateTopic"}, count=1) @validate_span_events(exact_agents={"aws.operation": "Subscribe"}, count=1) diff --git a/tests/external_botocore/test_botocore_dynamodb.py b/tests/external_botocore/test_botocore_dynamodb.py index 30114d53b1..932fb1743a 100644 --- a/tests/external_botocore/test_botocore_dynamodb.py +++ b/tests/external_botocore/test_botocore_dynamodb.py @@ -17,7 +17,7 @@ import botocore.session import moto -from testing_support.fixtures import override_application_settings +from testing_support.fixtures import dt_enabled from testing_support.validators.validate_span_events import validate_span_events from testing_support.validators.validate_transaction_metrics import ( validate_transaction_metrics, @@ -63,7 +63,7 @@ ] -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(expected_agents=("aws.requestId",), count=8) @validate_span_events(exact_agents={"aws.operation": "PutItem"}, count=1) @validate_span_events(exact_agents={"aws.operation": "GetItem"}, count=1) @@ -80,7 +80,7 @@ background_task=True, ) @background_task() -@moto.mock_dynamodb2 +@moto.mock_dynamodb def test_dynamodb(): session = botocore.session.get_session() client = session.create_client( diff --git a/tests/external_botocore/test_botocore_ec2.py b/tests/external_botocore/test_botocore_ec2.py index 28a8ff63ae..3cb83e3185 100644 --- a/tests/external_botocore/test_botocore_ec2.py +++ b/tests/external_botocore/test_botocore_ec2.py @@ -17,7 +17,7 @@ import botocore.session import moto -from testing_support.fixtures import override_application_settings +from testing_support.fixtures import dt_enabled from testing_support.validators.validate_span_events import validate_span_events from testing_support.validators.validate_transaction_metrics import ( validate_transaction_metrics, @@ -55,7 +55,7 @@ ] -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(expected_agents=("aws.requestId",), count=3) @validate_span_events(exact_agents={"aws.operation": "RunInstances"}, count=1) @validate_span_events(exact_agents={"aws.operation": "DescribeInstances"}, count=1) diff --git a/tests/external_botocore/test_botocore_s3.py b/tests/external_botocore/test_botocore_s3.py index 1984d8103e..ea0c225390 100644 --- a/tests/external_botocore/test_botocore_s3.py +++ b/tests/external_botocore/test_botocore_s3.py @@ -18,7 +18,7 @@ import botocore import botocore.session import moto -from testing_support.fixtures import override_application_settings +from testing_support.fixtures import dt_enabled from testing_support.validators.validate_span_events import validate_span_events from testing_support.validators.validate_transaction_metrics import ( validate_transaction_metrics, @@ -67,7 +67,7 @@ ] -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(exact_agents={"aws.operation": "CreateBucket"}, count=1) @validate_span_events(exact_agents={"aws.operation": "PutObject"}, count=1) @validate_span_events(exact_agents={"aws.operation": "ListObjects"}, count=1) diff --git a/tests/external_botocore/test_botocore_sqs.py b/tests/external_botocore/test_botocore_sqs.py index 3f7d8c0220..63f15801b5 100644 --- a/tests/external_botocore/test_botocore_sqs.py +++ b/tests/external_botocore/test_botocore_sqs.py @@ -18,7 +18,7 @@ import botocore.session import moto import pytest -from testing_support.fixtures import override_application_settings +from testing_support.fixtures import dt_enabled from testing_support.validators.validate_span_events import validate_span_events from testing_support.validators.validate_transaction_metrics import ( validate_transaction_metrics, @@ -70,7 +70,7 @@ ] -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_span_events(exact_agents={"aws.operation": "CreateQueue"}, count=1) @validate_span_events(exact_agents={"aws.operation": "SendMessage"}, count=1) @validate_span_events(exact_agents={"aws.operation": "ReceiveMessage"}, count=1) @@ -124,7 +124,7 @@ def test_sqs(): assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200 -@override_application_settings({"distributed_tracing.enabled": True}) +@dt_enabled @validate_transaction_metrics( "test_botocore_sqs:test_sqs_malformed", scoped_metrics=_sqs_scoped_metrics_malformed, diff --git a/tests/testing_support/validators/validate_ml_events.py b/tests/testing_support/validators/validate_ml_events.py index 251e8dbe79..275a9b2e1b 100644 --- a/tests/testing_support/validators/validate_ml_events.py +++ b/tests/testing_support/validators/validate_ml_events.py @@ -24,7 +24,6 @@ def validate_ml_events(events): @function_wrapper def _validate_wrapper(wrapped, instance, args, kwargs): - record_called = [] recorded_events = [] @@ -55,7 +54,7 @@ def _validate_ml_events(wrapped, instance, args, kwargs): for captured in found_events: if _check_event_attributes(expected, captured, mismatches): matching_ml_events += 1 - assert matching_ml_events == 1, _event_details(matching_ml_events, events, mismatches) + assert matching_ml_events == 1, _event_details(matching_ml_events, found_events, mismatches) return val diff --git a/tox.ini b/tox.ini index eb6b801f4d..25f602d455 100644 --- a/tox.ini +++ b/tox.ini @@ -49,7 +49,7 @@ envlist = python-adapter_daphne-{py37,py38,py39,py310,py311}-daphnelatest, python-adapter_gevent-{py27,py37,py38,py310,py311}, python-adapter_gunicorn-{py37,py38,py39,py310,py311}-aiohttp3-gunicornlatest, - python-adapter_hypercorn-{py37,py38,py39,py310,py311}-hypercornlatest, + python-adapter_hypercorn-{py38,py39,py310,py311}-hypercornlatest, python-adapter_hypercorn-py38-hypercorn{0010,0011,0012,0013}, python-adapter_uvicorn-{py37,py38,py39,py310,py311}-uvicorn{014,latest}, python-adapter_waitress-{py37,py38,py39,py310}-waitress02, @@ -97,7 +97,6 @@ envlist = redis-datastore_redis-{py37,py38,py39,py310,py311,pypy38}-redis{0400,latest}, rediscluster-datastore_rediscluster-{py37,py311,pypy38}-redis{latest}, python-datastore_sqlite-{py27,py37,py38,py39,py310,py311,pypy27,pypy38}, - python-external_boto3-{py27,py37,py38,py39,py310,py311}-boto01, python-external_botocore-{py37,py38,py39,py310,py311}-botocorelatest, python-external_botocore-{py311}-botocore128, python-external_botocore-py310-botocore0125, @@ -252,15 +251,13 @@ deps = datastore_redis-redislatest: redis datastore_rediscluster-redislatest: redis datastore_redis-redis0400: redis<4.1 - external_boto3-boto01: boto3<2.0 - external_boto3-boto01: moto<2.0 - external_boto3-py27: rsa<4.7.1 external_botocore-botocorelatest: botocore + external_botocore-botocorelatest: boto3 external_botocore-botocore128: botocore<1.29 external_botocore-botocore0125: botocore<1.26 - external_botocore-{py37,py38,py39,py310,py311}: moto[awslambda,ec2,iam]<3.0 + external_botocore-{py37,py38,py39,py310,py311}: moto[awslambda,ec2,iam,sqs] external_botocore-py27: rsa<4.7.1 - external_botocore-py27: moto[awslambda,ec2,iam]<2.0 + external_botocore-py27: moto[awslambda,ec2,iam,sqs]<2.0 external_feedparser-feedparser05: feedparser<6 external_feedparser-feedparser06: feedparser<7 external_httplib2: httplib2<1.0 @@ -440,7 +437,6 @@ changedir = datastore_redis: tests/datastore_redis datastore_rediscluster: tests/datastore_rediscluster datastore_sqlite: tests/datastore_sqlite - external_boto3: tests/external_boto3 external_botocore: tests/external_botocore external_feedparser: tests/external_feedparser external_http: tests/external_http