minions-py 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.
- minions/__init__.py +64 -0
- minions/claude.py +286 -0
- minions/config.py +211 -0
- minions/gemini.py +309 -0
- minions/gpt.py +227 -0
- minions/grok.py +393 -0
- minions/mistral.py +464 -0
- minions_py-0.2.0.dist-info/METADATA +464 -0
- minions_py-0.2.0.dist-info/RECORD +11 -0
- minions_py-0.2.0.dist-info/WHEEL +5 -0
- minions_py-0.2.0.dist-info/top_level.txt +1 -0
minions/__init__.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'''
|
|
2
|
+
******************************************************************************************
|
|
3
|
+
Assembly: minions
|
|
4
|
+
Filename: __init__.py
|
|
5
|
+
Author: Terry D. Eppler
|
|
6
|
+
Created: 09-02-2026
|
|
7
|
+
|
|
8
|
+
Last Modified By: Terry D. Eppler
|
|
9
|
+
Last Modified On: 09-06-2026
|
|
10
|
+
******************************************************************************************
|
|
11
|
+
<copyright file="__init__.py" company="Terry D. Eppler">
|
|
12
|
+
|
|
13
|
+
__init__.py
|
|
14
|
+
Copyright © 2026 Terry D. Eppler
|
|
15
|
+
|
|
16
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
+
of this software and associated documentation files (the “Software”),
|
|
18
|
+
to deal in the Software without restriction,
|
|
19
|
+
including without limitation the rights to use, copy, modify, merge, publish,
|
|
20
|
+
distribute, sublicense, and/or sell copies of the Software,
|
|
21
|
+
and to permit persons to whom the Software is furnished to do so,
|
|
22
|
+
subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in all
|
|
25
|
+
copies or substantial portions of the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
28
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
29
|
+
PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
30
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
31
|
+
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
32
|
+
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
33
|
+
|
|
34
|
+
You can contact me at: terryeppler@gmail.com or eppler.terry@epa.gov
|
|
35
|
+
|
|
36
|
+
</copyright>
|
|
37
|
+
<summary>
|
|
38
|
+
Public package utilities for provider-specific Minions.
|
|
39
|
+
</summary>
|
|
40
|
+
******************************************************************************************
|
|
41
|
+
'''
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def throw_if( name: str, value: object ) -> None:
|
|
46
|
+
"""Validate a required value.
|
|
47
|
+
|
|
48
|
+
Purpose:
|
|
49
|
+
Raises a consistent error when a required value is empty.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
name (str): Argument name included in the error.
|
|
53
|
+
value (object): Value to validate.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
None: Validation succeeds without returning a value.
|
|
57
|
+
"""
|
|
58
|
+
if not value:
|
|
59
|
+
raise ValueError( f'Argument "{name}" cannot be empty!' )
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
__version__ = '0.2.0'
|
|
63
|
+
|
|
64
|
+
__all__: list[ str ] = [ 'throw_if' ]
|
minions/claude.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
'''
|
|
2
|
+
******************************************************************************************
|
|
3
|
+
Assembly: minions
|
|
4
|
+
Filename: claude.py
|
|
5
|
+
Author: Terry D. Eppler
|
|
6
|
+
Created: 09-05-2026
|
|
7
|
+
|
|
8
|
+
Last Modified By: Terry D. Eppler
|
|
9
|
+
Last Modified On: 09-06-2026
|
|
10
|
+
******************************************************************************************
|
|
11
|
+
<copyright file="claude.py" company="Terry D. Eppler">
|
|
12
|
+
|
|
13
|
+
claude.py
|
|
14
|
+
Copyright © 2026 Terry D. Eppler
|
|
15
|
+
|
|
16
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
+
of this software and associated documentation files (the “Software”),
|
|
18
|
+
to deal in the Software without restriction,
|
|
19
|
+
including without limitation the rights to use, copy, modify, merge, publish,
|
|
20
|
+
distribute, sublicense, and/or sell copies of the Software,
|
|
21
|
+
and to permit persons to whom the Software is furnished to do so,
|
|
22
|
+
subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in all
|
|
25
|
+
copies or substantial portions of the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
28
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
29
|
+
PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
30
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
31
|
+
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
32
|
+
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
33
|
+
|
|
34
|
+
You can contact me at: terryeppler@gmail.com or eppler.terry@epa.gov
|
|
35
|
+
|
|
36
|
+
</copyright>
|
|
37
|
+
<summary>
|
|
38
|
+
Anthropic Claude Minion implementations.
|
|
39
|
+
</summary>
|
|
40
|
+
******************************************************************************************
|
|
41
|
+
'''
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from collections.abc import Sequence
|
|
45
|
+
import asyncio
|
|
46
|
+
import os
|
|
47
|
+
|
|
48
|
+
from anthropic import Anthropic, AsyncAnthropic, beta_async_tool
|
|
49
|
+
from anthropic.lib.tools import (
|
|
50
|
+
BetaAsyncFunctionTool,
|
|
51
|
+
BetaAsyncStreamingToolRunner,
|
|
52
|
+
BetaFunctionTool,
|
|
53
|
+
)
|
|
54
|
+
from anthropic.types.beta import (
|
|
55
|
+
BetaCodeExecutionTool20260521Param,
|
|
56
|
+
BetaMessage,
|
|
57
|
+
BetaToolUnionParam,
|
|
58
|
+
BetaWebFetchTool20260318Param,
|
|
59
|
+
BetaWebSearchTool20260318Param,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
from . import throw_if
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
ClaudeTool = BetaFunctionTool | BetaToolUnionParam
|
|
66
|
+
ClaudeAsyncTool = BetaAsyncFunctionTool | BetaToolUnionParam
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class Minion:
|
|
70
|
+
"""Claude workflow agent backed by Anthropic's native tool runner."""
|
|
71
|
+
|
|
72
|
+
minion_name: str = 'Claude Minion'
|
|
73
|
+
|
|
74
|
+
def __init__( self, model: str, instructions: str,
|
|
75
|
+
tools: Sequence[ ClaudeTool ] | None=None, max_turns: int=10,
|
|
76
|
+
max_tokens: int=4096, api_key: str | None=None,
|
|
77
|
+
name: str | None=None ) -> None:
|
|
78
|
+
"""Initialize a Claude Minion.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
model (str): Claude model identifier.
|
|
82
|
+
instructions (str): System instructions for the agent.
|
|
83
|
+
tools (Sequence[ClaudeTool] | None): Optional Anthropic local or server tools.
|
|
84
|
+
max_turns (int): Maximum model iterations per execution.
|
|
85
|
+
max_tokens (int): Maximum output tokens per model iteration.
|
|
86
|
+
api_key (str | None): Optional Anthropic API key override.
|
|
87
|
+
name (str | None): Optional name overriding the implementation default.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
None: Configuration and native clients are stored.
|
|
91
|
+
"""
|
|
92
|
+
throw_if( 'model', model )
|
|
93
|
+
throw_if( 'instructions', instructions )
|
|
94
|
+
if max_turns < 1:
|
|
95
|
+
raise ValueError( 'Argument "max_turns" must be at least 1!' )
|
|
96
|
+
if max_tokens < 1:
|
|
97
|
+
raise ValueError( 'Argument "max_tokens" must be at least 1!' )
|
|
98
|
+
self.name = name or self.minion_name
|
|
99
|
+
throw_if( 'name', self.name )
|
|
100
|
+
self.model = model
|
|
101
|
+
self.instructions = instructions
|
|
102
|
+
self.tools = list( tools or [ ] )
|
|
103
|
+
self.max_turns = max_turns
|
|
104
|
+
self.max_tokens = max_tokens
|
|
105
|
+
self.api_key = api_key or os.getenv( 'ANTHROPIC_API_KEY' )
|
|
106
|
+
throw_if( 'api_key', self.api_key )
|
|
107
|
+
self.async_tools: list[ ClaudeAsyncTool ] = [
|
|
108
|
+
self.create_async_tool( tool ) if isinstance( tool, BetaFunctionTool ) else tool
|
|
109
|
+
for tool in self.tools
|
|
110
|
+
]
|
|
111
|
+
self.client = Anthropic( api_key=self.api_key )
|
|
112
|
+
self.async_client = AsyncAnthropic( api_key=self.api_key )
|
|
113
|
+
self.result: BetaMessage | BetaAsyncStreamingToolRunner[ object ] | None = None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def create_async_tool( self, tool: BetaFunctionTool ) -> BetaAsyncFunctionTool:
|
|
117
|
+
"""Create an async adapter without changing the provider schema.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
tool (BetaFunctionTool): Synchronous Anthropic beta tool.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
BetaAsyncFunctionTool: Schema-identical asynchronous tool.
|
|
124
|
+
"""
|
|
125
|
+
async def invoke( **arguments: object ) -> object:
|
|
126
|
+
return await asyncio.to_thread( tool.func, **arguments )
|
|
127
|
+
|
|
128
|
+
invoke.__name__ = tool.name
|
|
129
|
+
invoke.__doc__ = tool.description
|
|
130
|
+
return beta_async_tool( invoke, name=tool.name, description=tool.description,
|
|
131
|
+
input_schema=tool.input_schema, )
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def run( self, prompt: str ) -> BetaMessage:
|
|
135
|
+
"""Execute the complete Claude workflow synchronously.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
prompt (str): User input for the workflow.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
BetaMessage: Final provider-native message.
|
|
142
|
+
"""
|
|
143
|
+
throw_if( 'prompt', prompt )
|
|
144
|
+
runner = self.client.beta.messages.tool_runner( model=self.model,
|
|
145
|
+
max_tokens=self.max_tokens, max_iterations=self.max_turns, system=self.instructions,
|
|
146
|
+
tools=self.tools, messages=[ { 'role': 'user', 'content': prompt } ], )
|
|
147
|
+
result = runner.until_done( )
|
|
148
|
+
self.result = result
|
|
149
|
+
return self.result
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def run_async( self, prompt: str ) -> BetaMessage:
|
|
153
|
+
"""Execute the complete Claude workflow asynchronously.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
prompt (str): User input for the workflow.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
BetaMessage: Final provider-native message.
|
|
160
|
+
"""
|
|
161
|
+
throw_if( 'prompt', prompt )
|
|
162
|
+
runner = self.async_client.beta.messages.tool_runner(
|
|
163
|
+
model=self.model,
|
|
164
|
+
max_tokens=self.max_tokens,
|
|
165
|
+
max_iterations=self.max_turns,
|
|
166
|
+
system=self.instructions,
|
|
167
|
+
tools=self.async_tools,
|
|
168
|
+
messages=[ { 'role': 'user', 'content': prompt } ],
|
|
169
|
+
)
|
|
170
|
+
result = await runner.until_done( )
|
|
171
|
+
self.result = result
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def stream( self, prompt: str ) -> BetaAsyncStreamingToolRunner[ object ]:
|
|
176
|
+
"""Start Anthropic's complete async streaming tool runner.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
prompt (str): User input for the workflow.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
BetaAsyncStreamingToolRunner[object]: Native runner that streams and executes tools.
|
|
183
|
+
"""
|
|
184
|
+
throw_if( 'prompt', prompt )
|
|
185
|
+
runner = self.async_client.beta.messages.tool_runner(
|
|
186
|
+
model=self.model,
|
|
187
|
+
max_tokens=self.max_tokens,
|
|
188
|
+
max_iterations=self.max_turns,
|
|
189
|
+
system=self.instructions,
|
|
190
|
+
tools=self.async_tools,
|
|
191
|
+
messages=[ { 'role': 'user', 'content': prompt } ],
|
|
192
|
+
stream=True,
|
|
193
|
+
)
|
|
194
|
+
self.result = runner
|
|
195
|
+
return runner
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class DataMinion( Minion ):
|
|
199
|
+
"""Claude Minion specialized for data workflows."""
|
|
200
|
+
|
|
201
|
+
minion_name: str = 'Data Minion'
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class GovernanceMinion( Minion ):
|
|
205
|
+
"""Claude Minion specialized for governance workflows."""
|
|
206
|
+
|
|
207
|
+
minion_name: str = 'Governance Minion'
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class ResearchMinion( Minion ):
|
|
211
|
+
"""Claude Minion specialized for research workflows."""
|
|
212
|
+
|
|
213
|
+
minion_name: str = 'Research Minion'
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class CodingMinion( Minion ):
|
|
217
|
+
"""Claude Minion specialized for software workflows."""
|
|
218
|
+
|
|
219
|
+
minion_name: str = 'Coding Minion'
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class WritingMinion( Minion ):
|
|
223
|
+
"""Claude Minion specialized for writing workflows."""
|
|
224
|
+
|
|
225
|
+
minion_name: str = 'Writing Minion'
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class PlanningMinion( Minion ):
|
|
229
|
+
"""Claude Minion specialized for planning workflows."""
|
|
230
|
+
|
|
231
|
+
minion_name: str = 'Planning Minion'
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ComplianceMinion( Minion ):
|
|
235
|
+
"""Claude Minion specialized for compliance, legal, and budget workflows."""
|
|
236
|
+
|
|
237
|
+
minion_name: str = 'Compliance Minion'
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class BusinessMinion( Minion ):
|
|
241
|
+
"""Claude Minion specialized for business, finance, and marketing workflows."""
|
|
242
|
+
|
|
243
|
+
minion_name: str = 'Business Minion'
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class ImageGenerationMinion( Minion ):
|
|
247
|
+
"""Claude Minion specialized for image-generation workflows."""
|
|
248
|
+
|
|
249
|
+
minion_name: str = 'Image Generation Minion'
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class ImageAnalysisMinion( Minion ):
|
|
253
|
+
"""Claude Minion specialized for image-analysis workflows."""
|
|
254
|
+
|
|
255
|
+
minion_name: str = 'Image Analysis Minion'
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class ImageEditingMinion( Minion ):
|
|
259
|
+
"""Claude Minion specialized for image-editing workflows."""
|
|
260
|
+
|
|
261
|
+
minion_name: str = 'Image Editing Minion'
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class TranslationMinion( Minion ):
|
|
265
|
+
"""Claude Minion specialized for translation workflows."""
|
|
266
|
+
|
|
267
|
+
minion_name: str = 'Translation Minion'
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class TranscriptionMinion( Minion ):
|
|
271
|
+
"""Claude Minion specialized for transcription workflows."""
|
|
272
|
+
|
|
273
|
+
minion_name: str = 'Transcription Minion'
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class SpeechMinion( Minion ):
|
|
277
|
+
"""Claude Minion specialized for speech workflows."""
|
|
278
|
+
|
|
279
|
+
minion_name: str = 'Speech Minion'
|
|
280
|
+
|
|
281
|
+
__all__: list[ str ] = [ 'BetaCodeExecutionTool20260521Param',
|
|
282
|
+
'BetaWebFetchTool20260318Param', 'BetaWebSearchTool20260318Param', 'BusinessMinion',
|
|
283
|
+
'ClaudeAsyncTool', 'ClaudeTool', 'CodingMinion', 'ComplianceMinion', 'DataMinion',
|
|
284
|
+
'GovernanceMinion', 'ImageAnalysisMinion', 'ImageEditingMinion',
|
|
285
|
+
'ImageGenerationMinion', 'Minion', 'PlanningMinion', 'ResearchMinion', 'SpeechMinion',
|
|
286
|
+
'TranscriptionMinion', 'TranslationMinion', 'WritingMinion', ]
|
minions/config.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
'''
|
|
2
|
+
******************************************************************************************
|
|
3
|
+
Assembly: minions
|
|
4
|
+
Filename: config.py
|
|
5
|
+
Author: Terry D. Eppler
|
|
6
|
+
Created: 09-03-2026
|
|
7
|
+
|
|
8
|
+
Last Modified By: Terry D. Eppler
|
|
9
|
+
Last Modified On: 09-06-2026
|
|
10
|
+
******************************************************************************************
|
|
11
|
+
<copyright file="config.py" company="Terry D. Eppler">
|
|
12
|
+
|
|
13
|
+
config.py
|
|
14
|
+
Copyright © 2026 Terry D. Eppler
|
|
15
|
+
|
|
16
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
+
of this software and associated documentation files (the “Software”),
|
|
18
|
+
to deal in the Software without restriction,
|
|
19
|
+
including without limitation the rights to use, copy, modify, merge, publish,
|
|
20
|
+
distribute, sublicense, and/or sell copies of the Software,
|
|
21
|
+
and to permit persons to whom the Software is furnished to do so,
|
|
22
|
+
subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in all
|
|
25
|
+
copies or substantial portions of the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
28
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
29
|
+
PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
30
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
31
|
+
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
32
|
+
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
33
|
+
|
|
34
|
+
You can contact me at: terryeppler@gmail.com or eppler.terry@epa.gov
|
|
35
|
+
|
|
36
|
+
</copyright>
|
|
37
|
+
<summary>
|
|
38
|
+
Environment and model configuration for Minions.
|
|
39
|
+
</summary>
|
|
40
|
+
******************************************************************************************
|
|
41
|
+
'''
|
|
42
|
+
|
|
43
|
+
import os
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
# -------------- APP-LEVEL UTILITIES -------------
|
|
47
|
+
|
|
48
|
+
def throw_if( name: str, value: object ) -> None:
|
|
49
|
+
"""Raise a ``ValueError`` when a required value is empty.
|
|
50
|
+
|
|
51
|
+
Purpose:
|
|
52
|
+
Provides a small, consistent guard for required arguments and configuration values. The
|
|
53
|
+
function treats falsy values as invalid and raises a ``ValueError`` containing the
|
|
54
|
+
caller-supplied argument or setting name.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
name (str): Name of the argument or configuration value being validated.
|
|
58
|
+
value (object): Value to validate.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ValueError: Raised when ``value`` is falsy.
|
|
62
|
+
"""
|
|
63
|
+
if not value:
|
|
64
|
+
raise ValueError( f'Argument "{name}" cannot be empty!' )
|
|
65
|
+
|
|
66
|
+
def get_bool( name: str, default: bool = False ) -> bool:
|
|
67
|
+
"""Read a Boolean environment variable.
|
|
68
|
+
|
|
69
|
+
Purpose:
|
|
70
|
+
Converts environment-variable text into a deterministic Boolean value. Missing
|
|
71
|
+
variables return the caller-provided default. Values of ``1``, ``true``, ``yes``,
|
|
72
|
+
``y``, and ``on`` are treated as ``True``; all other defined values are treated as
|
|
73
|
+
``False``.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
name (str): Environment variable name.
|
|
77
|
+
default (bool): Default value used when the environment variable is not defined.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Parsed Boolean value, or the original default value when parsing fails.
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
throw_if( 'name', name )
|
|
84
|
+
value = os.getenv( name )
|
|
85
|
+
return default if value is None else value.strip( ).lower( ) in (
|
|
86
|
+
'1',
|
|
87
|
+
'true',
|
|
88
|
+
'yes',
|
|
89
|
+
'y',
|
|
90
|
+
'on'
|
|
91
|
+
)
|
|
92
|
+
except Exception:
|
|
93
|
+
return default
|
|
94
|
+
|
|
95
|
+
def get_int( name: str, default: int ) -> int:
|
|
96
|
+
"""Read an integer environment variable.
|
|
97
|
+
|
|
98
|
+
Purpose:
|
|
99
|
+
Parses an optional environment variable as an integer while preserving a safe
|
|
100
|
+
default when the variable is missing, empty, or invalid. This keeps module import
|
|
101
|
+
safe even when deployment configuration is incomplete.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
name (str): Environment variable name.
|
|
105
|
+
default (int): Default integer value used when parsing is not possible.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Parsed integer value or the supplied default value.
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
throw_if( 'name', name )
|
|
112
|
+
value = os.getenv( name )
|
|
113
|
+
return default if value in (None, '') else int( str( value ).strip( ) )
|
|
114
|
+
except Exception:
|
|
115
|
+
return default
|
|
116
|
+
|
|
117
|
+
def get_float( name: str, default: float ) -> float:
|
|
118
|
+
"""Read a floating-point environment variable.
|
|
119
|
+
|
|
120
|
+
Purpose:
|
|
121
|
+
Parses an optional environment variable as a float while preserving a safe default
|
|
122
|
+
when the variable is missing, empty, or invalid. This helper supports numeric
|
|
123
|
+
configuration without making module import dependent on perfect environment state.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
name (str): Environment variable name.
|
|
127
|
+
default (float): Default floating-point value used when parsing is not possible.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Parsed floating-point value or the supplied default value.
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
throw_if( 'name', name )
|
|
134
|
+
value = os.getenv( name )
|
|
135
|
+
return default if value in (None, '') else float( str( value ).strip( ) )
|
|
136
|
+
except Exception:
|
|
137
|
+
return default
|
|
138
|
+
|
|
139
|
+
def get_path( name: str, default: Path ) -> Path:
|
|
140
|
+
"""Read a path environment variable.
|
|
141
|
+
|
|
142
|
+
Purpose:
|
|
143
|
+
Resolves optional filesystem configuration from the environment. Missing variables
|
|
144
|
+
return the resolved default path, and invalid values fall back to the resolved
|
|
145
|
+
default path rather than interrupting module import.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
name (str): Environment variable name.
|
|
149
|
+
default (Path): Default path used when the environment variable is not defined.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Resolved path value or the resolved default path.
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
throw_if( 'name', name )
|
|
156
|
+
throw_if( 'default', default )
|
|
157
|
+
value = os.getenv( name )
|
|
158
|
+
return Path( value ).resolve( ) if value else default.resolve( )
|
|
159
|
+
except Exception:
|
|
160
|
+
return default.resolve( )
|
|
161
|
+
|
|
162
|
+
def get_text( name: str, default: str ) -> str:
|
|
163
|
+
"""Read a text environment variable.
|
|
164
|
+
|
|
165
|
+
Purpose:
|
|
166
|
+
Returns an environment variable as text while preserving the supplied default when
|
|
167
|
+
the variable is missing or empty. This keeps optional configuration centralized and
|
|
168
|
+
stable for callers that import the module early in application startup.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
name (str): Environment variable name.
|
|
172
|
+
default (str): Default text value.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Environment value or supplied default.
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
throw_if( 'name', name )
|
|
179
|
+
value = os.getenv( name )
|
|
180
|
+
return default if value in (None, '') else str( value )
|
|
181
|
+
except Exception:
|
|
182
|
+
return default
|
|
183
|
+
|
|
184
|
+
# ------ CONSTANTS -------------------
|
|
185
|
+
|
|
186
|
+
BASE_DIR = Path( __file__ ).resolve( ).parent
|
|
187
|
+
ROOT_DIR = Path( __file__ ).resolve( ).parent
|
|
188
|
+
LOG_DIR: Path = get_path( 'LOG_DIR', ROOT_DIR / 'logging' )
|
|
189
|
+
LOG_PATH: str = get_text( 'LOG_PATH', str( LOG_DIR / 'Exceptions.db' ) )
|
|
190
|
+
LOG_FILE: str = get_text( 'LOG_FILE', 'Exceptions' )
|
|
191
|
+
|
|
192
|
+
# ------ MODELS -------------------
|
|
193
|
+
|
|
194
|
+
GPT_MODELS = [ 'gpt-5.6-sol', 'gpt-5.6-luna', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4.1',
|
|
195
|
+
'gpt-4.1-mini', ]
|
|
196
|
+
|
|
197
|
+
GEMINI_MODELS = [ 'gemini-3.7-flash', 'gemini-3.6-flash', 'gemini-3.5-flash',
|
|
198
|
+
'gemini-3.5-flash-lite', 'gemini-3.1-pro-preview', 'gemini-3.1-flash-lite',
|
|
199
|
+
'gemini-3-flash-preview', 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite', ]
|
|
200
|
+
|
|
201
|
+
GROK_MODELS = [ 'grok-4.6', 'grok-4.5', 'grok-4.3', 'grok-4.20-multi-agent',
|
|
202
|
+
'grok-4.20-multi-agent-latest', ]
|
|
203
|
+
|
|
204
|
+
CLAUDE_MODELS = [ 'claude-fable-5', 'claude-opus-5', 'claude-opus-4-8', 'claude-opus-4-7',
|
|
205
|
+
'claude-opus-4-6', 'claude-sonnet-5', 'claude-sonnet-4-6', 'claude-sonnet-4-5-20250929',
|
|
206
|
+
'claude-haiku-4-5-20251001', ]
|
|
207
|
+
|
|
208
|
+
MISTRAL_MODELS = [ 'mistral-medium-latest', 'mistral-large-latest',
|
|
209
|
+
'mistral-small-latest', 'codestral-latest',
|
|
210
|
+
'ministral-14b-latest', 'ministral-8b-latest',
|
|
211
|
+
'ministral-3b-latest', ]
|