# Copyright 2010 New Relic, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json

import pytest
from testing_support.mock_external_http_server import MockExternalHTTPServer

from newrelic.common.package_version_utils import get_package_version_tuple

# This defines an external server test apps can make requests to instead of
# the real OpenAI backend. This provides 3 features:
#
# 1) This removes dependencies on external websites.
# 2) Provides a better mechanism for making an external call in a test app than
#    simple calling another endpoint the test app makes available because this
#    server will not be instrumented meaning we don't have to sort through
#    transactions to separate the ones created in the test app and the ones
#    created by an external call.
# 3) This app runs on a separate thread meaning it won't block the test app.

STREAMED_RESPONSES = {
    "Stream parsing error.": [
        {
            "Content-Type": "text/event-stream",
            "openai-model": "gpt-3.5-turbo-0613",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "516",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "200",
            "x-ratelimit-limit-tokens": "40000",
            "x-ratelimit-remaining-requests": "199",
            "x-ratelimit-remaining-tokens": "39940",
            "x-ratelimit-reset-requests": "7m12s",
            "x-ratelimit-reset-tokens": "90ms",
            "x-request-id": "49dbbffbd3c3f4612aa48def69059ccd",
        },
        200,
        [
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [
                    {"index": 0, "delta": {"role": "assistant", "content": ""}, "logprobs": None, "finish_reason": None}
                ],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": "212"}, "logprobs": None, "finish_reason": None}],
            },
        ],
    ],
    "Invalid API key.": [
        {"Content-Type": "application/json; charset=utf-8", "x-request-id": "4f8f61a7d0401e42a6760ea2ca2049f6"},
        401,
        {
            "error": {
                "message": "Incorrect API key provided: DEADBEEF. You can find your API key at https://platform.openai.com/account/api-keys.",
                "type": "invalid_request_error",
                "param": None,
                "code": "invalid_api_key",
            }
        },
    ],
    "Model does not exist.": [
        {
            "Content-Type": "application/json; charset=utf-8",
            "x-request-id": "cfdf51fb795362ae578c12a21796262c",
        },
        404,
        {
            "error": {
                "message": "The model `does-not-exist` does not exist",
                "type": "invalid_request_error",
                "param": None,
                "code": "model_not_found",
            }
        },
    ],
    "You are a scientist.": [
        {
            "Content-Type": "text/event-stream",
            "openai-model": "gpt-3.5-turbo-0613",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "516",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "200",
            "x-ratelimit-limit-tokens": "40000",
            "x-ratelimit-remaining-requests": "199",
            "x-ratelimit-remaining-tokens": "39940",
            "x-ratelimit-reset-requests": "7m12s",
            "x-ratelimit-reset-tokens": "90ms",
            "x-request-id": "49dbbffbd3c3f4612aa48def69059ccd",
        },
        200,
        [
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [
                    {"index": 0, "delta": {"role": "assistant", "content": ""}, "logprobs": None, "finish_reason": None}
                ],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": "212"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " degrees"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " Fahrenheit"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " is"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " equal"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " to"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " "}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": "100"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " degrees"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " Celsius"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": "."}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
                "object": "chat.completion.chunk",
                "created": 1706565311,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {}, "logprobs": None, "finish_reason": "stop"}],
            },
        ],
    ],
}

