model-library 0.1.4__py3-none-any.whl → 0.1.5__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.
@@ -9,9 +9,7 @@ from pydantic import BaseModel, Field, computed_field, field_validator
9
9
  from typing_extensions import override
10
10
 
11
11
  from model_library.base.input import InputItem, ToolCall
12
- from model_library.base.utils import (
13
- sum_optional,
14
- )
12
+ from model_library.base.utils import add_optional
15
13
  from model_library.utils import truncate_str
16
14
 
17
15
 
@@ -42,10 +40,14 @@ class QueryResultCost(BaseModel):
42
40
  reasoning: float | None = None
43
41
  cache_read: float | None = None
44
42
  cache_write: float | None = None
43
+ total_override: float | None = None
45
44
 
46
45
  @computed_field
47
46
  @property
48
47
  def total(self) -> float:
48
+ if self.total_override is not None:
49
+ return self.total_override
50
+
49
51
  return sum(
50
52
  filter(
51
53
  None,
@@ -86,6 +88,16 @@ class QueryResultCost(BaseModel):
86
88
  )
87
89
  )
88
90
 
91
+ def __add__(self, other: "QueryResultCost") -> "QueryResultCost":
92
+ return QueryResultCost(
93
+ input=self.input + other.input,
94
+ output=self.output + other.output,
95
+ reasoning=add_optional(self.reasoning, other.reasoning),
96
+ cache_read=add_optional(self.cache_read, other.cache_read),
97
+ cache_write=add_optional(self.cache_write, other.cache_write),
98
+ total_override=add_optional(self.total_override, other.total_override),
99
+ )
100
+
89
101
  @override
90
102
  def __repr__(self):
91
103
  use_cents = self.total < 1
