yera 0.1.1__py3-none-any.whl → 0.2.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.
Files changed (192) hide show
  1. infra_mvp/base_client.py +29 -0
  2. infra_mvp/base_server.py +68 -0
  3. infra_mvp/monitoring/__init__.py +15 -0
  4. infra_mvp/monitoring/metrics.py +185 -0
  5. infra_mvp/stream/README.md +56 -0
  6. infra_mvp/stream/__init__.py +14 -0
  7. infra_mvp/stream/__main__.py +101 -0
  8. infra_mvp/stream/agents/demos/financial/chart_additions_plan.md +170 -0
  9. infra_mvp/stream/agents/demos/financial/portfolio_assistant_stream.json +1571 -0
  10. infra_mvp/stream/agents/reference/blocks/action.json +170 -0
  11. infra_mvp/stream/agents/reference/blocks/button.json +66 -0
  12. infra_mvp/stream/agents/reference/blocks/date.json +65 -0
  13. infra_mvp/stream/agents/reference/blocks/input_prompt.json +94 -0
  14. infra_mvp/stream/agents/reference/blocks/layout.json +288 -0
  15. infra_mvp/stream/agents/reference/blocks/markdown.json +344 -0
  16. infra_mvp/stream/agents/reference/blocks/slider.json +67 -0
  17. infra_mvp/stream/agents/reference/blocks/spinner.json +110 -0
  18. infra_mvp/stream/agents/reference/blocks/table.json +56 -0
  19. infra_mvp/stream/agents/reference/chat_dynamics/branching_test_stream.json +145 -0
  20. infra_mvp/stream/app.py +49 -0
  21. infra_mvp/stream/container.py +112 -0
  22. infra_mvp/stream/schemas/__init__.py +16 -0
  23. infra_mvp/stream/schemas/agent.py +24 -0
  24. infra_mvp/stream/schemas/interaction.py +28 -0
  25. infra_mvp/stream/schemas/session.py +30 -0
  26. infra_mvp/stream/server.py +321 -0
  27. infra_mvp/stream/services/__init__.py +12 -0
  28. infra_mvp/stream/services/agent_service.py +40 -0
  29. infra_mvp/stream/services/event_converter.py +83 -0
  30. infra_mvp/stream/services/session_service.py +247 -0
  31. yera/__init__.py +50 -1
  32. yera/agents/__init__.py +2 -0
  33. yera/agents/context.py +41 -0
  34. yera/agents/dataclasses.py +69 -0
  35. yera/agents/decorator.py +207 -0
  36. yera/agents/discovery.py +124 -0
  37. yera/agents/typing/__init__.py +0 -0
  38. yera/agents/typing/coerce.py +408 -0
  39. yera/agents/typing/utils.py +19 -0
  40. yera/agents/typing/validate.py +206 -0
  41. yera/cli.py +377 -0
  42. yera/config/__init__.py +1 -0
  43. yera/config/config_utils.py +164 -0
  44. yera/config/function_config.py +55 -0
  45. yera/config/logging.py +18 -0
  46. yera/config/tool_config.py +8 -0
  47. yera/config2/__init__.py +8 -0
  48. yera/config2/dataclasses.py +534 -0
  49. yera/config2/keyring.py +270 -0
  50. yera/config2/paths.py +28 -0
  51. yera/config2/read.py +113 -0
  52. yera/config2/setup.py +109 -0
  53. yera/config2/setup_handlers/__init__.py +1 -0
  54. yera/config2/setup_handlers/anthropic.py +126 -0
  55. yera/config2/setup_handlers/azure.py +236 -0
  56. yera/config2/setup_handlers/base.py +125 -0
  57. yera/config2/setup_handlers/llama_cpp.py +205 -0
  58. yera/config2/setup_handlers/ollama.py +157 -0
  59. yera/config2/setup_handlers/openai.py +137 -0
  60. yera/config2/write.py +87 -0
  61. yera/dsl/__init__.py +0 -0
  62. yera/dsl/functions.py +94 -0
  63. yera/dsl/struct.py +20 -0
  64. yera/dsl/workspace.py +79 -0
  65. yera/events/__init__.py +57 -0
  66. yera/events/blocks/__init__.py +68 -0
  67. yera/events/blocks/action.py +57 -0
  68. yera/events/blocks/bar_chart.py +92 -0
  69. yera/events/blocks/base/__init__.py +20 -0
  70. yera/events/blocks/base/base.py +166 -0
  71. yera/events/blocks/base/chart.py +288 -0
  72. yera/events/blocks/base/layout.py +111 -0
  73. yera/events/blocks/buttons.py +37 -0
  74. yera/events/blocks/columns.py +26 -0
  75. yera/events/blocks/container.py +24 -0
  76. yera/events/blocks/date_picker.py +50 -0
  77. yera/events/blocks/exit.py +39 -0
  78. yera/events/blocks/form.py +24 -0
  79. yera/events/blocks/input_echo.py +22 -0
  80. yera/events/blocks/input_request.py +31 -0
  81. yera/events/blocks/line_chart.py +97 -0
  82. yera/events/blocks/markdown.py +67 -0
  83. yera/events/blocks/slider.py +54 -0
  84. yera/events/blocks/spinner.py +55 -0
  85. yera/events/blocks/system_prompt.py +22 -0
  86. yera/events/blocks/table.py +291 -0
  87. yera/events/models/__init__.py +39 -0
  88. yera/events/models/block_data.py +112 -0
  89. yera/events/models/in_event.py +7 -0
  90. yera/events/models/out_event.py +75 -0
  91. yera/events/runtime.py +187 -0
  92. yera/events/stream.py +91 -0
  93. yera/models/__init__.py +0 -0
  94. yera/models/data_classes.py +20 -0
  95. yera/models/llm_atlas_proxy.py +44 -0
  96. yera/models/llm_context.py +99 -0
  97. yera/models/llm_interfaces/__init__.py +0 -0
  98. yera/models/llm_interfaces/anthropic.py +153 -0
  99. yera/models/llm_interfaces/aws_bedrock.py +14 -0
  100. yera/models/llm_interfaces/azure_openai.py +143 -0
  101. yera/models/llm_interfaces/base.py +26 -0
  102. yera/models/llm_interfaces/interface_registry.py +74 -0
  103. yera/models/llm_interfaces/llama_cpp.py +136 -0
  104. yera/models/llm_interfaces/mock.py +29 -0
  105. yera/models/llm_interfaces/ollama_interface.py +118 -0
  106. yera/models/llm_interfaces/open_ai.py +150 -0
  107. yera/models/llm_workspace.py +19 -0
  108. yera/models/model_atlas.py +139 -0
  109. yera/models/model_definition.py +38 -0
  110. yera/models/model_factory.py +33 -0
  111. yera/opaque/__init__.py +9 -0
  112. yera/opaque/base.py +20 -0
  113. yera/opaque/decorator.py +8 -0
  114. yera/opaque/markdown.py +57 -0
  115. yera/opaque/opaque_function.py +25 -0
  116. yera/tools/__init__.py +29 -0
  117. yera/tools/atlas_tool.py +20 -0
  118. yera/tools/base.py +24 -0
  119. yera/tools/decorated_tool.py +18 -0
  120. yera/tools/decorator.py +35 -0
  121. yera/tools/tool_atlas.py +51 -0
  122. yera/tools/tool_utils.py +361 -0
  123. yera/ui/dist/404.html +1 -0
  124. yera/ui/dist/__next.__PAGE__.txt +10 -0
  125. yera/ui/dist/__next._full.txt +23 -0
  126. yera/ui/dist/__next._head.txt +6 -0
  127. yera/ui/dist/__next._index.txt +5 -0
  128. yera/ui/dist/__next._tree.txt +7 -0
  129. yera/ui/dist/_next/static/chunks/4c4688e1ff21ad98.js +1 -0
  130. yera/ui/dist/_next/static/chunks/652cd53c27924d50.js +4 -0
  131. yera/ui/dist/_next/static/chunks/786d2107b51e8499.css +1 -0
  132. yera/ui/dist/_next/static/chunks/7de9141b1af425c3.js +1 -0
  133. yera/ui/dist/_next/static/chunks/87ef65064d3524c1.js +2 -0
  134. yera/ui/dist/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  135. yera/ui/dist/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  136. yera/ui/dist/_next/static/chunks/c4c79d5d0b280aeb.js +1 -0
  137. yera/ui/dist/_next/static/chunks/dc2d2a247505d66f.css +5 -0
  138. yera/ui/dist/_next/static/chunks/f773f714b55ec620.js +37 -0
  139. yera/ui/dist/_next/static/chunks/turbopack-98b3031e1b1dbc33.js +4 -0
  140. yera/ui/dist/_next/static/lnhYLzJ1-a5EfNbW1uFF6/_buildManifest.js +11 -0
  141. yera/ui/dist/_next/static/lnhYLzJ1-a5EfNbW1uFF6/_clientMiddlewareManifest.json +1 -0
  142. yera/ui/dist/_next/static/lnhYLzJ1-a5EfNbW1uFF6/_ssgManifest.js +1 -0
  143. yera/ui/dist/_next/static/media/14e23f9b59180572-s.9c448f3c.woff2 +0 -0
  144. yera/ui/dist/_next/static/media/2a65768255d6b625-s.p.d19752fb.woff2 +0 -0
  145. yera/ui/dist/_next/static/media/2b2eb4836d2dad95-s.f36de3af.woff2 +0 -0
  146. yera/ui/dist/_next/static/media/31183d9fd602dc89-s.c4ff9b73.woff2 +0 -0
  147. yera/ui/dist/_next/static/media/3fcb63a1ac6a562e-s.2f77a576.woff2 +0 -0
  148. yera/ui/dist/_next/static/media/45ec8de98929b0f6-s.81056204.woff2 +0 -0
  149. yera/ui/dist/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  150. yera/ui/dist/_next/static/media/65c558afe41e89d6-s.e2c8389a.woff2 +0 -0
  151. yera/ui/dist/_next/static/media/67add6cc0f54b8cf-s.8ce53448.woff2 +0 -0
  152. yera/ui/dist/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  153. yera/ui/dist/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  154. yera/ui/dist/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  155. yera/ui/dist/_next/static/media/a8ff2d5d0ccb0d12-s.fc5b72a7.woff2 +0 -0
  156. yera/ui/dist/_next/static/media/aae5f0be330e13db-s.p.853e26d6.woff2 +0 -0
  157. yera/ui/dist/_next/static/media/b11a6ccf4a3edec7-s.2113d282.woff2 +0 -0
  158. yera/ui/dist/_next/static/media/b49b0d9b851e4899-s.4f3fa681.woff2 +0 -0
  159. yera/ui/dist/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  160. yera/ui/dist/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  161. yera/ui/dist/_next/static/media/favicon.0b3bf435.ico +0 -0
  162. yera/ui/dist/_not-found/__next._full.txt +14 -0
  163. yera/ui/dist/_not-found/__next._head.txt +6 -0
  164. yera/ui/dist/_not-found/__next._index.txt +5 -0
  165. yera/ui/dist/_not-found/__next._not-found.__PAGE__.txt +5 -0
  166. yera/ui/dist/_not-found/__next._not-found.txt +4 -0
  167. yera/ui/dist/_not-found/__next._tree.txt +2 -0
  168. yera/ui/dist/_not-found.html +1 -0
  169. yera/ui/dist/_not-found.txt +14 -0
  170. yera/ui/dist/agent-icon.svg +3 -0
  171. yera/ui/dist/favicon.ico +0 -0
  172. yera/ui/dist/file.svg +1 -0
  173. yera/ui/dist/globe.svg +1 -0
  174. yera/ui/dist/index.html +1 -0
  175. yera/ui/dist/index.txt +23 -0
  176. yera/ui/dist/logo/full_logo.png +0 -0
  177. yera/ui/dist/logo/rune_logo.png +0 -0
  178. yera/ui/dist/logo/rune_logo_borderless.png +0 -0
  179. yera/ui/dist/logo/text_logo.png +0 -0
  180. yera/ui/dist/next.svg +1 -0
  181. yera/ui/dist/send.png +0 -0
  182. yera/ui/dist/send_single.png +0 -0
  183. yera/ui/dist/vercel.svg +1 -0
  184. yera/ui/dist/window.svg +1 -0
  185. yera/utils/__init__.py +1 -0
  186. yera/utils/path_utils.py +38 -0
  187. yera-0.2.0.dist-info/METADATA +65 -0
  188. yera-0.2.0.dist-info/RECORD +190 -0
  189. {yera-0.1.1.dist-info → yera-0.2.0.dist-info}/WHEEL +1 -1
  190. yera-0.2.0.dist-info/entry_points.txt +2 -0
  191. yera-0.1.1.dist-info/METADATA +0 -11
  192. yera-0.1.1.dist-info/RECORD +0 -4