RESPONSES_V1 = {
    "Model does not exist.": [
        {"content-type": "application/json; charset=utf-8", "x-request-id": "req_715be6580ab5bf4eef8d2b0893926ec9"},
        404,
        {
            "error": {
                "message": "The model `does-not-exist` does not exist or you do not have access to it.",
                "type": "invalid_request_error",
                "param": None,
                "code": "model_not_found",
            }
        },
    ],
    "Invalid API key.": [
        {"content-type": "application/json; charset=utf-8", "x-request-id": "req_7ffd0e41c0d751be15275b1df6b2644c"},
        401,
        {
            "error": {
                "message": "Incorrect API key provided: DEADBEEF. You can find your API key at https://platform.openai.com/account/api-keys.",
                "type": "invalid_request_error",
                "param": None,
                "code": "invalid_api_key",
            }
        },
    ],
    "You are a scientist.": [
        {
            "content-type": "application/json",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "1676",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "10000",
            "x-ratelimit-limit-tokens": "60000",
            "x-ratelimit-remaining-requests": "9993",
            "x-ratelimit-remaining-tokens": "59880",
            "x-ratelimit-reset-requests": "54.889s",
            "x-ratelimit-reset-tokens": "120ms",
            "x-request-id": "req_25be7e064e0c590cd65709c85385c796",
        },
        200,
        {
            "id": "chatcmpl-9NPYxI4Zk5ztxNwW5osYdpevgoiBQ",
            "object": "chat.completion",
            "created": 1715366835,
            "model": "gpt-3.5-turbo-0125",
            "choices": [
                {
                    "index": 0,
                    "message": {
                        "role": "assistant",
                        "content": "212 degrees Fahrenheit is equivalent to 100 degrees Celsius. \n\nThe formula to convert Fahrenheit to Celsius is: \n\n\\[Celsius = (Fahrenheit - 32) \\times \\frac{5}{9}\\]\n\nSo, for 212 degrees Fahrenheit:\n\n\\[Celsius = (212 - 32) \\times \\frac{5}{9} = 100\\]",
                    },
                    "logprobs": None,
                    "finish_reason": "stop",
                }
            ],
            "usage": {"prompt_tokens": 26, "completion_tokens": 75, "total_tokens": 101},
            "system_fingerprint": None,
        },
    ],
    "No usage data": [
        {
            "content-type": "application/json",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "324",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "10000",
            "x-ratelimit-limit-tokens": "60000",
            "x-ratelimit-remaining-requests": "9986",
            "x-ratelimit-remaining-tokens": "59895",
            "x-ratelimit-reset-requests": "1m55.869s",
            "x-ratelimit-reset-tokens": "105ms",
            "x-request-id": "req_2c8bb96fe67d2ccfa8305923f04759a2",
        },
        200,
        {
            "id": "chatcmpl-9NPZEmq5Loals5BA3Uw2GsSLhmlNH",
            "object": "chat.completion",
            "created": 1715366852,
            "model": "gpt-3.5-turbo-0125",
            "choices": [
                {
                    "index": 0,
                    "message": {"role": "assistant", "content": "Hello! How can I assist you today?"},
                    "logprobs": None,
                    "finish_reason": "stop",
                }
            ],
            "usage": {"prompt_tokens": 10, "completion_tokens": 9, "total_tokens": 19},
            "system_fingerprint": None,
        },
    ],
    "This is an embedding test.": [
        {
            "content-type": "application/json",
            "openai-model": "text-embedding-ada-002",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "17",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "3000",
            "x-ratelimit-limit-tokens": "1000000",
            "x-ratelimit-remaining-requests": "2999",
            "x-ratelimit-remaining-tokens": "999994",
            "x-ratelimit-reset-requests": "20ms",
            "x-ratelimit-reset-tokens": "0s",
            "x-request-id": "req_eb2b9f2d23a671ad0d69545044437d68",
        },
        200,
        {
            "object": "list",
            "data": [
                {
                    "object": "embedding",
                    "index": 0,
                    "embedding": " ",
                }
            ],
            "model": "text-embedding-ada-002",
            "usage": {"prompt_tokens": 6, "total_tokens": 6},
        },
    ],
}