@@ -150,18 +162,20 @@ class QueryResultMetadata(BaseModel):
150
162
  return QueryResultMetadata(
151
163
  in_tokens=self.in_tokens + other.in_tokens,
152
164
  out_tokens=self.out_tokens + other.out_tokens,
153
- reasoning_tokens=sum_optional(
154
- self.reasoning_tokens, other.reasoning_tokens
165
+ reasoning_tokens=cast(
166
+ int | None, add_optional(self.reasoning_tokens, other.reasoning_tokens)
155
167
  ),
156
- cache_read_tokens=sum_optional(
157
- self.cache_read_tokens, other.cache_read_tokens
168
+ cache_read_tokens=cast(
169
+ int | None,
170
+ add_optional(self.cache_read_tokens, other.cache_read_tokens),
158
171
  ),
159
- cache_write_tokens=sum_optional(
160
- self.cache_write_tokens, other.cache_write_tokens
172
+ cache_write_tokens=cast(
173
+ int | None,
174
+ add_optional(self.cache_write_tokens, other.cache_write_tokens),
161
175
  ),
162
176
  duration_seconds=self.default_duration_seconds
163
177
  + other.default_duration_seconds,
164
- cost=self.cost,
178
+ cost=cast(QueryResultCost | None, add_optional(self.cost, other.cost)),
165
179
  )
166
180
 
167
181
  @override
@@ -1,4 +1,4 @@
1
- from typing import Sequence, cast
1
+ from typing import Sequence, TypeVar, cast
2
2
 
3
3
  from model_library.base.input import (
4
4
  FileBase,
@@ -8,17 +8,39 @@ from model_library.base.input import (
8
8
  ToolResult,
9
9
  )
10
10
  from model_library.utils import truncate_str
11
+ from pydantic import BaseModel
11
12
 
13
+ T = TypeVar("T", bound=BaseModel)
12
14
 
13
- def sum_optional(a: int | None, b: int | None) -> int | None:
14
- """Sum two optional integers, returning None if both are None.
15
+
16
+ def add_optional(
17
+ a: int | float | T | None, b: int | float | T | None
18
+ ) -> int | float | T | None:
19
+ """Add two optional objects, returning None if both are None.
15
20
 
16
21
  Preserves None to indicate "unknown/not provided" when both inputs are None,
17
- otherwise treats None as 0 for summation.
22
+ otherwise returns the non-None value or their sum.
18
23
  """
19
24
  if a is None and b is None:
20
25
  return None
21
- return (a or 0) + (b or 0)
26
+
27
+ if a is None or b is None:
28
+ return a or b
29
+
30
+ if isinstance(a, (int, float)) and isinstance(b, (int, float)):
31
+ return a + b
32
+
33
+ # NOTE: Ensure that the subtypes are the same so we can use the __add__ method just from one
34
+ if type(a) is type(b):
35
+ add_method = getattr(a, "__add__", None)
36
+ if add_method is not None:
37
+ return add_method(b)
38
+ else:
39
+ raise ValueError(
40
+ f"Cannot add {type(a)} and {type(b)} because they are not the same subclass"
41
+ )
42
+
43
+ return None
22
44
 
23
45
 
24
46
  def get_pretty_input_types(input: Sequence["InputItem"], verbose: bool = False) -> str:
@@ -1,4 +1,54 @@
1
1
  {
2
+ "google/gemini-3-flash-preview": {
3
+ "company": "Google",
4
+ "label": "Gemini 3 Flash (12/25)",
5
+ "description": "Google's newest budget workhorse model",
6
+ "release_date": "2025-12-17",
7
+ "open_source": false,
8
+ "documentation_url": "https://ai.google.dev/gemini-api/docs/models",
9
+ "properties": {
10
+ "context_window": 1048576,
11
+ "max_tokens": 65536,
12
+ "training_cutoff": null,
13
+ "reasoning_model": true
14
+ },
15
+ "supports": {
16
+ "images": true,
17
+ "videos": true,
18
+ "files": true,
19
+ "batch": true,
20
+ "temperature": true,
21
+ "tools": true
22
+ },
23
+ "metadata": {
24
+ "deprecated": false,
25
+ "available_for_everyone": true,
26
+ "available_as_evaluator": false,
27
+ "ignored_for_cost": false
28
+ },
29
+ "provider_properties": {},
30
+ "costs_per_million_token": {
31
+ "input": 0.5,
32
+ "output": 3.0,
33
+ "cache": {
34
+ "read_discount": 0.1,
35
+ "write_markup": 1.0
36
+ },
37
+ "batch": {
38
+ "input_discount": 0.5,
39
+ "output_discount": 0.5
40
+ }
41
+ },
42
+ "alternative_keys": [],
43
+ "default_parameters": {
44
+ "temperature": 1.0,
45
+ "reasoning_effort": "high"
46
+ },
47
+ "provider_endpoint": "gemini-3-flash-preview",
48
+ "provider_name": "google",
49
+ "full_key": "google/gemini-3-flash-preview",
50
+ "slug": "google_gemini-3-flash-preview"
51
+ },
2
52
  "openai/gpt-5.2-pro-2025-12-11": {
3
53
  "company": "OpenAI",
4
54
  "label": "GPT 5.2 Pro",
@@ -15428,7 +15478,7 @@
15428
15478
  "tools": false
15429
15479
  },
15430
15480
  "metadata": {
15431
- "deprecated": false,
15481
+ "deprecated": true,
15432
15482
  "available_for_everyone": true,
15433
15483
  "available_as_evaluator": false,
15434
15484
  "ignored_for_cost": false
@@ -54,6 +54,21 @@ gemini-3-models:
54
54
  temperature: 1
55
55
  reasoning_effort: "high"
56
56
 
57
+ google/gemini-3-flash-preview:
58
+ label: Gemini 3 Flash (12/25)
59
+ description: Google's newest budget workhorse model
60
+ release_date: 2025-12-17
61
+ properties:
62
+ context_window: 1048576
63
+ max_tokens: 65536
64
+ reasoning_model: true
65
+ costs_per_million_token:
66
+ input: 0.50
67
+ output: 3.00
68
+ default_parameters:
69
+ temperature: 1
70
+ reasoning_effort: "high"
71
+
57
72
  google/gemini-3-pro-preview:
58
73
  label: Gemini 3 Pro (11/25)
59
74
  description: Gemini 3 Pro, Google's most powerful model.
@@ -46,6 +46,8 @@ perplexity-models:
46
46
  label: Sonar Reasoning
47
47
  description: Reasoning-focused search model that exposes intermediate thinking for step-by-step answers.
48
48
  documentation_url: https://docs.perplexity.ai/models/models/sonar-reasoning
49
+ metadata:
50
+ deprecated: true
49
51
  properties:
50
52
  context_window: 128000
51
53
  reasoning_model: true
@@ -183,8 +183,8 @@ RETRIABLE_EXCEPTION_CODES = [
183
183
  "server_error",
184
184
  "overloaded",
185
185
  "throttling", # AWS throttling errors
186
- "throttlingexception", # AWS throttling errors
187
186
  "internal server error",
187
+ "InternalServerError",
188
188
  ]
189
189
 
190
190
 
@@ -1,9 +1,11 @@
1
1
  from functools import cache
2
2
  from pathlib import Path
3
+ from typing import TypedDict
3
4
 
4
5
  import tiktoken
5
6
 
6
7
  from model_library.base import LLM, LLMConfig, ProviderConfig
8
+ from model_library.base.output import QueryResultCost, QueryResultMetadata
7
9
  from model_library.register_models import (
8
10
  CostProperties,
9
11
  ModelConfig,
@@ -129,6 +131,64 @@ def get_model_cost(model_str: str) -> CostProperties | None:
129
131
  return model_config.costs_per_million_token
130
132
 
131
133
 
134
+ class TokenDict(TypedDict, total=False):
135
+ """Token counts for cost calculation."""
136
+
137
+ in_tokens: int
138
+ out_tokens: int
139
+ reasoning_tokens: int | None
140
+ cache_read_tokens: int | None
141
+ cache_write_tokens: int | None
142
+
143
+
144
+ async def recompute_cost(
145
+ model_str: str,
146
+ tokens: TokenDict,
147
+ ) -> QueryResultCost:
148
+ """
149
+ Recompute the cost for a model based on token information.
150
+
151
+ Uses the model provider's existing _calculate_cost method to ensure
152
+ provider-specific cost calculations are applied.
153
+
154
+ Args:
155
+ model_str: The model identifier (e.g., "openai/gpt-4o")
156
+ tokens: Dictionary containing token counts with keys:
157
+ - in_tokens (required): Number of input tokens
158
+ - out_tokens (required): Number of output tokens
159
+ - reasoning_tokens (optional): Number of reasoning tokens
160
+ - cache_read_tokens (optional): Number of cache read tokens
161
+ - cache_write_tokens (optional): Number of cache write tokens
162
+
163
+ Returns:
164
+ QueryResultCost with computed costs
165
+
166
+ Raises:
167
+ ValueError: If required token parameters are missing
168
+ Exception: If model not found in registry or costs not configured
169
+ """
170
+ if "in_tokens" not in tokens:
171
+ raise ValueError("Token dict must contain 'in_tokens'")
172
+ if "out_tokens" not in tokens:
173
+ raise ValueError("Token dict must contain 'out_tokens'")
174
+
175
+ model = get_registry_model(model_str)
176
+
177
+ metadata = QueryResultMetadata(
178
+ in_tokens=tokens["in_tokens"],
179
+ out_tokens=tokens["out_tokens"],
180
+ reasoning_tokens=tokens.get("reasoning_tokens"),
181
+ cache_read_tokens=tokens.get("cache_read_tokens"),
182
+ cache_write_tokens=tokens.get("cache_write_tokens"),
183
+ )
184
+
185
+ cost = await model._calculate_cost(metadata) # type: ignore[arg-type]
186
+ if cost is None:
187
+ raise Exception(f"No cost information available for model {model_str}")
188
+
189
+ return cost
190
+
191
+
132
192
  @cache
133
193
  def get_provider_names() -> list[str]:
134
194
  """Return all provider names in the registry"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: model-library
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Model Library for vals.ai
5
5
  Author-email: "Vals AI, Inc." <contact@vals.ai>
6
6
  License: MIT
@@ -1,11 +1,11 @@
1
1
  model_library/__init__.py,sha256=AKc_15aklOf-LbcS9z1Xer_moRWNpG6Dh3kqvSQ0nOI,714
2
- model_library/exceptions.py,sha256=I9wquqj5hE640OfwVjUFtQUuu_potWAejLcOQCpDxIg,8705
2
+ model_library/exceptions.py,sha256=ZHMr6lloXZz4V4Wy1UP8zc1CdUHx6-IS9_rOi6oN45s,8680
3
3
  model_library/file_utils.py,sha256=FAZRRtDT8c4Rjfoj64Te3knEHggXAAfRRuS8WLCsSe8,3682
4
4
  model_library/logging.py,sha256=McyaPHUk7RkB38-LrfnudrrU1B62ta8wAbbIBwLRmj0,853
5
5
  model_library/model_utils.py,sha256=l8oCltGeimMGtnne_3Q1EguVtzCj61UMsLsma-1czwg,753
6
6
  model_library/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  model_library/register_models.py,sha256=CY3Wd16AcWf7tYu_O2I2_kg_hdvQJFcvyQQA2OUu2SA,13646
8
- model_library/registry_utils.py,sha256=-ut95Aup5RYrZdv5Aih3bbYhe2vw9V0l4EFyH_1ONsQ,6797
8
+ model_library/registry_utils.py,sha256=BVauHcP02Et2maLxowNBbdpGd32cnLz1_zSjDLVJjp0,8843
9
9
  model_library/settings.py,sha256=QyeUqzWBpexFi014L_mZkoXP49no3SAQNJRObATXrL8,873
10
10
  model_library/utils.py,sha256=T91ACGTc-KtksVyMFspt-vJtR5I-xcO3nVfH6SltmMU,3988
11
11
  model_library/base/__init__.py,sha256=TtxCXGUtkEqWZNMMofLPuC4orN7Ja2hemtbtHitt_UA,266
@@ -13,25 +13,25 @@ model_library/base/base.py,sha256=HXxImh2H-GIIiVGNqV7gRPi0HH1KJxB_4ckuKyEqAYo,14
13
13
  model_library/base/batch.py,sha256=-jd6L0ECc5pkj73zoX2ZYcv_9iQdqxEi1kEilwaXWSA,2895
14
14
  model_library/base/delegate_only.py,sha256=V2MzENtvBg0pySKncgE-mfCLBhhRZ0y4BntQwQsxbqU,2111
15
15
  model_library/base/input.py,sha256=Nhg8Ril1kFau1DnE8u102JC1l-vxNd-v9e3SjovR-Do,1876
16
- model_library/base/output.py,sha256=9pQZSOskkLDd_MAuDbYSimrbEcBL6x_3z6nLrPUnCOw,6701
17
- model_library/base/utils.py,sha256=KJZRVWr38Tik3yNJvTXnBy62ccilzzmSxHZFpQBJMPo,1330
16
+ model_library/base/output.py,sha256=Ak6CJRYqtjYILsSWkfE70fSK3yvP7v_n5NYfysMaIL4,7464
17
+ model_library/base/utils.py,sha256=YGQLPyQgCbfHNBxyTxCvpZNZ-ctEji258IdfMiXUJXs,1962
18
18
  model_library/config/README.md,sha256=i8_wHnlI6uHIqWN9fYBkDCglZM2p5ZMVD3SLlxiwUVk,4274
19
19
  model_library/config/ai21labs_models.yaml,sha256=ZWHhk1cep2GQIYHqkTS_0152mF3oZg2tSzMPmvfMRSI,2478
20
20
  model_library/config/alibaba_models.yaml,sha256=-RLWOwh3ZaCQqjaZ-4Zw0BJNVE6JVHJ8Ggm9gQJZ6QI,2082
21
- model_library/config/all_models.json,sha256=HuTWNX-noeGfLNoWuzLVjhjXqkFGJX0CgBMt01Ejy3A,529312
21
+ model_library/config/all_models.json,sha256=suyLLiU87NbUjWsnGeUpd94aIQ6UPyEURtPh48AW4-4,530914
22
22
  model_library/config/amazon_models.yaml,sha256=HgLmhpfedHCQtkPEviEJCBbAb-dNQPOnVtf4UnwrDds,7654
23
23
  model_library/config/anthropic_models.yaml,sha256=bTc_3Oqn4wCdq-dcWcEfmXrPVZjcR8-V6pTao7sGa_E,10475
24
24
  model_library/config/cohere_models.yaml,sha256=ZfWrS1K45Hxd5nT_gpP5YGAovJcBIlLNIdaRyE3V-7o,5022
25
25
  model_library/config/deepseek_models.yaml,sha256=4CCrf-4UPBgFCrS6CQa3vzNiaYlD4B6dFJFK_kIYBWY,1156
26
26
  model_library/config/dummy_model.yaml,sha256=lImYJBtBVJk_jgnLbkuSyOshQphVlYCMkw-UiJIBYhY,877
27
27
  model_library/config/fireworks_models.yaml,sha256=BMyQqjEpayNfSVGekzOFNIx7Ng3QOfPtldw5k2msqX0,6269
28
- model_library/config/google_models.yaml,sha256=n6yPRSVLyKGoJQW7L3UiVmb182zKiYhVLbmiUQDwXiY,16101
28
+ model_library/config/google_models.yaml,sha256=Rg127nsBbHpk62X7WBq2ckdHo0bwYM0NVjF7T2h_1c0,16494
29
29
  model_library/config/inception_models.yaml,sha256=YCqfQlkH_pTdHIKee5dP_aRFXw_fTIEQCpUvX2bwO0M,560
30
30
  model_library/config/kimi_models.yaml,sha256=AAqse_BCE-lrHkJHIWJVqMtttnZQCa-5Qy5qiLUJjYs,755
31
31
  model_library/config/minimax_models.yaml,sha256=IttkpdBrp75J9WZQ0IRE4m4eSfd0LonfcA9OtrzJrMY,873
32
32
  model_library/config/mistral_models.yaml,sha256=mYKYSzJl24lUiA_erSkom7nCBxAoeJ57Mi3090q1ArM,5162
33
33
  model_library/config/openai_models.yaml,sha256=1lKsTQwsxMMJqXtEoYs3liy6NcaK4p8NN7b-GSFnl8k,25261
34
- model_library/config/perplexity_models.yaml,sha256=XEvs3fXrsSYjYNHLJuGSlTW7biHMaXpZaW4Q-aVn6wU,2299
34
+ model_library/config/perplexity_models.yaml,sha256=WUDqhLvnB0kQhCCwPv19FYLHozet3m33Spdo6bGff3Q,2336
35
35
  model_library/config/together_models.yaml,sha256=BeqRJonYzPvWwoLfkyH0RMRKBYUrCSEQhg_25Nvx97M,23867
36
36
  model_library/config/xai_models.yaml,sha256=2KRNNQy3kV-4xxSfhj7Uhp9TZF-S5qPlM8Ef-04zv8Y,7985
37
37
  model_library/config/zai_models.yaml,sha256=Esa4P-zc5K1pejQTylKPe-uiH9AnvB_Zn7RB_sAZ5mU,1577
@@ -57,8 +57,8 @@ model_library/providers/zai.py,sha256=O_GM6KlJ0fM2wYoxO9xrCWfnpYH7IpoKEzjiD4jB8K
57
57
  model_library/providers/google/__init__.py,sha256=ypuLVL_QJEQ7C3S47FhC9y4wyawYOdGikAViJmACI0U,115
58
58
  model_library/providers/google/batch.py,sha256=4TE90Uo1adi54dVtGcGyUAxw11YExJq-Y4KmkQ-cyHA,9978
59
59
  model_library/providers/google/google.py,sha256=s9vky9r5SVNhBvMXcIr0_h0MlKLXwx_tQlZzs57xXYo,16507
60
- model_library-0.1.4.dist-info/licenses/LICENSE,sha256=x6mf4o7U_wHaaqcfxoU-0R6uYJLbqL_TNuoULP3asaA,1070
61
- model_library-0.1.4.dist-info/METADATA,sha256=4XPEbWSeOBYYoQ3ZYsdktZSnrDz2YbZixPIW7wTqJfw,6989
62
- model_library-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
- model_library-0.1.4.dist-info/top_level.txt,sha256=HtQYxA_7RP8UT35I6VcUw20L6edI0Zf2t5Ys1uDGVjs,14
64
- model_library-0.1.4.dist-info/RECORD,,
60
+ model_library-0.1.5.dist-info/licenses/LICENSE,sha256=x6mf4o7U_wHaaqcfxoU-0R6uYJLbqL_TNuoULP3asaA,1070
61
+ model_library-0.1.5.dist-info/METADATA,sha256=HeLAgZOFNM7TBGJm2bfubjspsa388C0Va1hcWO-uu6I,6989
62
+ model_library-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
+ model_library-0.1.5.dist-info/top_level.txt,sha256=HtQYxA_7RP8UT35I6VcUw20L6edI0Zf2t5Ys1uDGVjs,14
64
+ model_library-0.1.5.dist-info/RECORD,,