File size: 6,271 Bytes
88c0e85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""Router-level error path tests for OpenAI-compatible payloads."""
from __future__ import annotations

import asyncio

import pytest
from fastapi import HTTPException

from app.core.model_registry import ModelSpec
from app.routers import chat, completions, embeddings, responses
from app.schemas.chat import ChatCompletionRequest
from app.schemas.completions import CompletionRequest
from app.schemas.responses import ResponseRequest


def _raise_key_error(_: str) -> None:
    raise KeyError("unknown")


def test_completions_unknown_model_returns_404_openai_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr("app.routers.completions.get_model_spec", _raise_key_error)
    payload = CompletionRequest.model_validate({"model": "missing", "prompt": "Hi"})

    with pytest.raises(HTTPException) as exc:
        asyncio.run(completions.create_completion(payload))

    assert exc.value.status_code == 404
    assert exc.value.detail["type"] == "model_not_found"
    assert exc.value.detail["param"] == "model"
    assert exc.value.detail["code"] == "model_not_found"


def test_chat_unknown_model_returns_404_openai_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr("app.routers.chat.get_model_spec", _raise_key_error)
    payload = ChatCompletionRequest.model_validate(
        {
            "model": "missing",
            "messages": [{"role": "user", "content": "Hi"}],
        }
    )

    with pytest.raises(HTTPException) as exc:
        asyncio.run(chat.create_chat_completion(payload))

    assert exc.value.status_code == 404
    assert exc.value.detail["type"] == "model_not_found"
    assert exc.value.detail["param"] == "model"
    assert exc.value.detail["code"] == "model_not_found"


def test_responses_unknown_model_returns_404_openai_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr("app.routers.responses.get_model_spec", _raise_key_error)
    payload = ResponseRequest.model_validate({"model": "missing", "input": "Hi"})

    with pytest.raises(HTTPException) as exc:
        asyncio.run(responses.create_response(payload))

    assert exc.value.status_code == 404
    assert exc.value.detail["type"] == "model_not_found"
    assert exc.value.detail["param"] == "model"
    assert exc.value.detail["code"] == "model_not_found"


def test_completions_generation_exception_returns_generation_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    def boom(*_: object, **__: object) -> None:
        raise RuntimeError("boom")

    monkeypatch.setattr("app.routers.completions.get_model_spec", lambda _: None)
    monkeypatch.setattr("app.routers.completions.engine.generate", boom)
    payload = CompletionRequest.model_validate({"model": "GPT3-dev", "prompt": "Hi"})

    with pytest.raises(HTTPException) as exc:
        asyncio.run(completions.create_completion(payload))

    assert exc.value.status_code == 500
    assert exc.value.detail["type"] == "server_error"
    assert exc.value.detail["code"] == "generation_error"
    assert "Generation error:" in exc.value.detail["message"]


def test_chat_generation_exception_returns_generation_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    def boom(*_: object, **__: object) -> None:
        raise RuntimeError("boom")

    monkeypatch.setattr(
        "app.routers.chat.get_model_spec",
        lambda model: ModelSpec(name=model, hf_repo="dummy/instruct", is_instruct=True),
    )
    monkeypatch.setattr("app.routers.chat.engine.apply_chat_template", lambda *_: "prompt")
    monkeypatch.setattr("app.routers.chat.engine.generate", boom)

    payload = ChatCompletionRequest.model_validate(
        {
            "model": "GPT4-dev-177M-1511-Instruct",
            "messages": [{"role": "user", "content": "Hi"}],
        }
    )

    with pytest.raises(HTTPException) as exc:
        asyncio.run(chat.create_chat_completion(payload))

    assert exc.value.status_code == 500
    assert exc.value.detail["type"] == "server_error"
    assert exc.value.detail["code"] == "generation_error"
    assert "Generation error:" in exc.value.detail["message"]


def test_responses_generation_exception_returns_generation_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    def boom(*_: object, **__: object) -> None:
        raise RuntimeError("boom")

    monkeypatch.setattr(
        "app.routers.responses.get_model_spec",
        lambda model: ModelSpec(name=model, hf_repo="dummy/base", is_instruct=False),
    )
    monkeypatch.setattr("app.routers.responses.engine.generate", boom)
    payload = ResponseRequest.model_validate({"model": "GPT3-dev", "input": "Hi"})

    with pytest.raises(HTTPException) as exc:
        asyncio.run(responses.create_response(payload))

    assert exc.value.status_code == 500
    assert exc.value.detail["type"] == "server_error"
    assert exc.value.detail["code"] == "generation_error"
    assert "Generation error:" in exc.value.detail["message"]


def test_responses_structured_input_with_non_instruct_model_returns_400(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr(
        "app.routers.responses.get_model_spec",
        lambda model: ModelSpec(name=model, hf_repo="dummy/base", is_instruct=False),
    )

    payload = ResponseRequest.model_validate(
        {
            "model": "GPT3-dev",
            "input": [{"role": "user", "content": "Hi"}],
        }
    )

    with pytest.raises(HTTPException) as exc:
        asyncio.run(responses.create_response(payload))

    assert exc.value.status_code == 400
    assert exc.value.detail["type"] == "invalid_request_error"
    assert exc.value.detail["param"] == "model"
    assert "not an instruct model" in exc.value.detail["message"]


def test_embeddings_enabled_backend_returns_pending_code(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    class DummySettings:
        enable_embeddings_backend = True

    monkeypatch.setattr("app.routers.embeddings.get_settings", lambda: DummySettings())

    with pytest.raises(HTTPException) as exc:
        asyncio.run(embeddings.create_embeddings())

    assert exc.value.status_code == 501
    assert exc.value.detail["type"] == "not_implemented_error"
    assert exc.value.detail["code"] == "embeddings_backend_pending"