dot-inference 2.0.2__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.
- dot_inference/README.md +83 -0
- dot_inference/__init__.py +86 -0
- dot_inference/_client.py +520 -0
- dot_inference/_cost_tracker.py +71 -0
- dot_inference/_llm_registry.py +302 -0
- dot_inference/_local_check.py +76 -0
- dot_inference/_mistral_compat.py +530 -0
- dot_inference/_openai_compat.py +570 -0
- dot_inference/_rate_limit.py +64 -0
- dot_inference/_retry.py +66 -0
- dot_inference/_sync.py +49 -0
- dot_inference/_types.py +229 -0
- dot_inference/mock.py +151 -0
- dot_inference/models.yaml +481 -0
- dot_inference/settings.py +138 -0
- dot_inference-2.0.2.dist-info/METADATA +351 -0
- dot_inference-2.0.2.dist-info/RECORD +19 -0
- dot_inference-2.0.2.dist-info/WHEEL +4 -0
- dot_inference-2.0.2.dist-info/licenses/LICENSE.md +660 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
# Model registry — canonical model definitions with per-provider routing.
|
|
2
|
+
#
|
|
3
|
+
# ─── Custom models ────────────────────────────────────────────────────
|
|
4
|
+
#
|
|
5
|
+
# To add your own models (or override built-in ones) without editing this
|
|
6
|
+
# file, create a YAML file with the same format and set:
|
|
7
|
+
#
|
|
8
|
+
# export DOTI_CUSTOM_MODELS=/path/to/my_models.yaml
|
|
9
|
+
#
|
|
10
|
+
# Entries in your custom file are merged on top of this registry:
|
|
11
|
+
# - New model IDs are added.
|
|
12
|
+
# - Existing model IDs are fully replaced.
|
|
13
|
+
#
|
|
14
|
+
# Example custom file:
|
|
15
|
+
#
|
|
16
|
+
# anthropic/claude-sonnet-4:
|
|
17
|
+
# supports_strict_json: true
|
|
18
|
+
# input_token_cost: 3.0e-6
|
|
19
|
+
# output_token_cost: 1.5e-5
|
|
20
|
+
# providers:
|
|
21
|
+
# openrouter:
|
|
22
|
+
# api_model_name: anthropic/claude-sonnet-4
|
|
23
|
+
#
|
|
24
|
+
# ─── Field reference ──────────────────────────────────────────────────
|
|
25
|
+
#
|
|
26
|
+
# Each top-level key is a canonical model ID used in settings (e.g.
|
|
27
|
+
# DOTI_GENERATION__OPENAI__MODEL_NAME=openai/gpt-4o).
|
|
28
|
+
#
|
|
29
|
+
# Model-level fields (defined once, shared across providers):
|
|
30
|
+
# supports_strict_json: true/false
|
|
31
|
+
# - If true: uses server-side constrained decoding (strict: true in
|
|
32
|
+
# json_schema). Output is guaranteed valid JSON.
|
|
33
|
+
# - If false: uses client-side parsing. Best-effort, may fail on
|
|
34
|
+
# weaker models.
|
|
35
|
+
#
|
|
36
|
+
# supports_reasoning: true/false
|
|
37
|
+
# - Whether the model supports the reasoning_effort parameter.
|
|
38
|
+
# - Can be overridden per provider (see below) when reasoning is
|
|
39
|
+
# available through one provider but not another.
|
|
40
|
+
#
|
|
41
|
+
# reasoning_effort_options: list[str] (optional)
|
|
42
|
+
# - The reasoning_effort values this model actually accepts. Lets a
|
|
43
|
+
# client offer only the valid choices. Defaults to
|
|
44
|
+
# [low, medium, high] when supports_reasoning is true and this is
|
|
45
|
+
# omitted. Mistral reasoning models (Medium 3.5, Small) accept only
|
|
46
|
+
# [none, high]. Ignored when supports_reasoning is false.
|
|
47
|
+
#
|
|
48
|
+
# default_reasoning_effort: str (optional)
|
|
49
|
+
# - The effort to use for this model when the caller doesn't specify
|
|
50
|
+
# one. Must be a member of reasoning_effort_options. Falls back to
|
|
51
|
+
# the global GenerationSettings default when omitted.
|
|
52
|
+
#
|
|
53
|
+
# input_token_cost / output_token_cost:
|
|
54
|
+
# - Default per-token USD cost (direct-from-vendor pricing).
|
|
55
|
+
# - Can be overridden per provider (e.g. OpenRouter markup).
|
|
56
|
+
# - Inline "# $X/M" comments restate the cost per million tokens.
|
|
57
|
+
#
|
|
58
|
+
# default_provider: str (optional)
|
|
59
|
+
# - Provider used when get_llm_client() is called with model= but no
|
|
60
|
+
# provider=. Falls back to DOTI_GENERATION__PROVIDER when unset.
|
|
61
|
+
# - An explicit provider= argument always wins.
|
|
62
|
+
#
|
|
63
|
+
# context_window: int (optional)
|
|
64
|
+
# - Maximum tokens (input + output) the model accepts in one request.
|
|
65
|
+
# - Public spec value; used by callers to render fullness gauges and
|
|
66
|
+
# guard against over-long prompts. Omit when not declared.
|
|
67
|
+
#
|
|
68
|
+
# Per-provider fields (nested under "providers"):
|
|
69
|
+
# api_model_name: str
|
|
70
|
+
# - The model string sent to the provider's API.
|
|
71
|
+
#
|
|
72
|
+
# input_token_cost / output_token_cost (optional):
|
|
73
|
+
# - Provider-specific cost override.
|
|
74
|
+
#
|
|
75
|
+
# supports_reasoning: true/false (optional)
|
|
76
|
+
# - Per-provider override for reasoning support.
|
|
77
|
+
# - When omitted, inherits the model-level value.
|
|
78
|
+
# - Useful when the model supports reasoning via one provider (e.g.
|
|
79
|
+
# OpenRouter) but not another (e.g. native SDK lacks the param).
|
|
80
|
+
#
|
|
81
|
+
# features (optional, provider-specific):
|
|
82
|
+
# response_healing: true/false
|
|
83
|
+
# - OpenRouter plugin that post-processes malformed JSON responses.
|
|
84
|
+
# - Only useful when supports_strict_json is false.
|
|
85
|
+
#
|
|
86
|
+
# fallback_models: list of model names
|
|
87
|
+
# - Alternative models if the primary fails (OpenRouter "models").
|
|
88
|
+
#
|
|
89
|
+
# provider_routing: dict
|
|
90
|
+
# - OpenRouter provider routing preferences.
|
|
91
|
+
# - Example: only: ["mistral"] routes exclusively through Mistral.
|
|
92
|
+
|
|
93
|
+
# ─── OpenAI models ───────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
openai/gpt-5:
|
|
96
|
+
supports_strict_json: true
|
|
97
|
+
supports_reasoning: true
|
|
98
|
+
input_token_cost: 1.25e-6 # $1.25/M
|
|
99
|
+
output_token_cost: 1.e-5 # $10/M
|
|
100
|
+
context_window: 400000
|
|
101
|
+
providers:
|
|
102
|
+
openai:
|
|
103
|
+
api_model_name: gpt-5
|
|
104
|
+
openrouter:
|
|
105
|
+
api_model_name: openai/gpt-5
|
|
106
|
+
features:
|
|
107
|
+
provider_routing:
|
|
108
|
+
only: ["openai"]
|
|
109
|
+
|
|
110
|
+
openai/gpt-5.1:
|
|
111
|
+
supports_strict_json: true
|
|
112
|
+
supports_reasoning: true
|
|
113
|
+
input_token_cost: 1.25e-6 # $1.25/M
|
|
114
|
+
output_token_cost: 1.e-5 # $10/M
|
|
115
|
+
context_window: 400000
|
|
116
|
+
providers:
|
|
117
|
+
openai:
|
|
118
|
+
api_model_name: gpt-5.1
|
|
119
|
+
openrouter:
|
|
120
|
+
api_model_name: openai/gpt-5.1
|
|
121
|
+
features:
|
|
122
|
+
provider_routing:
|
|
123
|
+
only: ["openai"]
|
|
124
|
+
|
|
125
|
+
openai/gpt-5.2:
|
|
126
|
+
supports_strict_json: true
|
|
127
|
+
supports_reasoning: true
|
|
128
|
+
input_token_cost: 1.75e-6 # $1.75/M
|
|
129
|
+
output_token_cost: 1.4e-5 # $14/M
|
|
130
|
+
context_window: 400000
|
|
131
|
+
providers:
|
|
132
|
+
openai:
|
|
133
|
+
api_model_name: gpt-5.2
|
|
134
|
+
openrouter:
|
|
135
|
+
api_model_name: openai/gpt-5.2
|
|
136
|
+
features:
|
|
137
|
+
provider_routing:
|
|
138
|
+
only: ["openai"]
|
|
139
|
+
|
|
140
|
+
openai/gpt-5-mini:
|
|
141
|
+
supports_strict_json: true
|
|
142
|
+
supports_reasoning: true
|
|
143
|
+
input_token_cost: 2.5e-7 # $0.25/M
|
|
144
|
+
output_token_cost: 2.e-6 # $2/M
|
|
145
|
+
context_window: 400000
|
|
146
|
+
providers:
|
|
147
|
+
openai:
|
|
148
|
+
api_model_name: gpt-5-mini
|
|
149
|
+
openrouter:
|
|
150
|
+
api_model_name: openai/gpt-5-mini
|
|
151
|
+
features:
|
|
152
|
+
provider_routing:
|
|
153
|
+
only: ["openai"]
|
|
154
|
+
|
|
155
|
+
openai/gpt-oss-20b:
|
|
156
|
+
supports_strict_json: true
|
|
157
|
+
supports_reasoning: true
|
|
158
|
+
input_token_cost: 2e-8 # $0.02/M
|
|
159
|
+
output_token_cost: 1e-7 # $0.10/M
|
|
160
|
+
context_window: 128000
|
|
161
|
+
providers:
|
|
162
|
+
openrouter:
|
|
163
|
+
api_model_name: openai/gpt-oss-20b
|
|
164
|
+
local:
|
|
165
|
+
api_model_name: openai/gpt-oss-20b
|
|
166
|
+
|
|
167
|
+
# Self-hosted alias — see openai/gpt-oss-120b-local below.
|
|
168
|
+
openai/gpt-oss-20b-local:
|
|
169
|
+
supports_strict_json: true
|
|
170
|
+
supports_reasoning: true
|
|
171
|
+
input_token_cost: 0
|
|
172
|
+
output_token_cost: 0
|
|
173
|
+
context_window: 128000
|
|
174
|
+
default_provider: local
|
|
175
|
+
providers:
|
|
176
|
+
local:
|
|
177
|
+
api_model_name: openai/gpt-oss-20b
|
|
178
|
+
|
|
179
|
+
openai/gpt-5.4-nano:
|
|
180
|
+
supports_strict_json: true
|
|
181
|
+
supports_reasoning: true
|
|
182
|
+
input_token_cost: 2e-7 # $0.20/M
|
|
183
|
+
output_token_cost: 1.25e-6 # $1.25/M
|
|
184
|
+
context_window: 400000
|
|
185
|
+
providers:
|
|
186
|
+
openai:
|
|
187
|
+
api_model_name: gpt-5.4-nano
|
|
188
|
+
openrouter:
|
|
189
|
+
api_model_name: openai/gpt-5.4-nano
|
|
190
|
+
features:
|
|
191
|
+
provider_routing:
|
|
192
|
+
only: ["openai"]
|
|
193
|
+
|
|
194
|
+
openai/gpt-5.4-mini:
|
|
195
|
+
supports_strict_json: true
|
|
196
|
+
supports_reasoning: true
|
|
197
|
+
input_token_cost: 7.5e-7 # $0.75/M
|
|
198
|
+
output_token_cost: 4.5e-6 # $4.50/M
|
|
199
|
+
context_window: 400000
|
|
200
|
+
providers:
|
|
201
|
+
openai:
|
|
202
|
+
api_model_name: gpt-5.4-mini
|
|
203
|
+
openrouter:
|
|
204
|
+
api_model_name: openai/gpt-5.4-mini
|
|
205
|
+
features:
|
|
206
|
+
provider_routing:
|
|
207
|
+
only: ["openai"]
|
|
208
|
+
|
|
209
|
+
openai/gpt-5.4:
|
|
210
|
+
supports_strict_json: true
|
|
211
|
+
supports_reasoning: true
|
|
212
|
+
input_token_cost: 2.5e-6 # $2.50/M
|
|
213
|
+
output_token_cost: 1.5e-5 # $15/M
|
|
214
|
+
context_window: 1050000
|
|
215
|
+
providers:
|
|
216
|
+
openai:
|
|
217
|
+
api_model_name: gpt-5.4
|
|
218
|
+
openrouter:
|
|
219
|
+
api_model_name: openai/gpt-5.4
|
|
220
|
+
features:
|
|
221
|
+
provider_routing:
|
|
222
|
+
only: ["openai"]
|
|
223
|
+
|
|
224
|
+
openai/gpt-oss-120b:
|
|
225
|
+
supports_strict_json: true
|
|
226
|
+
supports_reasoning: true
|
|
227
|
+
input_token_cost: 3.9e-8 # $0.039/M
|
|
228
|
+
output_token_cost: 1.9e-7 # $0.19/M
|
|
229
|
+
context_window: 128000
|
|
230
|
+
providers:
|
|
231
|
+
openrouter:
|
|
232
|
+
api_model_name: openai/gpt-oss-120b
|
|
233
|
+
local:
|
|
234
|
+
api_model_name: openai/gpt-oss-120b
|
|
235
|
+
|
|
236
|
+
# Self-hosted alias: pinned to the `local` provider so it can
|
|
237
|
+
# coexist with the OpenRouter route above as a distinct selectable model.
|
|
238
|
+
# Requires DOTI_GENERATION__LOCAL__BASE_URL (get_llm_client errors otherwise);
|
|
239
|
+
openai/gpt-oss-120b-local:
|
|
240
|
+
supports_strict_json: true
|
|
241
|
+
supports_reasoning: true
|
|
242
|
+
input_token_cost: 0
|
|
243
|
+
output_token_cost: 0
|
|
244
|
+
context_window: 128000
|
|
245
|
+
default_provider: local
|
|
246
|
+
providers:
|
|
247
|
+
local:
|
|
248
|
+
api_model_name: openai/gpt-oss-120b
|
|
249
|
+
|
|
250
|
+
# ─── Google models ───────────────────────────────────────────────────
|
|
251
|
+
# Gemini is only served by Google on OpenRouter, so no provider pin is
|
|
252
|
+
# needed. The canonical IDs are the friendly aliases the agent exposes;
|
|
253
|
+
# api_model_name carries the dated OpenRouter slug (which drifts as Google
|
|
254
|
+
# ships new previews — keep these mapped to a slug that actually resolves).
|
|
255
|
+
|
|
256
|
+
google/gemini-3-flash:
|
|
257
|
+
supports_strict_json: true
|
|
258
|
+
input_token_cost: 5e-7 # $0.50/M
|
|
259
|
+
output_token_cost: 3e-6 # $3/M
|
|
260
|
+
context_window: 1000000
|
|
261
|
+
providers:
|
|
262
|
+
openrouter:
|
|
263
|
+
api_model_name: google/gemini-3-flash-preview
|
|
264
|
+
|
|
265
|
+
# Friendly alias "gemini-3-pro": OpenRouter has no plain google/gemini-3-pro
|
|
266
|
+
# text endpoint, so this resolves to the current Gemini 3-series pro preview.
|
|
267
|
+
google/gemini-3-pro:
|
|
268
|
+
supports_strict_json: true
|
|
269
|
+
input_token_cost: 2e-6 # $2/M
|
|
270
|
+
output_token_cost: 1.2e-5 # $12/M
|
|
271
|
+
context_window: 1000000
|
|
272
|
+
providers:
|
|
273
|
+
openrouter:
|
|
274
|
+
api_model_name: google/gemini-3.1-pro-preview
|
|
275
|
+
|
|
276
|
+
# Gemma 4 31B (v26.04, April 2026): open-weight (Apache 2.0) dense multimodal
|
|
277
|
+
# model with native function calling, structured outputs, and a configurable
|
|
278
|
+
# reasoning mode. Served across providers on OpenRouter.
|
|
279
|
+
google/gemma-4-31b-it:
|
|
280
|
+
supports_strict_json: true
|
|
281
|
+
supports_reasoning: true
|
|
282
|
+
input_token_cost: 1.2e-7 # $0.12/M
|
|
283
|
+
output_token_cost: 3.5e-7 # $0.35/M
|
|
284
|
+
context_window: 262144
|
|
285
|
+
providers:
|
|
286
|
+
openrouter:
|
|
287
|
+
api_model_name: google/gemma-4-31b-it
|
|
288
|
+
|
|
289
|
+
# ─── Mistral models ─────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
mistral/mistral-large-latest:
|
|
292
|
+
default_provider: mistral
|
|
293
|
+
supports_strict_json: true
|
|
294
|
+
input_token_cost: 2e-6 # $2/M
|
|
295
|
+
output_token_cost: 6e-6 # $6/M
|
|
296
|
+
context_window: 256000
|
|
297
|
+
providers:
|
|
298
|
+
mistral:
|
|
299
|
+
api_model_name: mistral-large-latest
|
|
300
|
+
openrouter:
|
|
301
|
+
api_model_name: mistralai/mistral-large-2512
|
|
302
|
+
input_token_cost: 5e-7 # $0.50/M
|
|
303
|
+
output_token_cost: 1.5e-6 # $1.50/M
|
|
304
|
+
features:
|
|
305
|
+
provider_routing:
|
|
306
|
+
only: ["mistral"]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# Alias — always resolves to the latest Mistral Small release.
|
|
310
|
+
# Use this when you want automatic upgrades as Mistral ships new versions.
|
|
311
|
+
# Note: behavior may change silently when the alias is reassigned.
|
|
312
|
+
#
|
|
313
|
+
# Reasoning: the Mistral REST API supports reasoning_effort ("high"/"none")
|
|
314
|
+
# for this model, but the Python SDK (mistralai v2.0.2) does not yet expose
|
|
315
|
+
# the parameter. Reasoning is disabled at the model level to keep behavior
|
|
316
|
+
# consistent across providers. Re-enable once the SDK adds support.
|
|
317
|
+
mistral/mistral-small-latest:
|
|
318
|
+
default_provider: mistral
|
|
319
|
+
supports_strict_json: true
|
|
320
|
+
input_token_cost: 1.5e-7 # $0.15/M
|
|
321
|
+
output_token_cost: 6e-7 # $0.60/M
|
|
322
|
+
context_window: 256000
|
|
323
|
+
providers:
|
|
324
|
+
mistral:
|
|
325
|
+
api_model_name: mistral-small-latest
|
|
326
|
+
openrouter:
|
|
327
|
+
api_model_name: mistralai/mistral-small-2603
|
|
328
|
+
input_token_cost: 1.5e-7 # $0.15/M
|
|
329
|
+
output_token_cost: 6e-7 # $0.60/M
|
|
330
|
+
features:
|
|
331
|
+
provider_routing:
|
|
332
|
+
only: ["mistral"]
|
|
333
|
+
|
|
334
|
+
# Pinned version of Mistral Small 4 (March 2026).
|
|
335
|
+
# Kept alongside the alias for production stability: pipelines that require
|
|
336
|
+
# reproducible behavior should reference this ID so they are unaffected when
|
|
337
|
+
# mistral-small-latest is reassigned to a future release.
|
|
338
|
+
# Same SDK limitation as the alias above — see comment there.
|
|
339
|
+
mistral/mistral-small-2603:
|
|
340
|
+
default_provider: mistral
|
|
341
|
+
supports_strict_json: true
|
|
342
|
+
input_token_cost: 1.5e-7 # $0.15/M
|
|
343
|
+
output_token_cost: 6e-7 # $0.60/M
|
|
344
|
+
providers:
|
|
345
|
+
mistral:
|
|
346
|
+
api_model_name: mistral-small-2603
|
|
347
|
+
openrouter:
|
|
348
|
+
api_model_name: mistralai/mistral-small-2603
|
|
349
|
+
input_token_cost: 1.5e-7 # $0.15/M
|
|
350
|
+
output_token_cost: 6e-7 # $0.60/M
|
|
351
|
+
features:
|
|
352
|
+
provider_routing:
|
|
353
|
+
only: ["mistral"]
|
|
354
|
+
|
|
355
|
+
mistral/mistral-medium-3.1:
|
|
356
|
+
default_provider: mistral
|
|
357
|
+
supports_strict_json: true
|
|
358
|
+
input_token_cost: 4e-7 # $0.40/M
|
|
359
|
+
output_token_cost: 2e-6 # $2/M
|
|
360
|
+
providers:
|
|
361
|
+
mistral:
|
|
362
|
+
api_model_name: mistral-medium-2508
|
|
363
|
+
openrouter:
|
|
364
|
+
api_model_name: mistralai/mistral-medium-3.1
|
|
365
|
+
input_token_cost: 4e-7 # $0.40/M
|
|
366
|
+
output_token_cost: 2e-6 # $2/M
|
|
367
|
+
features:
|
|
368
|
+
provider_routing:
|
|
369
|
+
only: ["mistral"]
|
|
370
|
+
|
|
371
|
+
# Frontier-class multimodal model optimized for agentic and coding use cases
|
|
372
|
+
# (v26.04, April 2026). Open weights under a Modified MIT license.
|
|
373
|
+
mistral/mistral-medium-3.5:
|
|
374
|
+
default_provider: mistral
|
|
375
|
+
supports_strict_json: true
|
|
376
|
+
# Adjustable reasoning: La Plateforme accepts only "none" (minimal
|
|
377
|
+
# thinking, no thinking chunk) and "high" (full thinking chunk).
|
|
378
|
+
# Mistral recommends "high" for agentic & code use cases.
|
|
379
|
+
supports_reasoning: true
|
|
380
|
+
reasoning_effort_options: [none, high]
|
|
381
|
+
default_reasoning_effort: high
|
|
382
|
+
input_token_cost: 1.5e-6 # $1.50/M
|
|
383
|
+
output_token_cost: 7.5e-6 # $7.50/M
|
|
384
|
+
context_window: 256000
|
|
385
|
+
providers:
|
|
386
|
+
mistral:
|
|
387
|
+
api_model_name: mistral-medium-2604
|
|
388
|
+
openrouter:
|
|
389
|
+
api_model_name: mistralai/mistral-medium-3-5
|
|
390
|
+
input_token_cost: 1.5e-6 # $1.50/M
|
|
391
|
+
output_token_cost: 7.5e-6 # $7.50/M
|
|
392
|
+
features:
|
|
393
|
+
provider_routing:
|
|
394
|
+
only: ["mistral"]
|
|
395
|
+
|
|
396
|
+
# ─── NVIDIA models ───────────────────────────────────────────────
|
|
397
|
+
|
|
398
|
+
nvidia/nemotron-3-super-120b-a12b:
|
|
399
|
+
supports_strict_json: true
|
|
400
|
+
supports_reasoning: true
|
|
401
|
+
input_token_cost: 9e-8 # $0.09/M
|
|
402
|
+
output_token_cost: 4.5e-7 # $0.45/M
|
|
403
|
+
context_window: 1000000
|
|
404
|
+
providers:
|
|
405
|
+
openrouter:
|
|
406
|
+
api_model_name: nvidia/nemotron-3-super-120b-a12b
|
|
407
|
+
|
|
408
|
+
# ─── MiniMax models ───────────────────────────────────────────────
|
|
409
|
+
|
|
410
|
+
minimax/minimax-m2.7:
|
|
411
|
+
supports_strict_json: false
|
|
412
|
+
supports_reasoning: true
|
|
413
|
+
input_token_cost: 3e-7 # $0.30/M
|
|
414
|
+
output_token_cost: 1.2e-6 # $1.20/M
|
|
415
|
+
providers:
|
|
416
|
+
openrouter:
|
|
417
|
+
api_model_name: minimax/minimax-m2.7
|
|
418
|
+
|
|
419
|
+
# ─── Qwen models ───────────────────────────────────────────────────
|
|
420
|
+
|
|
421
|
+
qwen/qwen3.5-9b:
|
|
422
|
+
supports_strict_json: true
|
|
423
|
+
supports_reasoning: true
|
|
424
|
+
input_token_cost: 5e-8 # $0.05/M
|
|
425
|
+
output_token_cost: 1.5e-7 # $0.15/M
|
|
426
|
+
providers:
|
|
427
|
+
openrouter:
|
|
428
|
+
api_model_name: qwen/qwen3.5-9b
|
|
429
|
+
|
|
430
|
+
qwen/qwen3.5-27b:
|
|
431
|
+
supports_strict_json: true
|
|
432
|
+
supports_reasoning: true
|
|
433
|
+
input_token_cost: 1.95e-7 # $0.195/M
|
|
434
|
+
output_token_cost: 1.56e-6 # $1.56/M
|
|
435
|
+
providers:
|
|
436
|
+
openrouter:
|
|
437
|
+
api_model_name: qwen/qwen3.5-27b
|
|
438
|
+
|
|
439
|
+
# ─── Anthropic models ──────────────────────────────────────────────
|
|
440
|
+
|
|
441
|
+
anthropic/claude-haiku-4.5:
|
|
442
|
+
supports_strict_json: true
|
|
443
|
+
supports_reasoning: true
|
|
444
|
+
requires_cache_control: true
|
|
445
|
+
input_token_cost: 1e-6 # $1/M
|
|
446
|
+
output_token_cost: 5e-6 # $5/M
|
|
447
|
+
context_window: 200000
|
|
448
|
+
providers:
|
|
449
|
+
openrouter:
|
|
450
|
+
api_model_name: anthropic/claude-haiku-4.5
|
|
451
|
+
features:
|
|
452
|
+
provider_routing:
|
|
453
|
+
only: ["anthropic"]
|
|
454
|
+
|
|
455
|
+
anthropic/claude-sonnet-4.6:
|
|
456
|
+
supports_strict_json: true
|
|
457
|
+
supports_reasoning: true
|
|
458
|
+
requires_cache_control: true
|
|
459
|
+
input_token_cost: 3e-6 # $3/M
|
|
460
|
+
output_token_cost: 1.5e-5 # $15/M
|
|
461
|
+
context_window: 1000000
|
|
462
|
+
providers:
|
|
463
|
+
openrouter:
|
|
464
|
+
api_model_name: anthropic/claude-sonnet-4.6
|
|
465
|
+
features:
|
|
466
|
+
provider_routing:
|
|
467
|
+
only: ["anthropic"]
|
|
468
|
+
|
|
469
|
+
anthropic/claude-opus-4.6:
|
|
470
|
+
supports_strict_json: true
|
|
471
|
+
supports_reasoning: true
|
|
472
|
+
requires_cache_control: true
|
|
473
|
+
input_token_cost: 5e-6 # $5/M
|
|
474
|
+
output_token_cost: 2.5e-5 # $25/M
|
|
475
|
+
context_window: 1000000
|
|
476
|
+
providers:
|
|
477
|
+
openrouter:
|
|
478
|
+
api_model_name: anthropic/claude-opus-4.6
|
|
479
|
+
features:
|
|
480
|
+
provider_routing:
|
|
481
|
+
only: ["anthropic"]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Kannon For Deep Tech
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
"""Settings for dot-inference configuration via environment variables.
|
|
5
|
+
|
|
6
|
+
All environment variables are prefixed with ``DOTI_`` and use ``__`` as
|
|
7
|
+
nested delimiter. Provider-specific settings (model, api_key, base_url)
|
|
8
|
+
are grouped under the provider name::
|
|
9
|
+
|
|
10
|
+
DOTI_GENERATION__PROVIDER=openai
|
|
11
|
+
DOTI_GENERATION__OPENAI__MODEL_NAME=gpt-5
|
|
12
|
+
DOTI_GENERATION__OPENAI__API_KEY=sk-...
|
|
13
|
+
DOTI_GENERATION__LOCAL__MODEL_NAME=llama3.2:1b
|
|
14
|
+
DOTI_GENERATION__LOCAL__BASE_URL=http://localhost:11434/v1
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from enum import StrEnum, auto
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel, SecretStr, field_validator
|
|
22
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Provider(StrEnum):
|
|
26
|
+
"""Supported LLM providers."""
|
|
27
|
+
|
|
28
|
+
MISTRAL = auto()
|
|
29
|
+
OPENAI = auto()
|
|
30
|
+
OPENROUTER = auto()
|
|
31
|
+
AZURE = auto()
|
|
32
|
+
LOCAL = auto()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ReasoningEffort(StrEnum):
|
|
36
|
+
"""Reasoning effort levels for reasoning models (e.g. o-series, gpt-5's).
|
|
37
|
+
|
|
38
|
+
``NONE`` is the Mistral-style "minimal thinking, no thinking chunk"
|
|
39
|
+
setting (Mistral Medium 3.5 / Small accept only ``none`` and ``high``);
|
|
40
|
+
it is distinct from passing ``None``, which means "leave unset / use the
|
|
41
|
+
instance default". The ``low``/``medium``/``high`` ladder is the
|
|
42
|
+
OpenAI/Anthropic convention. Which subset a given model accepts is
|
|
43
|
+
declared per-model — see :func:`allowed_reasoning_efforts`.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
NONE = auto()
|
|
47
|
+
LOW = auto()
|
|
48
|
+
MEDIUM = auto()
|
|
49
|
+
HIGH = auto()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ProviderSettings(BaseModel):
|
|
53
|
+
"""Per-provider settings (model_name, api_key, base_url, ...).
|
|
54
|
+
|
|
55
|
+
``api_key`` is a :class:`~pydantic.SecretStr`, so it renders as
|
|
56
|
+
``**********`` in ``repr()``, ``str()`` and ``model_dump()`` — a settings
|
|
57
|
+
object caught in a traceback or an error report never carries the key in
|
|
58
|
+
clear text. Read the real value with ``api_key.get_secret_value()``.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
model_name: str | None = None
|
|
62
|
+
api_key: SecretStr | None = None
|
|
63
|
+
base_url: str | None = None
|
|
64
|
+
|
|
65
|
+
@field_validator("api_key", mode="before")
|
|
66
|
+
@classmethod
|
|
67
|
+
def _blank_key_is_no_key(cls, v: object) -> object:
|
|
68
|
+
"""Treat ``API_KEY=`` (set but empty) as absent.
|
|
69
|
+
|
|
70
|
+
Without this, an empty env var would yield ``SecretStr('')`` — a
|
|
71
|
+
truthy object — and the "is a key configured?" checks would wrongly
|
|
72
|
+
report yes.
|
|
73
|
+
"""
|
|
74
|
+
return None if isinstance(v, str) and not v.strip() else v
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class AzureProviderSettings(ProviderSettings):
|
|
78
|
+
"""Azure-specific provider settings."""
|
|
79
|
+
|
|
80
|
+
api_version: str = "2024-12-01-preview" # might be outdated, needs double checking
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class GenerationSettings(BaseModel):
|
|
84
|
+
"""Generation-related settings."""
|
|
85
|
+
|
|
86
|
+
mock: bool = False
|
|
87
|
+
provider: Provider = Provider.OPENROUTER
|
|
88
|
+
temperature: float = 0
|
|
89
|
+
max_tokens: int | None = None
|
|
90
|
+
reasoning_effort: ReasoningEffort | None = ReasoningEffort.MEDIUM
|
|
91
|
+
calls_per_minute_limit: int = 0 # no limit by default
|
|
92
|
+
timeout: float = 600 # HTTP timeout in seconds
|
|
93
|
+
|
|
94
|
+
# Per-provider settings
|
|
95
|
+
mistral: ProviderSettings = ProviderSettings()
|
|
96
|
+
openai: ProviderSettings = ProviderSettings()
|
|
97
|
+
openrouter: ProviderSettings = ProviderSettings()
|
|
98
|
+
azure: AzureProviderSettings = AzureProviderSettings()
|
|
99
|
+
local: ProviderSettings = ProviderSettings()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class EmbeddingsSettings(BaseModel):
|
|
103
|
+
"""Embeddings-related settings."""
|
|
104
|
+
|
|
105
|
+
mock: bool = False
|
|
106
|
+
provider: Provider = Provider.OPENAI
|
|
107
|
+
batch_size: int = 32
|
|
108
|
+
calls_per_minute_limit: int = 0 # no limit set here atm
|
|
109
|
+
timeout: float = 300 # HTTP timeout in seconds
|
|
110
|
+
|
|
111
|
+
# Per-provider settings
|
|
112
|
+
mistral: ProviderSettings = ProviderSettings()
|
|
113
|
+
openai: ProviderSettings = ProviderSettings()
|
|
114
|
+
openrouter: ProviderSettings = ProviderSettings()
|
|
115
|
+
azure: AzureProviderSettings = AzureProviderSettings()
|
|
116
|
+
local: ProviderSettings = ProviderSettings()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class InferenceSettings(BaseSettings):
|
|
120
|
+
"""Root settings combining generation and embedding configuration.
|
|
121
|
+
|
|
122
|
+
Instantiate explicitly — there is no module-level singleton::
|
|
123
|
+
|
|
124
|
+
from dot_inference.settings import InferenceSettings
|
|
125
|
+
|
|
126
|
+
settings = InferenceSettings() # reads from env
|
|
127
|
+
settings = InferenceSettings(_env_file="path/to/.env") # also reads a .env file
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
model_config = SettingsConfigDict(
|
|
131
|
+
env_prefix="DOTI_",
|
|
132
|
+
env_nested_delimiter="__",
|
|
133
|
+
env_file_encoding="utf-8",
|
|
134
|
+
extra="ignore",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
generation: GenerationSettings = GenerationSettings()
|
|
138
|
+
embedding: EmbeddingsSettings = EmbeddingsSettings()
|