@@ -0,0 +1,534 @@
1
+ """Data classes for Yera's config."""
2
+
3
+ from typing import Literal
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+ MODEL_TYPES = ["llm", "embedding", "reranker", "image", "moderation", "tts", "stt"]
8
+
9
+ # ============================================================================
10
+ # Shared Components
11
+ # ============================================================================
12
+
13
+
14
+ class ModelProperties(BaseModel):
15
+ """Technical properties of a model."""
16
+
17
+ remote: bool = True
18
+ context_window: int | None = None # LLMs only
19
+
20
+ model_config = ConfigDict(extra="forbid")
21
+
22
+
23
+ # ============================================================================
24
+ # Global Defaults
25
+ # ============================================================================
26
+
27
+
28
+ class GlobalDefaults(BaseModel):
29
+ """Global default settings for Yera."""
30
+
31
+ max_retries: int = 3
32
+ timeout_seconds: int = 60
33
+ log_level: Literal["debug", "info", "warn", "error"] = "info"
34
+ enable_telemetry: bool = False
35
+
36
+ model_config = ConfigDict(extra="forbid")
37
+
38
+
39
+ # ============================================================================
40
+ # LLM Models
41
+ # ============================================================================
42
+
43
+
44
+ class LLMCapabilities(BaseModel):
45
+ """Capabilities specific to LLM models."""
46
+
47
+ function_calling: bool = False
48
+ structured_generation: bool = False
49
+ vision: bool = False
50
+ reasoning: bool = False
51
+ multilingual: bool = False
52
+
53
+ model_config = ConfigDict(extra="forbid")
54
+
55
+
56
+ class LLMCost(BaseModel):
57
+ """Cost structure for LLM models."""
58
+
59
+ input_tokens_per_million: float
60
+ output_tokens_per_million: float
61
+
62
+ model_config = ConfigDict(extra="forbid")
63
+
64
+
65
+ class LLMInference(BaseModel):
66
+ """Runtime inference parameters for LLM models."""
67
+
68
+ temperature: float | None = None
69
+ max_tokens: int | None = None
70
+ top_p: float | None = None
71
+ top_k: int | None = None
72
+ stop_sequences: list[str] = Field(default_factory=list)
73
+
74
+ model_config = ConfigDict(extra="allow")
75
+
76
+
77
+ class LLMConfig(BaseModel):
78
+ """Configuration for a Language Model."""
79
+
80
+ id: str # Full identifier like "anthropic.claude-sonnet-4"
81
+ display_name: str
82
+ credentials: str
83
+ provider: str
84
+ interface: str
85
+ model_id: str
86
+ properties: ModelProperties = Field(default_factory=ModelProperties)
87
+ cost: LLMCost | None = None
88
+ capabilities: LLMCapabilities = Field(default_factory=LLMCapabilities)
89
+ inference: LLMInference = Field(default_factory=LLMInference)
90
+
91
+ model_config = ConfigDict(extra="forbid")
92
+
93
+
94
+ # ============================================================================
95
+ # Embedding Models
96
+ # ============================================================================
97
+
98
+
99
+ class EmbeddingCapabilities(BaseModel):
100
+ """Capabilities specific to embedding models."""
101
+
102
+ cosine_similarity: bool = True
103
+ dot_product: bool = True
104
+ euclidean_distance: bool = False
105
+
106
+ model_config = ConfigDict(extra="forbid")
107
+
108
+
109
+ class EmbeddingCost(BaseModel):
110
+ """Cost structure for embedding models."""
111
+
112
+ tokens_per_million: float
113
+
114
+ model_config = ConfigDict(extra="forbid")
115
+
116
+
117
+ class EmbeddingInference(BaseModel):
118
+ """Runtime inference parameters for embedding models."""
119
+
120
+ batch_size: int | None = None
121
+ dimensions: int | None = None # For models that support configurable dimensions
122
+
123
+ model_config = ConfigDict(extra="forbid")
124
+
125
+
126
+ class EmbeddingModelConfig(BaseModel):
127
+ """Configuration for an embedding model."""
128
+
129
+ id: str # Full identifier like "openai.text-embedding-3-small"
130
+ credentials: str
131
+ provider: str
132
+ interface: str
133
+ model_id: str
134
+ dimensions: int
135
+ max_input_tokens: int
136
+ status: Literal["stable", "beta", "deprecated"] = "stable"
137
+ properties: ModelProperties
138
+ cost: EmbeddingCost
139
+ capabilities: EmbeddingCapabilities = Field(default_factory=EmbeddingCapabilities)
140
+ inference: EmbeddingInference = Field(default_factory=EmbeddingInference)
141
+
142
+ model_config = ConfigDict(extra="forbid")
143
+
144
+
145
+ # ============================================================================
146
+ # Reranker Models
147
+ # ============================================================================
148
+
149
+
150
+ class RerankerCapabilities(BaseModel):
151
+ """Capabilities specific to reranker models."""
152
+
153
+ multilingual: bool = False
154
+ context_aware: bool = True
155
+
156
+ model_config = ConfigDict(extra="forbid")
157
+
158
+
159
+ class RerankerCost(BaseModel):
160
+ """Cost structure for reranker models."""
161
+
162
+ per_thousand_searches: float
163
+
164
+ model_config = ConfigDict(extra="forbid")
165
+
166
+
167
+ class RerankerInference(BaseModel):
168
+ """Runtime inference parameters for reranker models."""
169
+
170
+ top_n: int | None = None
171
+ return_documents: bool = True
172
+
173
+ model_config = ConfigDict(extra="forbid")
174
+
175
+
176
+ class RerankerModelConfig(BaseModel):
177
+ """Configuration for a reranker model."""
178
+
179
+ id: str # Full identifier like "cohere.rerank-english-v3"
180
+ credentials: str
181
+ provider: str
182
+ interface: str
183
+ model_id: str
184
+ max_documents: int
185
+ max_query_length: int
186
+ status: Literal["stable", "beta", "deprecated"] = "stable"
187
+ properties: ModelProperties
188
+ cost: RerankerCost
189
+ capabilities: RerankerCapabilities = Field(default_factory=RerankerCapabilities)
190
+ inference: RerankerInference = Field(default_factory=RerankerInference)
191
+
192
+ model_config = ConfigDict(extra="forbid")
193
+
194
+
195
+ # ============================================================================
196
+ # Image Generation Models
197
+ # ============================================================================
198
+
199
+
200
+ class ImageCapabilities(BaseModel):
201
+ """Capabilities specific to image generation models."""
202
+
203
+ supports_variations: bool = False
204
+ supports_edits: bool = False
205
+ supports_transparent_background: bool = False
206
+
207
+ model_config = ConfigDict(extra="forbid")
208
+
209
+
210
+ class ImageCost(BaseModel):
211
+ """Cost structure for image generation models."""
212
+
213
+ per_image_standard: float | None = None
214
+ per_image_hd: float | None = None
215
+ per_image_1024: float | None = None # DALL-E 2 pricing
216
+ per_image_512: float | None = None
217
+ per_image_256: float | None = None
218
+
219
+ model_config = ConfigDict(extra="forbid")
220
+
221
+
222
+ class ImageInference(BaseModel):
223
+ """Runtime inference parameters for image generation models."""
224
+
225
+ size: str | None = None
226
+ quality: str | None = None
227
+ style: str | None = None
228
+ n: int = 1 # Number of images to generate
229
+
230
+ model_config = ConfigDict(extra="forbid")
231
+
232
+
233
+ class ImageModelConfig(BaseModel):
234
+ """Configuration for an image generation model."""
235
+
236
+ id: str # Full identifier like "openai.dall-e-3"
237
+ credentials: str
238
+ provider: str
239
+ interface: str
240
+ model_id: str
241
+ supported_sizes: list[str]
242
+ supported_qualities: list[str]
243
+ supported_styles: list[str] = Field(default_factory=list)
244
+ status: Literal["stable", "beta", "deprecated"] = "stable"
245
+ properties: ModelProperties
246
+ cost: ImageCost
247
+ capabilities: ImageCapabilities = Field(default_factory=ImageCapabilities)
248
+ inference: ImageInference = Field(default_factory=ImageInference)
249
+
250
+ model_config = ConfigDict(extra="forbid")
251
+
252
+
253
+ # ============================================================================
254
+ # Moderation Models
255
+ # ============================================================================
256
+
257
+
258
+ class ModerationCapabilities(BaseModel):
259
+ """Capabilities specific to moderation models."""
260
+
261
+ categories: list[str] = Field(default_factory=list)
262
+
263
+ model_config = ConfigDict(extra="forbid")
264
+
265
+
266
+ class ModerationCost(BaseModel):
267
+ """Cost structure for moderation models."""
268
+
269
+ free: bool = True
270
+
271
+ model_config = ConfigDict(extra="forbid")
272
+
273
+
274
+ class ModerationInference(BaseModel):
275
+ """Runtime inference parameters for moderation models."""
276
+
277
+ # Most moderation models have no configurable inference params
278
+ model_config = ConfigDict(extra="forbid")
279
+
280
+
281
+ class ModerationModelConfig(BaseModel):
282
+ """Configuration for a moderation model."""
283
+
284
+ id: str # Full identifier like "openai.text-moderation-latest"
285
+ credentials: str
286
+ provider: str
287
+ interface: str
288
+ model_id: str
289
+ status: Literal["stable", "beta", "deprecated"] = "stable"
290
+ properties: ModelProperties
291
+ cost: ModerationCost
292
+ capabilities: ModerationCapabilities = Field(default_factory=ModerationCapabilities)
293
+ inference: ModerationInference = Field(default_factory=ModerationInference)
294
+
295
+ model_config = ConfigDict(extra="forbid")
296
+
297
+
298
+ # ============================================================================
299
+ # Text-to-Speech Models
300
+ # ============================================================================
301
+
302
+
303
+ class TTSCapabilities(BaseModel):
304
+ """Capabilities specific to TTS models."""
305
+
306
+ streaming: bool = False
307
+ speed_control: bool = False
308
+ voice_cloning: bool = False
309
+ emotion_control: bool = False
310
+
311
+ model_config = ConfigDict(extra="forbid")
312
+
313
+
314
+ class TTSCost(BaseModel):
315
+ """Cost structure for TTS models."""
316
+
317
+ chars_per_million: float
318
+
319
+ model_config = ConfigDict(extra="forbid")
320
+
321
+
322
+ class TTSInference(BaseModel):
323
+ """Runtime inference parameters for TTS models."""
324
+
325
+ voice: str | None = None
326
+ speed: float | None = None
327
+ format: str | None = None # mp3, opus, etc.
328
+ sample_rate: int | None = None
329
+
330
+ model_config = ConfigDict(extra="forbid")
331
+
332
+
333
+ class TTSModelConfig(BaseModel):
334
+ """Configuration for a text-to-speech model."""
335
+
336
+ id: str # Full identifier like "openai.tts-1"
337
+ credentials: str
338
+ provider: str
339
+ interface: str
340
+ model_id: str
341
+ voices: list[str] = Field(default_factory=list)
342
+ default_voice: str = ""
343
+ supported_formats: list[str] = Field(default_factory=list)
344
+ sample_rates: list[int] = Field(default_factory=list)
345
+ status: Literal["stable", "beta", "deprecated"] = "stable"
346
+ properties: ModelProperties
347
+ cost: TTSCost
348
+ capabilities: TTSCapabilities = Field(default_factory=TTSCapabilities)
349
+ inference: TTSInference = Field(default_factory=TTSInference)
350
+
351
+ model_config = ConfigDict(extra="forbid")
352
+
353
+
354
+ # ============================================================================
355
+ # Speech-to-Text Models
356
+ # ============================================================================
357
+
358
+
359
+ class STTCapabilities(BaseModel):
360
+ """Capabilities specific to STT models."""
361
+
362
+ timestamps: bool = False
363
+ translation: bool = False
364
+ language_detection: bool = False
365
+
366
+ model_config = ConfigDict(extra="forbid")
367
+
368
+
369
+ class STTCost(BaseModel):
370
+ """Cost structure for STT models."""
371
+
372
+ per_minute: float
373
+
374
+ model_config = ConfigDict(extra="forbid")
375
+
376
+
377
+ class STTInference(BaseModel):
378
+ """Runtime inference parameters for STT models."""
379
+
380
+ language: str | None = None
381
+ enable_timestamps: bool = False
382
+ response_format: str | None = None # json, text, srt, vtt
383
+
384
+ model_config = ConfigDict(extra="forbid")
385
+
386
+
387
+ class STTModelConfig(BaseModel):
388
+ """Configuration for a speech-to-text model."""
389
+
390
+ id: str # Full identifier like "openai.whisper-1"
391
+ credentials: str
392
+ provider: str
393
+ interface: str
394
+ model_id: str
395
+ supported_formats: list[str] = Field(default_factory=list)
396
+ max_file_size_mb: float = 25.0
397
+ languages: list[str] = Field(default_factory=list)
398
+ status: Literal["stable", "beta", "deprecated"] = "stable"
399
+ properties: ModelProperties
400
+ cost: STTCost
401
+ capabilities: STTCapabilities = Field(default_factory=STTCapabilities)
402
+ inference: STTInference = Field(default_factory=STTInference)
403
+
404
+ model_config = ConfigDict(extra="forbid")
405
+
406
+
407
+ # ============================================================================
408
+ # Model Registry
409
+ # ============================================================================
410
+
411
+
412
+ class ModelRegistry(BaseModel):
413
+ """Registry of all configured models by type.
414
+
415
+ Models are keyed by their full dot-path identifier (e.g.,
416
+ "anthropic.claude-sonnet-4").
417
+
418
+ """
419
+
420
+ llm: dict[str, LLMConfig] = Field(default_factory=dict)
421
+ embedding: dict[str, EmbeddingModelConfig] = Field(default_factory=dict)
422
+ reranker: dict[str, RerankerModelConfig] = Field(default_factory=dict)
423
+ image: dict[str, ImageModelConfig] = Field(default_factory=dict)
424
+ moderation: dict[str, ModerationModelConfig] = Field(default_factory=dict)
425
+ tts: dict[str, TTSModelConfig] = Field(default_factory=dict)
426
+ stt: dict[str, STTModelConfig] = Field(default_factory=dict)
427
+
428
+ model_config = ConfigDict(extra="forbid")
429
+
430
+
431
+ # ============================================================================
432
+ # Top-Level Configuration
433
+ # ============================================================================
434
+
435
+
436
+ class DefaultModels(BaseModel):
437
+ """Default model selections by type."""
438
+
439
+ llm: str | None = None
440
+ embedding: str | None = None
441
+ reranker: str | None = None
442
+ image: str | None = None
443
+ tts: str | None = None
444
+ stt: str | None = None
445
+ moderation: str | None = None
446
+
447
+ model_config = ConfigDict(extra="forbid")
448
+
449
+
450
+ class Defaults(BaseModel):
451
+ """Yera configuration defaults section."""
452
+
453
+ max_retries: int = 3
454
+ timeout_seconds: int = 60
455
+ log_level: Literal["debug", "info", "warn", "error"] = "info"
456
+ enable_telemetry: bool = False
457
+
458
+ models: DefaultModels = Field(default_factory=DefaultModels)
459
+
460
+ model_config = ConfigDict(extra="forbid")
461
+
462
+
463
+ class CredentialsMap(BaseModel):
464
+ """Credentials for Yera configuration."""
465
+
466
+ providers: dict[str, dict[str, str]] = Field(default_factory=dict)
467
+ tools: dict[str, dict[str, str]] = Field(default_factory=dict)
468
+
469
+
470
+ class YeraConfig(BaseModel):
471
+ """Complete Yera configuration.
472
+
473
+ This is the root configuration object that contains all provider credentials,
474
+ model definitions, and default selections.
475
+ """
476
+
477
+ defaults: Defaults = Field(default_factory=Defaults)
478
+ credentials: CredentialsMap = Field(default_factory=CredentialsMap)
479
+ models: ModelRegistry = Field(default_factory=ModelRegistry)
480
+
481
+ model_config = ConfigDict(extra="forbid")
482
+
483
+ def merge(self, other: "YeraConfig") -> "YeraConfig":
484
+ """Merge this configuration with another, creating a new config.
485
+
486
+ Creates a new YeraConfig by combining this configuration with another,
487
+ where values from `other` take precedence in case of conflicts. This is
488
+ for extending and updating yera config.
489
+
490
+ Args:
491
+ other: Another YeraConfig to merge with this one. Values from this
492
+ config will override values from self when keys conflict.
493
+
494
+ Returns:
495
+ A new YeraConfig instance containing the merged configuration. The
496
+ original configs (self and other) are not modified.
497
+
498
+ """
499
+ merged_defaults = {
500
+ **self.defaults.model_dump(),
501
+ **other.defaults.model_dump(),
502
+ }
503
+ merged_default_models = {
504
+ **self.defaults.models.model_dump(),
505
+ **other.defaults.models.model_dump(),
506
+ }
507
+ defaults = self.defaults.model_validate(
508
+ {
509
+ **merged_defaults,
510
+ "models": merged_default_models,
511
+ }
512
+ )
513
+
514
+ providers_merge = {**self.credentials.providers, **other.credentials.providers}
515
+ tools_merge = {**self.credentials.tools, **other.credentials.tools}
516
+ credentials = self.credentials.model_copy(
517
+ update={
518
+ "providers": providers_merge,
519
+ "tools": tools_merge,
520
+ }
521
+ )
522
+
523
+ model_reg1 = self.models.model_dump()
524
+ model_reg2 = other.models.model_dump()
525
+ models_merge = {}
526
+ for k in model_reg1:
527
+ models_merge[k] = {**model_reg1[k], **model_reg2[k]}
528
+ models = ModelRegistry(**models_merge)
529
+
530
+ return YeraConfig(
531
+ defaults=defaults,
532
+ credentials=credentials,
533
+ models=models,
534
+ )