231 lines
6.9 KiB
Python
231 lines
6.9 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from tests.gateway.tools import Gateway, save_yaml
|
|
|
|
BASE_ROUTE = "/api/2.0/endpoints/"
|
|
|
|
|
|
@pytest.fixture
|
|
def basic_config_dict():
|
|
return {
|
|
"endpoints": [
|
|
{
|
|
"name": "completions-gpt4",
|
|
"endpoint_type": "llm/v1/completions",
|
|
"model": {
|
|
"name": "gpt-4",
|
|
"provider": "openai",
|
|
"config": {
|
|
"openai_api_key": "mykey",
|
|
"openai_api_base": "https://api.openai.com/v1",
|
|
"openai_api_version": "2023-05-15",
|
|
"openai_api_type": "openai",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"name": "embeddings-gpt4",
|
|
"endpoint_type": "llm/v1/embeddings",
|
|
"model": {
|
|
"name": "gpt-4",
|
|
"provider": "openai",
|
|
"config": {
|
|
"openai_api_key": "mykey",
|
|
"openai_api_base": "https://api.openai.com/v1",
|
|
"openai_api_version": "2023-05-15",
|
|
"openai_api_type": "openai",
|
|
},
|
|
},
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def basic_routes():
|
|
return [
|
|
{
|
|
"name": "completions-gpt4",
|
|
"endpoint_type": "llm/v1/completions",
|
|
"endpoint_url": "/gateway/completions-gpt4/invocations",
|
|
"model": {
|
|
"name": "gpt-4",
|
|
"provider": "openai",
|
|
},
|
|
"limit": None,
|
|
},
|
|
{
|
|
"name": "embeddings-gpt4",
|
|
"endpoint_type": "llm/v1/embeddings",
|
|
"endpoint_url": "/gateway/embeddings-gpt4/invocations",
|
|
"model": {
|
|
"name": "gpt-4",
|
|
"provider": "openai",
|
|
},
|
|
"limit": None,
|
|
},
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def update_config_dict():
|
|
return {
|
|
"endpoints": [
|
|
{
|
|
"name": "chat-gpt4",
|
|
"endpoint_type": "llm/v1/chat",
|
|
"model": {
|
|
"name": "gpt-4",
|
|
"provider": "openai",
|
|
"config": {
|
|
"openai_api_key": "mykey",
|
|
"openai_api_base": "https://api.openai.com/v1",
|
|
"openai_api_version": "2023-05-15",
|
|
"openai_api_type": "openai",
|
|
},
|
|
},
|
|
"limit": None,
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def update_routes():
|
|
return [
|
|
{
|
|
"name": "chat-gpt4",
|
|
"endpoint_type": "llm/v1/chat",
|
|
"endpoint_url": "/gateway/chat-gpt4/invocations",
|
|
"model": {
|
|
"name": "gpt-4",
|
|
"provider": "openai",
|
|
},
|
|
"limit": None,
|
|
},
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def invalid_config_dict():
|
|
return {
|
|
"endpoints": [
|
|
{
|
|
"invalid_name": "invalid",
|
|
"endpoint_type": "llm/v1/chat",
|
|
"model": {"invalidkey": "invalid", "invalid_provider": "invalid"},
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def test_server_update(
|
|
tmp_path: Path, basic_config_dict, update_config_dict, basic_routes, update_routes
|
|
):
|
|
config = tmp_path / "config.yaml"
|
|
save_yaml(config, basic_config_dict)
|
|
|
|
with Gateway(config) as gateway:
|
|
response = gateway.get(BASE_ROUTE)
|
|
assert response.json()["endpoints"] == basic_routes
|
|
|
|
# push an update to the config file
|
|
save_yaml(config, update_config_dict)
|
|
|
|
# Ensure there is no server downtime
|
|
gateway.assert_health()
|
|
|
|
# Wait for the app to restart
|
|
gateway.wait_reload()
|
|
response = gateway.get(BASE_ROUTE)
|
|
|
|
assert response.json()["endpoints"] == update_routes
|
|
|
|
# push the original file back
|
|
save_yaml(config, basic_config_dict)
|
|
gateway.assert_health()
|
|
gateway.wait_reload()
|
|
response = gateway.get(BASE_ROUTE)
|
|
assert response.json()["endpoints"] == basic_routes
|
|
|
|
|
|
def test_server_update_with_invalid_config(
|
|
tmp_path: Path, basic_config_dict, invalid_config_dict, basic_routes
|
|
):
|
|
config = tmp_path / "config.yaml"
|
|
save_yaml(config, basic_config_dict)
|
|
|
|
with Gateway(config) as gateway:
|
|
response = gateway.get(BASE_ROUTE)
|
|
assert response.json()["endpoints"] == basic_routes
|
|
# Give filewatch a moment to cycle
|
|
gateway.wait_reload()
|
|
# push an invalid config
|
|
save_yaml(config, invalid_config_dict)
|
|
gateway.assert_health()
|
|
# ensure that filewatch has run through the aborted config change logic
|
|
gateway.wait_reload()
|
|
gateway.assert_health()
|
|
response = gateway.get(BASE_ROUTE)
|
|
assert response.json()["endpoints"] == basic_routes
|
|
|
|
|
|
def test_server_update_config_removed_then_recreated(
|
|
tmp_path: Path, basic_config_dict, basic_routes
|
|
):
|
|
config = tmp_path / "config.yaml"
|
|
save_yaml(config, basic_config_dict)
|
|
|
|
with Gateway(config) as gateway:
|
|
response = gateway.get(BASE_ROUTE)
|
|
assert response.json()["endpoints"] == basic_routes
|
|
# Give filewatch a moment to cycle
|
|
gateway.wait_reload()
|
|
# remove config
|
|
config.unlink()
|
|
gateway.wait_reload()
|
|
gateway.assert_health()
|
|
|
|
save_yaml(config, {"endpoints": basic_config_dict["endpoints"][1:]})
|
|
gateway.wait_reload()
|
|
response = gateway.get(BASE_ROUTE)
|
|
assert response.json()["endpoints"] == basic_routes[1:]
|
|
|
|
|
|
def test_server_static_endpoints(tmp_path, basic_config_dict, basic_routes):
|
|
config = tmp_path / "config.yaml"
|
|
save_yaml(config, basic_config_dict)
|
|
|
|
with Gateway(config) as gateway:
|
|
response = gateway.get(BASE_ROUTE)
|
|
assert response.json()["endpoints"] == basic_routes
|
|
|
|
for route in ["docs", "redoc"]:
|
|
response = gateway.get(route)
|
|
assert response.status_code == 200
|
|
|
|
for index, route in enumerate(basic_config_dict["endpoints"]):
|
|
response = gateway.get(f"{BASE_ROUTE}{route['name']}")
|
|
assert response.json() == basic_routes[index]
|
|
|
|
|
|
def test_request_invalid_route(tmp_path, basic_config_dict):
|
|
config = tmp_path / "config.yaml"
|
|
save_yaml(config, basic_config_dict)
|
|
|
|
with Gateway(config) as gateway:
|
|
# Test get
|
|
response = gateway.get(f"{BASE_ROUTE}invalid/")
|
|
assert response.status_code == 404
|
|
assert response.json() == {
|
|
"detail": "The endpoint 'invalid' is not present or active on the server. Please "
|
|
"verify the endpoint name."
|
|
}
|
|
|
|
# Test post
|
|
response = gateway.post(f"{BASE_ROUTE}invalid", json={"input": "should fail"})
|
|
assert response.status_code == 405
|
|
assert response.json() == {"detail": "Method Not Allowed"}
|