modelsdotdev 0.20260514.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modelsdotdev/.gitignore +1 -0
- modelsdotdev/__init__.py +43 -0
- modelsdotdev/_db.sqlite +0 -0
- modelsdotdev/_internal/__init__.py +0 -0
- modelsdotdev/_internal/data.py +557 -0
- modelsdotdev/_internal/dist.py +261 -0
- modelsdotdev/_internal/schema.py +205 -0
- modelsdotdev/_internal/sync.py +495 -0
- modelsdotdev/py.typed +0 -0
- modelsdotdev-0.20260514.0.dist-info/METADATA +61 -0
- modelsdotdev-0.20260514.0.dist-info/RECORD +13 -0
- modelsdotdev-0.20260514.0.dist-info/WHEEL +4 -0
- modelsdotdev-0.20260514.0.dist-info/licenses/LICENSE +22 -0
modelsdotdev/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
_db.sqlite
|
modelsdotdev/__init__.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Typed offline interface for an offline models.dev database."""
|
|
2
|
+
|
|
3
|
+
from modelsdotdev._internal.data import (
|
|
4
|
+
Capability,
|
|
5
|
+
Cost,
|
|
6
|
+
ExperimentalMode,
|
|
7
|
+
Limits,
|
|
8
|
+
Modalities,
|
|
9
|
+
Modality,
|
|
10
|
+
Model,
|
|
11
|
+
ModelProviderConfig,
|
|
12
|
+
ModelRef,
|
|
13
|
+
Provider,
|
|
14
|
+
ProviderAPIShape,
|
|
15
|
+
Status,
|
|
16
|
+
get_model_by_id,
|
|
17
|
+
get_provider_by_id,
|
|
18
|
+
get_provider_by_name,
|
|
19
|
+
iter_models,
|
|
20
|
+
iter_providers,
|
|
21
|
+
parse_model_id,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"Capability",
|
|
26
|
+
"Cost",
|
|
27
|
+
"ExperimentalMode",
|
|
28
|
+
"Limits",
|
|
29
|
+
"Modalities",
|
|
30
|
+
"Modality",
|
|
31
|
+
"Model",
|
|
32
|
+
"ModelProviderConfig",
|
|
33
|
+
"ModelRef",
|
|
34
|
+
"Provider",
|
|
35
|
+
"ProviderAPIShape",
|
|
36
|
+
"Status",
|
|
37
|
+
"get_model_by_id",
|
|
38
|
+
"get_provider_by_id",
|
|
39
|
+
"get_provider_by_name",
|
|
40
|
+
"iter_models",
|
|
41
|
+
"iter_providers",
|
|
42
|
+
"parse_model_id",
|
|
43
|
+
]
|
modelsdotdev/_db.sqlite
ADDED
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sqlite3
|
|
6
|
+
from contextlib import closing
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from enum import StrEnum
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING, cast
|
|
11
|
+
|
|
12
|
+
from modelsdotdev._internal.schema import MODEL_COLUMNS, PROVIDER_COLUMNS
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Iterator
|
|
16
|
+
|
|
17
|
+
type JsonValue = str | int | float | bool | JsonObject | JsonArray | None
|
|
18
|
+
"""JSON scalar, object, or array value."""
|
|
19
|
+
|
|
20
|
+
type JsonObject = dict[str, JsonValue]
|
|
21
|
+
"""JSON object payload."""
|
|
22
|
+
|
|
23
|
+
type JsonArray = list[JsonValue]
|
|
24
|
+
"""JSON array payload."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Capability(StrEnum):
|
|
28
|
+
"""Model capability flag."""
|
|
29
|
+
|
|
30
|
+
ATTACHMENT = "attachment"
|
|
31
|
+
REASONING = "reasoning"
|
|
32
|
+
STRUCTURED_OUTPUT = "structured_output"
|
|
33
|
+
TEMPERATURE = "temperature"
|
|
34
|
+
TOOL_CALL = "tool_call"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Modality(StrEnum):
|
|
38
|
+
"""Supported input/output modality."""
|
|
39
|
+
|
|
40
|
+
TEXT = "text"
|
|
41
|
+
AUDIO = "audio"
|
|
42
|
+
IMAGE = "image"
|
|
43
|
+
VIDEO = "video"
|
|
44
|
+
PDF = "pdf"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ProviderAPIShape(StrEnum):
|
|
48
|
+
"""Provider API shape override."""
|
|
49
|
+
|
|
50
|
+
RESPONSES = "responses"
|
|
51
|
+
COMPLETIONS = "completions"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Status(StrEnum):
|
|
55
|
+
"""Model lifecycle status."""
|
|
56
|
+
|
|
57
|
+
ALPHA = "alpha"
|
|
58
|
+
BETA = "beta"
|
|
59
|
+
DEPRECATED = "deprecated"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
63
|
+
class Cost:
|
|
64
|
+
"""Token and media pricing tier."""
|
|
65
|
+
|
|
66
|
+
input: float
|
|
67
|
+
"""Input token cost."""
|
|
68
|
+
|
|
69
|
+
output: float
|
|
70
|
+
"""Output token cost."""
|
|
71
|
+
|
|
72
|
+
min_context: int = 0
|
|
73
|
+
"""Minimum context size where this pricing applies."""
|
|
74
|
+
|
|
75
|
+
reasoning: float | None = None
|
|
76
|
+
"""Reasoning token cost."""
|
|
77
|
+
|
|
78
|
+
cache_read: float | None = None
|
|
79
|
+
"""Cache-read token cost."""
|
|
80
|
+
|
|
81
|
+
cache_write: float | None = None
|
|
82
|
+
"""Cache-write token cost."""
|
|
83
|
+
|
|
84
|
+
input_audio: float | None = None
|
|
85
|
+
"""Audio input cost."""
|
|
86
|
+
|
|
87
|
+
output_audio: float | None = None
|
|
88
|
+
"""Audio output cost."""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
92
|
+
class Limits:
|
|
93
|
+
"""Token limits."""
|
|
94
|
+
|
|
95
|
+
context: int
|
|
96
|
+
"""Context window."""
|
|
97
|
+
|
|
98
|
+
output: int
|
|
99
|
+
"""Maximum output tokens."""
|
|
100
|
+
|
|
101
|
+
input: int | None = None
|
|
102
|
+
"""Maximum input tokens."""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
106
|
+
class Modalities:
|
|
107
|
+
"""Input and output media support."""
|
|
108
|
+
|
|
109
|
+
input: tuple[Modality, ...]
|
|
110
|
+
"""Accepted input modalities."""
|
|
111
|
+
|
|
112
|
+
output: tuple[Modality, ...]
|
|
113
|
+
"""Produced output modalities."""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
117
|
+
class ModelProviderConfig:
|
|
118
|
+
"""Model-specific provider override."""
|
|
119
|
+
|
|
120
|
+
npm: str | None = None
|
|
121
|
+
"""AI SDK provider package."""
|
|
122
|
+
|
|
123
|
+
api: str | None = None
|
|
124
|
+
"""Provider API base URL."""
|
|
125
|
+
|
|
126
|
+
api_shape: ProviderAPIShape | None = None
|
|
127
|
+
"""Request/response shape."""
|
|
128
|
+
|
|
129
|
+
body: JsonObject | None = None
|
|
130
|
+
"""Extra request body."""
|
|
131
|
+
|
|
132
|
+
headers: dict[str, str] | None = None
|
|
133
|
+
"""Extra request headers."""
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
137
|
+
class ModelRef:
|
|
138
|
+
"""Parsed model identifier."""
|
|
139
|
+
|
|
140
|
+
provider_id: str | None
|
|
141
|
+
"""Provider ID, if the model ID is provider-qualified."""
|
|
142
|
+
|
|
143
|
+
vendor_id: str | None
|
|
144
|
+
"""Model producer ID, if known."""
|
|
145
|
+
|
|
146
|
+
model_id: str
|
|
147
|
+
"""Provider-specific model ID."""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
151
|
+
class ExperimentalMode:
|
|
152
|
+
"""Experimental model mode."""
|
|
153
|
+
|
|
154
|
+
cost: list[Cost] | None = None
|
|
155
|
+
"""Mode-specific pricing tiers."""
|
|
156
|
+
|
|
157
|
+
provider: ModelProviderConfig | None = None
|
|
158
|
+
"""Mode provider override."""
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
162
|
+
class Provider:
|
|
163
|
+
"""Model provider."""
|
|
164
|
+
|
|
165
|
+
id: str
|
|
166
|
+
"""Stable provider ID."""
|
|
167
|
+
|
|
168
|
+
name: str
|
|
169
|
+
"""Display name."""
|
|
170
|
+
|
|
171
|
+
env: tuple[str, ...]
|
|
172
|
+
"""Environment variable names."""
|
|
173
|
+
|
|
174
|
+
npm: str
|
|
175
|
+
"""AI SDK provider package."""
|
|
176
|
+
|
|
177
|
+
doc: str
|
|
178
|
+
"""Model documentation URL."""
|
|
179
|
+
|
|
180
|
+
api: str | None = None
|
|
181
|
+
"""Provider API base URL."""
|
|
182
|
+
|
|
183
|
+
def get_model_by_id(self, model_id: str) -> Model | None:
|
|
184
|
+
return _get_model_by_provider_id(self.id, model_id)
|
|
185
|
+
|
|
186
|
+
def iter_models(self) -> Iterator[Model]:
|
|
187
|
+
return _iter_models_for_provider_id(self.id)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
191
|
+
class Model:
|
|
192
|
+
"""Provider-hosted model."""
|
|
193
|
+
|
|
194
|
+
provider_id: str
|
|
195
|
+
"""Owning provider ID."""
|
|
196
|
+
|
|
197
|
+
id: str
|
|
198
|
+
"""Provider-local model ID."""
|
|
199
|
+
|
|
200
|
+
name: str
|
|
201
|
+
"""Display name."""
|
|
202
|
+
|
|
203
|
+
capabilities: frozenset[Capability]
|
|
204
|
+
"""Supported model capabilities."""
|
|
205
|
+
|
|
206
|
+
modalities: Modalities
|
|
207
|
+
"""Input/output modalities."""
|
|
208
|
+
|
|
209
|
+
open_weights: bool
|
|
210
|
+
"""Whether weights are open."""
|
|
211
|
+
|
|
212
|
+
limits: Limits
|
|
213
|
+
"""Token limits."""
|
|
214
|
+
|
|
215
|
+
family: str | None = None
|
|
216
|
+
"""Model family."""
|
|
217
|
+
|
|
218
|
+
interleaved_field: str | None = None
|
|
219
|
+
"""Interleaving field, if specified."""
|
|
220
|
+
|
|
221
|
+
knowledge_cutoff: str | None = None
|
|
222
|
+
"""Knowledge cutoff."""
|
|
223
|
+
|
|
224
|
+
cost: list[Cost] | None = None
|
|
225
|
+
"""Pricing tiers."""
|
|
226
|
+
|
|
227
|
+
status: Status | None = None
|
|
228
|
+
"""Lifecycle status."""
|
|
229
|
+
|
|
230
|
+
experimental_modes: dict[str, ExperimentalMode] | None = None
|
|
231
|
+
"""Experimental modes keyed by name."""
|
|
232
|
+
|
|
233
|
+
provider_config: ModelProviderConfig | None = None
|
|
234
|
+
"""Provider override."""
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def qualified_id(self) -> str:
|
|
238
|
+
return f"{self.provider_id}:{self.id}"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
DATABASE_PATH_ENV = "MODELDOTDEV_DATABASE_PATH"
|
|
242
|
+
DB_PATH = Path(__file__).parents[1] / "_db.sqlite"
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def get_provider_by_name(name: str) -> Provider | None:
|
|
246
|
+
"""Return a provider by display name, using case-insensitive matching."""
|
|
247
|
+
with closing(_connect()) as connection:
|
|
248
|
+
row = connection.execute(
|
|
249
|
+
f"SELECT {PROVIDER_COLUMNS} FROM providers "
|
|
250
|
+
"WHERE name = ? COLLATE NOCASE",
|
|
251
|
+
(name,),
|
|
252
|
+
).fetchone()
|
|
253
|
+
return None if row is None else _provider_from_row(row)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def get_provider_by_id(provider_id: str) -> Provider | None:
|
|
257
|
+
with closing(_connect()) as connection:
|
|
258
|
+
row = connection.execute(
|
|
259
|
+
f"SELECT {PROVIDER_COLUMNS} FROM providers WHERE id = ?",
|
|
260
|
+
(provider_id,),
|
|
261
|
+
).fetchone()
|
|
262
|
+
return None if row is None else _provider_from_row(row)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def parse_model_id(model_id: str) -> ModelRef:
|
|
266
|
+
"""Parse a possibly provider-qualified model ID."""
|
|
267
|
+
if not model_id:
|
|
268
|
+
raise ValueError("model_id must not be empty")
|
|
269
|
+
|
|
270
|
+
for separator in (":", "/"):
|
|
271
|
+
if separator not in model_id:
|
|
272
|
+
continue
|
|
273
|
+
provider_id, provider_model_id = model_id.split(separator, 1)
|
|
274
|
+
if not provider_id or not provider_model_id:
|
|
275
|
+
raise ValueError("model_id must include provider and model IDs")
|
|
276
|
+
if get_provider_by_id(provider_id) is not None:
|
|
277
|
+
return ModelRef(
|
|
278
|
+
provider_id=provider_id,
|
|
279
|
+
vendor_id=None,
|
|
280
|
+
model_id=provider_model_id,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
return ModelRef(provider_id=None, vendor_id=None, model_id=model_id)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def get_model_by_id(model_id: str) -> Model | None:
|
|
287
|
+
"""Return a model by canonical ``provider:model`` ID."""
|
|
288
|
+
if ":" not in model_id:
|
|
289
|
+
raise ValueError("model_id must be in 'provider:model' format")
|
|
290
|
+
|
|
291
|
+
with closing(_connect()) as connection:
|
|
292
|
+
row = connection.execute(
|
|
293
|
+
f"SELECT {MODEL_COLUMNS} FROM models WHERE full_id = ?",
|
|
294
|
+
(model_id,),
|
|
295
|
+
).fetchone()
|
|
296
|
+
return None if row is None else _model_from_row(connection, row)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def iter_providers() -> Iterator[Provider]:
|
|
300
|
+
with closing(_connect()) as connection:
|
|
301
|
+
providers = tuple(
|
|
302
|
+
_provider_from_row(row)
|
|
303
|
+
for row in connection.execute(
|
|
304
|
+
f"SELECT {PROVIDER_COLUMNS} FROM providers "
|
|
305
|
+
"ORDER BY name COLLATE NOCASE",
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
return iter(providers)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def iter_models() -> Iterator[Model]:
|
|
312
|
+
with closing(_connect()) as connection:
|
|
313
|
+
models = tuple(
|
|
314
|
+
_model_from_row(connection, row)
|
|
315
|
+
for row in connection.execute(
|
|
316
|
+
f"SELECT {MODEL_COLUMNS} FROM models ORDER BY full_id",
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
return iter(models)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _iter_models_for_provider_id(provider_id: str) -> Iterator[Model]:
|
|
323
|
+
with closing(_connect()) as connection:
|
|
324
|
+
models = tuple(
|
|
325
|
+
_model_from_row(connection, row)
|
|
326
|
+
for row in connection.execute(
|
|
327
|
+
f"""
|
|
328
|
+
SELECT {MODEL_COLUMNS}
|
|
329
|
+
FROM models
|
|
330
|
+
WHERE provider_id = ?
|
|
331
|
+
ORDER BY id
|
|
332
|
+
""",
|
|
333
|
+
(provider_id,),
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
return iter(models)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _get_model_by_provider_id(provider_id: str, model_id: str) -> Model | None:
|
|
340
|
+
with closing(_connect()) as connection:
|
|
341
|
+
row = connection.execute(
|
|
342
|
+
f"""
|
|
343
|
+
SELECT {MODEL_COLUMNS}
|
|
344
|
+
FROM models
|
|
345
|
+
WHERE provider_id = ? AND id = ?
|
|
346
|
+
""",
|
|
347
|
+
(provider_id, model_id),
|
|
348
|
+
).fetchone()
|
|
349
|
+
return None if row is None else _model_from_row(connection, row)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _connect() -> sqlite3.Connection:
|
|
353
|
+
db_path = _database_path()
|
|
354
|
+
if not db_path.is_file():
|
|
355
|
+
raise FileNotFoundError(_missing_database_message(db_path))
|
|
356
|
+
connection = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
|
357
|
+
connection.row_factory = sqlite3.Row
|
|
358
|
+
return connection
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _database_path() -> Path:
|
|
362
|
+
if db_path := os.environ.get(DATABASE_PATH_ENV):
|
|
363
|
+
return Path(db_path).expanduser()
|
|
364
|
+
return DB_PATH
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _missing_database_message(db_path: Path) -> str:
|
|
368
|
+
message = f"modelsdotdev database not found at {db_path}"
|
|
369
|
+
if os.environ.get(DATABASE_PATH_ENV):
|
|
370
|
+
return f"{message}; check {DATABASE_PATH_ENV} or unset it"
|
|
371
|
+
if _source_checkout_root() is not None:
|
|
372
|
+
return f"{message}; run `uv run poe generate-db` to create it"
|
|
373
|
+
return f"{message}; reinstall the package or report missing package data"
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _source_checkout_root() -> Path | None:
|
|
377
|
+
root = Path(__file__).parents[3]
|
|
378
|
+
if (root / "pyproject.toml").is_file() and (
|
|
379
|
+
root / "src" / "modelsdotdev"
|
|
380
|
+
).is_dir():
|
|
381
|
+
return root
|
|
382
|
+
return None
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _provider_from_row(row: sqlite3.Row) -> Provider:
|
|
386
|
+
provider_id = cast("str", row["id"])
|
|
387
|
+
return Provider(
|
|
388
|
+
id=provider_id,
|
|
389
|
+
name=cast("str", row["name"]),
|
|
390
|
+
env=tuple(cast("str", row["env"]).split(";")),
|
|
391
|
+
npm=cast("str", row["npm"]),
|
|
392
|
+
api=cast("str | None", row["api"]),
|
|
393
|
+
doc=cast("str", row["doc"]),
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _model_from_row(
|
|
398
|
+
connection: sqlite3.Connection,
|
|
399
|
+
row: sqlite3.Row,
|
|
400
|
+
) -> Model:
|
|
401
|
+
full_id = cast("str", row["full_id"])
|
|
402
|
+
return Model(
|
|
403
|
+
provider_id=cast("str", row["provider_id"]),
|
|
404
|
+
id=cast("str", row["id"]),
|
|
405
|
+
name=cast("str", row["name"]),
|
|
406
|
+
family=cast("str | None", row["family"]),
|
|
407
|
+
capabilities=_capabilities(row),
|
|
408
|
+
interleaved_field=_interleaved_field(row),
|
|
409
|
+
knowledge_cutoff=cast("str | None", row["knowledge"]),
|
|
410
|
+
modalities=_modalities(connection, full_id),
|
|
411
|
+
open_weights=bool(row["open_weights"]),
|
|
412
|
+
cost=_cost(connection, full_id, None),
|
|
413
|
+
limits=Limits(
|
|
414
|
+
context=cast("int", row["limit_context"]),
|
|
415
|
+
input=cast("int | None", row["limit_input"]),
|
|
416
|
+
output=cast("int", row["limit_output"]),
|
|
417
|
+
),
|
|
418
|
+
status=_status(row),
|
|
419
|
+
experimental_modes=_experimental_modes(connection, full_id),
|
|
420
|
+
provider_config=_provider_config(row, "provider"),
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _capabilities(row: sqlite3.Row) -> frozenset[Capability]:
|
|
425
|
+
capabilities: set[Capability] = set()
|
|
426
|
+
for capability in Capability:
|
|
427
|
+
if row[capability.value]:
|
|
428
|
+
capabilities.add(capability)
|
|
429
|
+
return frozenset(capabilities)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _status(row: sqlite3.Row) -> Status | None:
|
|
433
|
+
status = row["status"]
|
|
434
|
+
return None if status is None else Status(cast("str", status))
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def _modalities(connection: sqlite3.Connection, full_id: str) -> Modalities:
|
|
438
|
+
rows = connection.execute(
|
|
439
|
+
"""
|
|
440
|
+
SELECT direction, value FROM model_modalities
|
|
441
|
+
WHERE model_full_id = ?
|
|
442
|
+
ORDER BY direction, position
|
|
443
|
+
""",
|
|
444
|
+
(full_id,),
|
|
445
|
+
)
|
|
446
|
+
values: dict[str, list[Modality]] = {"input": [], "output": []}
|
|
447
|
+
for row in rows:
|
|
448
|
+
direction = cast("str", row["direction"])
|
|
449
|
+
values[direction].append(Modality(cast("str", row["value"])))
|
|
450
|
+
return Modalities(
|
|
451
|
+
input=tuple(values["input"]),
|
|
452
|
+
output=tuple(values["output"]),
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _interleaved_field(row: sqlite3.Row) -> str | None:
|
|
457
|
+
return cast("str | None", row["interleaved_field"])
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _cost(
|
|
461
|
+
connection: sqlite3.Connection,
|
|
462
|
+
full_id: str,
|
|
463
|
+
experimental_mode_name: str | None,
|
|
464
|
+
) -> list[Cost] | None:
|
|
465
|
+
rows = list(
|
|
466
|
+
connection.execute(
|
|
467
|
+
"""
|
|
468
|
+
SELECT * FROM pricing
|
|
469
|
+
WHERE model_full_id = ?
|
|
470
|
+
AND experimental_mode_name IS ?
|
|
471
|
+
ORDER BY min_context
|
|
472
|
+
""",
|
|
473
|
+
(full_id, experimental_mode_name),
|
|
474
|
+
),
|
|
475
|
+
)
|
|
476
|
+
if not rows:
|
|
477
|
+
return None
|
|
478
|
+
return [
|
|
479
|
+
Cost(
|
|
480
|
+
input=cast("float", row["cost_input"]),
|
|
481
|
+
output=cast("float", row["cost_output"]),
|
|
482
|
+
min_context=cast("int", row["min_context"]),
|
|
483
|
+
reasoning=cast("float | None", row["cost_reasoning"]),
|
|
484
|
+
cache_read=cast("float | None", row["cost_cache_read"]),
|
|
485
|
+
cache_write=cast("float | None", row["cost_cache_write"]),
|
|
486
|
+
input_audio=cast("float | None", row["cost_input_audio"]),
|
|
487
|
+
output_audio=cast("float | None", row["cost_output_audio"]),
|
|
488
|
+
)
|
|
489
|
+
for row in rows
|
|
490
|
+
]
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def _provider_config(
|
|
494
|
+
row: sqlite3.Row,
|
|
495
|
+
prefix: str,
|
|
496
|
+
) -> ModelProviderConfig | None:
|
|
497
|
+
npm = row[f"{prefix}_npm"]
|
|
498
|
+
api = row[f"{prefix}_api"]
|
|
499
|
+
api_shape = row[f"{prefix}_api_shape"]
|
|
500
|
+
body = row[f"{prefix}_body_json"]
|
|
501
|
+
headers = row[f"{prefix}_headers_json"]
|
|
502
|
+
if (
|
|
503
|
+
npm is None
|
|
504
|
+
and api is None
|
|
505
|
+
and api_shape is None
|
|
506
|
+
and body is None
|
|
507
|
+
and headers is None
|
|
508
|
+
):
|
|
509
|
+
return None
|
|
510
|
+
return ModelProviderConfig(
|
|
511
|
+
npm=cast("str | None", npm),
|
|
512
|
+
api=cast("str | None", api),
|
|
513
|
+
api_shape=(
|
|
514
|
+
None
|
|
515
|
+
if api_shape is None
|
|
516
|
+
else ProviderAPIShape(cast("str", api_shape))
|
|
517
|
+
),
|
|
518
|
+
body=_json_object(body),
|
|
519
|
+
headers=_json_string_object(headers),
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _json_object(value: object) -> JsonObject | None:
|
|
524
|
+
if value is None:
|
|
525
|
+
return None
|
|
526
|
+
return cast("JsonObject", json.loads(cast("str", value)))
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def _json_string_object(value: object) -> dict[str, str] | None:
|
|
530
|
+
if value is None:
|
|
531
|
+
return None
|
|
532
|
+
return cast("dict[str, str]", json.loads(cast("str", value)))
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _experimental_modes(
|
|
536
|
+
connection: sqlite3.Connection,
|
|
537
|
+
full_id: str,
|
|
538
|
+
) -> dict[str, ExperimentalMode] | None:
|
|
539
|
+
rows = list(
|
|
540
|
+
connection.execute(
|
|
541
|
+
"""
|
|
542
|
+
SELECT * FROM experimental_modes
|
|
543
|
+
WHERE model_full_id = ?
|
|
544
|
+
ORDER BY name
|
|
545
|
+
""",
|
|
546
|
+
(full_id,),
|
|
547
|
+
),
|
|
548
|
+
)
|
|
549
|
+
if not rows:
|
|
550
|
+
return None
|
|
551
|
+
return {
|
|
552
|
+
cast("str", row["name"]): ExperimentalMode(
|
|
553
|
+
cost=_cost(connection, full_id, cast("str", row["name"])),
|
|
554
|
+
provider=_provider_config(row, "provider"),
|
|
555
|
+
)
|
|
556
|
+
for row in rows
|
|
557
|
+
}
|