STREAMED_RESPONSES_V1 = {
    "Invalid API key.": [
        {"content-type": "application/json; charset=utf-8", "x-request-id": "req_a78a2cb09e3c7f224e78bfbf0841e38a"},
        401,
        {
            "error": {
                "message": "Incorrect API key provided: DEADBEEF. You can find your API key at https://platform.openai.com/account/api-keys.",
                "type": "invalid_request_error",
                "param": None,
                "code": "invalid_api_key",
            }
        },
    ],
    "Model does not exist.": [
        {"content-type": "application/json; charset=utf-8", "x-request-id": "req_a03714410fba92532c7de2532d8cf46c"},
        404,
        {
            "error": {
                "message": "The model `does-not-exist` does not exist",
                "type": "invalid_request_error",
                "param": None,
                "code": "model_not_found",
            }
        },
    ],
    "You are a scientist.": [
        {
            "content-type": "text/event-stream",
            "openai-model": "gpt-3.5-turbo-0613",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "6326",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "200",
            "x-ratelimit-limit-tokens": "40000",
            "x-ratelimit-remaining-requests": "198",
            "x-ratelimit-remaining-tokens": "39880",
            "x-ratelimit-reset-requests": "11m32.334s",
            "x-ratelimit-reset-tokens": "180ms",
            "x-request-id": "f8d0f53b6881c5c0a3698e55f8f410ac",
        },
        200,
        [
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [
                    {"index": 0, "delta": {"role": "assistant", "content": ""}, "logprobs": None, "finish_reason": None}
                ],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": "212"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " degrees"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " Fahrenheit"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " is"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " equal"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " to"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " "}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": "100"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " degrees"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": " Celsius"}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {"content": "."}, "logprobs": None, "finish_reason": None}],
            },
            {
                "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
                "object": "chat.completion.chunk",
                "created": 1707867026,
                "model": "gpt-3.5-turbo-0613",
                "system_fingerprint": None,
                "choices": [{"index": 0, "delta": {}, "logprobs": None, "finish_reason": "stop"}],
            },
        ],
    ],
}
RESPONSES = {
    "Invalid API key.": (
        {"Content-Type": "application/json; charset=utf-8", "x-request-id": "4f8f61a7d0401e42a6760ea2ca2049f6"},
        401,
        {
            "error": {
                "message": "Incorrect API key provided: invalid. You can find your API key at https://platform.openai.com/account/api-keys.",
                "type": "invalid_request_error",
                "param": "null",
                "code": "invalid_api_key",
            }
        },
    ),
    "Embedded: Invalid API key.": (
        {"Content-Type": "application/json; charset=utf-8", "x-request-id": "4f8f61a7d0401e42a6760ea2ca2049f6"},
        401,
        {
            "error": {
                "message": "Incorrect API key provided: DEADBEEF. You can find your API key at https://platform.openai.com/account/api-keys.",
                "type": "invalid_request_error",
                "param": "null",
                "code": "invalid_api_key",
            }
        },
    ),
    "Model does not exist.": (
        {
            "Content-Type": "application/json",
            "x-request-id": "cfdf51fb795362ae578c12a21796262c",
        },
        404,
        {
            "error": {
                "message": "The model `does-not-exist` does not exist",
                "type": "invalid_request_error",
                "param": "null",
                "code": "model_not_found",
            }
        },
    ),
    "No usage data": [
        {
            "content-type": "application/json",
            "openai-model": "gpt-3.5-turbo-0613",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "6326",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "200",
            "x-ratelimit-limit-tokens": "40000",
            "x-ratelimit-limit-tokens_usage_based": "40000",
            "x-ratelimit-remaining-requests": "198",
            "x-ratelimit-remaining-tokens": "39880",
            "x-ratelimit-remaining-tokens_usage_based": "39880",
            "x-ratelimit-reset-requests": "11m32.334s",
            "x-ratelimit-reset-tokens": "180ms",
            "x-ratelimit-reset-tokens_usage_based": "180ms",
            "x-request-id": "f8d0f53b6881c5c0a3698e55f8f410ac",
        },
        200,
        {
            "id": "chatcmpl-8TJ9dS50zgQM7XicE8PLnCyEihRug",
            "object": "chat.completion",
            "created": 1701995833,
            "model": "gpt-3.5-turbo-0613",
            "choices": [
                {
                    "index": 0,
                    "message": {
                        "role": "assistant",
                        "content": "212 degrees Fahrenheit is equal to 100 degrees Celsius.",
                    },
                    "finish_reason": "stop",
                }
            ],
            "usage": None,
            "system_fingerprint": None,
        },
    ],
    "This is an embedding test.": (
        {
            "Content-Type": "application/json",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "54",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "200",
            "x-ratelimit-limit-tokens": "150000",
            "x-ratelimit-remaining-requests": "197",
            "x-ratelimit-remaining-tokens": "149994",
            "x-ratelimit-reset-requests": "19m45.394s",
            "x-ratelimit-reset-tokens": "2ms",
            "x-request-id": "c70828b2293314366a76a2b1dcb20688",
        },
        200,
        {
            "data": [
                {
                    "embedding": " ",
                    "index": 0,
                    "object": "embedding",
                }
            ],
            "model": "text-embedding-ada-002-v2",
            "object": "list",
            "usage": {"prompt_tokens": 6, "total_tokens": 6},
        },
    ),
    "You are a scientist.": (
        {
            "Content-Type": "application/json",
            "openai-model": "gpt-3.5-turbo-0613",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "1469",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "200",
            "x-ratelimit-limit-tokens": "40000",
            "x-ratelimit-remaining-requests": "199",
            "x-ratelimit-remaining-tokens": "39940",
            "x-ratelimit-reset-requests": "7m12s",
            "x-ratelimit-reset-tokens": "90ms",
            "x-request-id": "49dbbffbd3c3f4612aa48def69059ccd",
        },
        200,
        {
            "choices": [
                {
                    "finish_reason": "stop",
                    "index": 0,
                    "message": {
                        "content": "212 degrees " "Fahrenheit is " "equal to 100 " "degrees " "Celsius.",
                        "role": "assistant",
                    },
                }
            ],
            "created": 1696888863,
            "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTemv",
            "model": "gpt-3.5-turbo-0613",
            "object": "chat.completion",
            "usage": {"completion_tokens": 11, "prompt_tokens": 53, "total_tokens": 64},
        },
    ),
    "You are a mathematician.": (
        {
            "Content-Type": "application/json",
            "openai-model": "gpt-3.5-turbo-0613",
            "openai-organization": "new-relic-nkmd8b",
            "openai-processing-ms": "1469",
            "openai-version": "2020-10-01",
            "x-ratelimit-limit-requests": "200",
            "x-ratelimit-limit-tokens": "40000",
            "x-ratelimit-remaining-requests": "199",
            "x-ratelimit-remaining-tokens": "39940",
            "x-ratelimit-reset-requests": "7m12s",
            "x-ratelimit-reset-tokens": "90ms",
            "x-request-id": "49dbbffbd3c3f4612aa48def69059aad",
        },
        200,
        {
            "choices": [
                {
                    "finish_reason": "stop",
                    "index": 0,
                    "message": {
                        "content": "1 plus 2 is 3.",
                        "role": "assistant",
                    },
                }
            ],
            "created": 1696888865,
            "id": "chatcmpl-87sb95K4EF2nuJRcTs43Tm9ntTeat",
            "model": "gpt-3.5-turbo-0613",
            "object": "chat.completion",
            "usage": {"completion_tokens": 11, "prompt_tokens": 53, "total_tokens": 64},
        },
    ),
}


