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.
@@ -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()