|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -import importlib |
4 | | -from importlib.machinery import ModuleSpec |
5 | | -import sys |
6 | | -import types |
7 | 3 | from typing import cast |
8 | 4 |
|
9 | 5 | import pytest |
10 | | -from pydantic import BaseModel, ConfigDict |
11 | | - |
12 | | - |
13 | | -def _install_dependency_stubs() -> None: |
14 | | - """Register lightweight stubs for optional runtime dependencies.""" |
15 | | - |
16 | | - def _ensure_module(name: str, **attrs) -> None: |
17 | | - if name in sys.modules: |
18 | | - return |
19 | | - module = types.ModuleType(name) |
20 | | - for key, value in attrs.items(): |
21 | | - setattr(module, key, value) |
22 | | - sys.modules[name] = module |
23 | | - |
24 | | - try: # pragma: no cover - prefer real dependency when available |
25 | | - importlib.import_module("loguru") |
26 | | - except ModuleNotFoundError: |
27 | | - |
28 | | - class _Logger: # pragma: no cover - inert logging shim |
29 | | - def __getattr__(self, _name: str): |
30 | | - def _noop(*_args, **_kwargs): |
31 | | - return None |
32 | | - |
33 | | - return _noop |
34 | | - |
35 | | - _ensure_module("loguru", logger=_Logger()) |
36 | | - |
37 | | - def _noop_loader(*_args, **_kwargs): # pragma: no cover - placeholder loader |
38 | | - return {} |
39 | | - |
40 | | - def _field_type(name: str): |
41 | | - def __init__(self, *_args, **_kwargs): |
42 | | - return None |
43 | | - |
44 | | - return type(name, (), {"__init__": __init__}) |
45 | | - |
46 | | - class _SqliteDatabase: |
47 | | - def __init__(self, *_args, **_kwargs): |
48 | | - self.path = None |
49 | | - |
50 | | - def connect(self): # pragma: no cover - stub connection |
51 | | - return None |
52 | | - |
53 | | - def close(self): # pragma: no cover |
54 | | - return None |
55 | | - |
56 | | - def atomic(self): # pragma: no cover - context manager shim |
57 | | - class _Atomic: |
58 | | - def __enter__(self_inner): |
59 | | - return self_inner |
60 | | - |
61 | | - def __exit__(self_inner, *_exc): |
62 | | - return False |
63 | | - |
64 | | - return _Atomic() |
65 | | - |
66 | | - def create_tables(self, *_args, **_kwargs): # pragma: no cover |
67 | | - return None |
68 | | - |
69 | | - def create_table(self, *_args, **_kwargs): # pragma: no cover |
70 | | - return None |
71 | | - |
72 | | - def drop_tables(self, *_args, **_kwargs): # pragma: no cover |
73 | | - return None |
74 | | - |
75 | | - optional_stub_attrs = { |
76 | | - "toml": {"loads": _noop_loader, "load": _noop_loader}, |
77 | | - "datasets": {}, |
78 | | - "addict": {"Dict": dict}, |
79 | | - "deepdiff": {"DeepDiff": type("DeepDiff", (), {})}, |
80 | | - "peewee": { |
81 | | - "Model": type("Model", (), {}), |
82 | | - "SqliteDatabase": _SqliteDatabase, |
83 | | - "CharField": _field_type("CharField"), |
84 | | - "TextField": _field_type("TextField"), |
85 | | - "IntegerField": _field_type("IntegerField"), |
86 | | - "DateTimeField": _field_type("DateTimeField"), |
87 | | - "AutoField": _field_type("AutoField"), |
88 | | - "OperationalError": Exception, |
89 | | - }, |
90 | | - "backoff": {}, |
91 | | - "aiohttp": {"ClientSession": type("ClientSession", (), {})}, |
92 | | - "tqdm": {"tqdm": lambda iterable, *_args, **_kwargs: iterable}, |
93 | | - } |
94 | | - |
95 | | - for optional_module, attrs in optional_stub_attrs.items(): |
96 | | - try: |
97 | | - importlib.import_module(optional_module) |
98 | | - except ModuleNotFoundError: |
99 | | - _ensure_module(optional_module, **attrs) |
100 | | - |
101 | | - try: |
102 | | - importlib.import_module("litellm") |
103 | | - except ModuleNotFoundError: |
104 | | - litellm_mod = types.ModuleType("litellm") |
105 | | - |
106 | | - def _acompletion(*_args, **_kwargs): # pragma: no cover - stubbed async function |
107 | | - return None |
108 | | - |
109 | | - def _completion_cost(*_args, **_kwargs): # pragma: no cover - cost shim |
110 | | - return 0.0 |
111 | | - |
112 | | - litellm_mod.acompletion = _acompletion |
113 | | - litellm_mod.completion = _acompletion |
114 | | - litellm_mod.completion_cost = _completion_cost |
115 | | - |
116 | | - caching_pkg = types.ModuleType("litellm.caching") |
117 | | - caching_submodule = types.ModuleType("litellm.caching.caching") |
118 | | - caching_submodule.Cache = type("Cache", (), {}) |
119 | | - dual_cache_module = types.ModuleType("litellm.caching.dual_cache") |
120 | | - dual_cache_module.DualCache = type("DualCache", (), {}) |
121 | | - in_memory_cache_module = types.ModuleType("litellm.caching.in_memory_cache") |
122 | | - in_memory_cache_module.InMemoryCache = type("InMemoryCache", (), {}) |
123 | | - caching_pkg.caching = caching_submodule |
124 | | - caching_pkg.dual_cache = dual_cache_module |
125 | | - caching_pkg.in_memory_cache = in_memory_cache_module |
126 | | - redis_cache_module = types.ModuleType("litellm.caching.redis_cache") |
127 | | - redis_cache_module.RedisCache = type("RedisCache", (), {}) |
128 | | - caching_pkg.redis_cache = redis_cache_module |
129 | | - |
130 | | - litellm_mod.caching = caching_pkg |
131 | | - |
132 | | - main_module = types.ModuleType("litellm.main") |
133 | | - main_module.ModelResponse = type("ModelResponse", (), {}) |
134 | | - main_module.Usage = type("Usage", (), {}) |
135 | | - |
136 | | - cost_calculator_mod = types.ModuleType("litellm.cost_calculator") |
137 | | - cost_calculator_mod.cost_per_token = lambda *_args, **_kwargs: 0.0 |
138 | | - |
139 | | - sys.modules["litellm"] = litellm_mod |
140 | | - sys.modules["litellm.caching"] = caching_pkg |
141 | | - sys.modules["litellm.caching.caching"] = caching_submodule |
142 | | - sys.modules["litellm.caching.dual_cache"] = dual_cache_module |
143 | | - sys.modules["litellm.caching.in_memory_cache"] = in_memory_cache_module |
144 | | - sys.modules["litellm.caching.redis_cache"] = redis_cache_module |
145 | | - sys.modules["litellm.main"] = main_module |
146 | | - sys.modules["litellm.cost_calculator"] = cost_calculator_mod |
147 | | - |
148 | | - try: |
149 | | - importlib.import_module("playhouse.sqlite_ext") |
150 | | - except ModuleNotFoundError: |
151 | | - playhouse_mod = types.ModuleType("playhouse") |
152 | | - sqlite_ext_mod = types.ModuleType("playhouse.sqlite_ext") |
153 | | - sqlite_ext_mod.JSONField = type("JSONField", (), {}) |
154 | | - playhouse_mod.sqlite_ext = sqlite_ext_mod |
155 | | - |
156 | | - sys.modules["playhouse"] = playhouse_mod |
157 | | - sys.modules["playhouse.sqlite_ext"] = sqlite_ext_mod |
158 | | - |
159 | | - try: |
160 | | - importlib.import_module("openai") |
161 | | - return |
162 | | - except ModuleNotFoundError: |
163 | | - pass |
164 | | - |
165 | | - openai_mod = types.ModuleType("openai") |
166 | | - types_mod = types.ModuleType("openai.types") |
167 | | - completion_usage_mod = types.ModuleType("openai.types.completion_usage") |
168 | | - chat_mod = types.ModuleType("openai.types.chat") |
169 | | - chat_message_mod = types.ModuleType("openai.types.chat.chat_completion_message") |
170 | | - chat_message_param_mod = types.ModuleType("openai.types.chat.chat_completion_message_param") |
171 | | - tool_call_mod = types.ModuleType("openai.types.chat.chat_completion_message_tool_call") |
172 | | - |
173 | | - class CompletionUsage(BaseModel): # pragma: no cover - simple data container |
174 | | - prompt_tokens: int | None = None |
175 | | - completion_tokens: int | None = None |
176 | | - total_tokens: int | None = None |
177 | | - |
178 | | - model_config = ConfigDict(extra="allow") |
179 | | - |
180 | | - class FunctionCall(BaseModel): # pragma: no cover - simple data container |
181 | | - name: str | None = None |
182 | | - arguments: str | None = None |
183 | | - |
184 | | - model_config = ConfigDict(extra="allow") |
185 | | - |
186 | | - class FunctionDefinition(BaseModel): # pragma: no cover - simple data container |
187 | | - name: str | None = None |
188 | | - description: str | None = None |
189 | | - parameters: dict[str, Any] | None = None |
190 | | - |
191 | | - model_config = ConfigDict(extra="allow") |
192 | | - |
193 | | - class ChatCompletionContentPartTextParam(BaseModel): # pragma: no cover - simple data container |
194 | | - text: str | None = None |
195 | | - type: str = "text" |
196 | | - |
197 | | - model_config = ConfigDict(extra="allow") |
198 | | - |
199 | | - class ChatCompletionMessageToolCall(BaseModel): # pragma: no cover - simple data container |
200 | | - id: str | None = None |
201 | | - type: str | None = None |
202 | | - function: FunctionCall | None = None |
203 | | - |
204 | | - model_config = ConfigDict(extra="allow") |
205 | | - |
206 | | - class ChatCompletionMessageParam(BaseModel): # pragma: no cover - simple data container |
207 | | - content: str | None = None |
208 | | - role: str | None = None |
209 | | - |
210 | | - model_config = ConfigDict(extra="allow") |
211 | | - |
212 | | - class _NotGiven: # pragma: no cover - sentinel placeholder |
213 | | - pass |
214 | | - |
215 | | - types_mod.CompletionUsage = CompletionUsage |
216 | | - completion_usage_mod.CompletionUsage = CompletionUsage |
217 | | - chat_message_mod.FunctionCall = FunctionCall |
218 | | - chat_message_param_mod.ChatCompletionMessageParam = ChatCompletionMessageParam |
219 | | - tool_call_mod.ChatCompletionMessageToolCall = ChatCompletionMessageToolCall |
220 | | - chat_mod.ChatCompletionContentPartTextParam = ChatCompletionContentPartTextParam |
221 | | - types_mod.FunctionDefinition = FunctionDefinition |
222 | | - |
223 | | - openai_mod.__spec__ = ModuleSpec("openai", loader=None) |
224 | | - types_mod.__spec__ = ModuleSpec("openai.types", loader=None) |
225 | | - completion_usage_mod.__spec__ = ModuleSpec("openai.types.completion_usage", loader=None) |
226 | | - chat_mod.__spec__ = ModuleSpec("openai.types.chat", loader=None) |
227 | | - chat_message_mod.__spec__ = ModuleSpec("openai.types.chat.chat_completion_message", loader=None) |
228 | | - chat_message_param_mod.__spec__ = ModuleSpec("openai.types.chat.chat_completion_message_param", loader=None) |
229 | | - tool_call_mod.__spec__ = ModuleSpec("openai.types.chat.chat_completion_message_tool_call", loader=None) |
230 | | - |
231 | | - openai_mod.types = types_mod |
232 | | - openai_mod.NotGiven = _NotGiven |
233 | | - openai_mod.NOT_GIVEN = _NotGiven() |
234 | | - types_mod.completion_usage = completion_usage_mod |
235 | | - types_mod.chat = chat_mod |
236 | | - chat_mod.chat_completion_message = chat_message_mod |
237 | | - chat_mod.chat_completion_message_tool_call = tool_call_mod |
238 | | - chat_mod.chat_completion_message_param = chat_message_param_mod |
239 | | - |
240 | | - sys.modules["openai"] = openai_mod |
241 | | - sys.modules["openai.types"] = types_mod |
242 | | - sys.modules["openai.types.completion_usage"] = completion_usage_mod |
243 | | - sys.modules["openai.types.chat"] = chat_mod |
244 | | - sys.modules["openai.types.chat.chat_completion_message"] = chat_message_mod |
245 | | - sys.modules["openai.types.chat.chat_completion_message_tool_call"] = tool_call_mod |
246 | | - sys.modules["openai.types.chat.chat_completion_message_param"] = chat_message_param_mod |
247 | | - |
248 | | - |
249 | | -_install_dependency_stubs() |
250 | 6 |
|
251 | 7 | from eval_protocol.models import EvaluationRow, Message |
252 | 8 | from eval_protocol.pytest.dataset_preparation import load_and_prepare_rows |
|
0 commit comments