@pytest.fixture(scope="session")
def simple_get(openai_version, extract_shortened_prompt):
    def _simple_get(self):
        content_len = int(self.headers.get("content-length"))
        content = json.loads(self.rfile.read(content_len).decode("utf-8"))
        stream = content.get("stream", False)
        prompt = extract_shortened_prompt(content)
        if not prompt:
            self.send_response(500)
            self.end_headers()
            self.wfile.write("Could not parse prompt.".encode("utf-8"))
            return

        headers, response = ({}, "")

        if openai_version < (1, 0):
            mocked_responses = RESPONSES
            if stream:
                mocked_responses = STREAMED_RESPONSES
        else:
            mocked_responses = RESPONSES_V1
            if stream:
                mocked_responses = STREAMED_RESPONSES_V1

        for k, v in mocked_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(f"Unknown Prompt:\n{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
        if stream and status_code < 400:
            for resp in response:
                data = json.dumps(resp).encode("utf-8")
                if prompt == "Stream parsing error.":
                    # Force a parsing error by writing an invalid streamed response.
                    self.wfile.write(b"data: %s" % data)
                else:
                    self.wfile.write(b"data: %s\n\n" % data)
        else:
            self.wfile.write(json.dumps(response).encode("utf-8"))
        return

    return _simple_get


@pytest.fixture(scope="session")
def MockExternalOpenAIServer(simple_get):
    class _MockExternalOpenAIServer(MockExternalHTTPServer):
        # To use this class in a test one needs to start and stop this server
        # before and after making requests to the test app that makes the external
        # calls.

        def __init__(self, handler=simple_get, port=None, *args, **kwargs):
            super(_MockExternalOpenAIServer, self).__init__(handler=handler, port=port, *args, **kwargs)

    return _MockExternalOpenAIServer


@pytest.fixture(scope="session")
def extract_shortened_prompt(openai_version):
    def _extract_shortened_prompt(content):
        if openai_version < (1, 0):
            prompt = content.get("prompt", None) or content.get("input", None) or content.get("messages")[0]["content"]
        else:
            prompt = content.get("input", None) or content.get("messages")[0]["content"]
        return prompt

    return _extract_shortened_prompt


def get_openai_version():
    # Import OpenAI so that get package version can catpure the version from the
    # system module. OpenAI does not have a package version in v0.
    import openai  # noqa: F401; pylint: disable=W0611

    return get_package_version_tuple("openai")


@pytest.fixture(scope="session")
def openai_version():
    return get_openai_version()


if __name__ == "__main__":
    _MockExternalOpenAIServer = MockExternalOpenAIServer()
    with MockExternalOpenAIServer() as server:
        print(f"MockExternalOpenAIServer serving on port {str(server.port)}")
        while True:
            pass  # Serve forever
