smuod 0.9.2__tar.gz
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.
- smuod-0.9.2/.gitignore +24 -0
- smuod-0.9.2/.pre-commit-config.yaml +45 -0
- smuod-0.9.2/.python-version +1 -0
- smuod-0.9.2/LICENSE +20 -0
- smuod-0.9.2/PKG-INFO +222 -0
- smuod-0.9.2/README.md +186 -0
- smuod-0.9.2/pyproject.toml +127 -0
- smuod-0.9.2/src/smuod/__init__.py +8 -0
- smuod-0.9.2/src/smuod/database.py +104 -0
- smuod-0.9.2/src/smuod/db_helpers.py +99 -0
- smuod-0.9.2/src/smuod/langchain.py +33 -0
- smuod-0.9.2/src/smuod/logsetup.py +61 -0
- smuod-0.9.2/src/smuod/otelsetup.py +116 -0
- smuod-0.9.2/src/smuod/py.typed +0 -0
- smuod-0.9.2/src/smuod/pydantic_ai.py +70 -0
- smuod-0.9.2/src/smuod/pydantic_ai_client.py +63 -0
- smuod-0.9.2/src/smuod/settings.py +48 -0
- smuod-0.9.2/src/smuod/utils.py +32 -0
- smuod-0.9.2/tests/conftest.py +1 -0
- smuod-0.9.2/tests/test_azure_foundry_client.py +149 -0
- smuod-0.9.2/tests/test_db_engine.py +211 -0
- smuod-0.9.2/tests/test_logsetup.py +127 -0
- smuod-0.9.2/tests/test_pydantic_ai.py +121 -0
- smuod-0.9.2/uv.lock +4029 -0
smuod-0.9.2/.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Project
|
|
2
|
+
*.env
|
|
3
|
+
output*/
|
|
4
|
+
|
|
5
|
+
# Python
|
|
6
|
+
.venv/
|
|
7
|
+
__pycache__/
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
MANIFEST
|
|
15
|
+
|
|
16
|
+
# Jupyter Notebook
|
|
17
|
+
.ipynb_checkpoints/
|
|
18
|
+
*.ipynb
|
|
19
|
+
|
|
20
|
+
# IDE
|
|
21
|
+
.vscode/settings.json
|
|
22
|
+
|
|
23
|
+
# Autogenerated docs
|
|
24
|
+
docs/site
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
3
|
+
rev: 0.13.5
|
|
4
|
+
hooks:
|
|
5
|
+
# Audit project dependencies for known vulnerabilities
|
|
6
|
+
- id: uv-audit
|
|
7
|
+
name: Dependency vulnerability audit
|
|
8
|
+
args: [--locked, --no-dev]
|
|
9
|
+
always_run: true
|
|
10
|
+
|
|
11
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
12
|
+
rev: v0.17.2
|
|
13
|
+
hooks:
|
|
14
|
+
# Run the linter, automatically fixing issues when possible.
|
|
15
|
+
- id: ruff
|
|
16
|
+
name: Ruff linting
|
|
17
|
+
types_or: [python, pyi, jupyter]
|
|
18
|
+
args: [--fix]
|
|
19
|
+
# Run the formatter.
|
|
20
|
+
- id: ruff-format
|
|
21
|
+
name: Ruff formatting
|
|
22
|
+
types_or: [python, pyi, jupyter]
|
|
23
|
+
|
|
24
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
25
|
+
rev: v2.4.0
|
|
26
|
+
hooks:
|
|
27
|
+
- id: mypy
|
|
28
|
+
name: Mypy static type checker
|
|
29
|
+
additional_dependencies:
|
|
30
|
+
- pydantic
|
|
31
|
+
|
|
32
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
33
|
+
rev: v6.0.0
|
|
34
|
+
hooks:
|
|
35
|
+
- id: check-merge-conflict
|
|
36
|
+
- id: check-added-large-files
|
|
37
|
+
- id: end-of-file-fixer
|
|
38
|
+
- id: mixed-line-ending
|
|
39
|
+
args: ["--fix", "lf"]
|
|
40
|
+
|
|
41
|
+
- repo: https://github.com/kynan/nbstripout
|
|
42
|
+
rev: 0.9.1
|
|
43
|
+
hooks:
|
|
44
|
+
- id: nbstripout
|
|
45
|
+
name: Strip output from Jupyter notebooks
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
smuod-0.9.2/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Martin Høy (martin.hoy@dnv.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
6
|
+
software and associated documentation files (the "Software"), to deal in the Software
|
|
7
|
+
without restriction, including without limitation the rights to use, copy, modify,
|
|
8
|
+
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to the following
|
|
10
|
+
conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all copies or
|
|
13
|
+
substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
16
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
17
|
+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
|
19
|
+
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
20
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
smuod-0.9.2/PKG-INFO
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: smuod
|
|
3
|
+
Version: 0.9.2
|
|
4
|
+
Summary: Common utilities for SMUOD
|
|
5
|
+
Author-email: Martin Høy <martin.hoy@dnv.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: azure-identity>=1.26
|
|
10
|
+
Requires-Dist: colorama>=0.4
|
|
11
|
+
Requires-Dist: logfire[system-metrics]>=5.3
|
|
12
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.45
|
|
13
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.66b1
|
|
14
|
+
Requires-Dist: opentelemetry-instrumentation-httpx>=0.66b1
|
|
15
|
+
Requires-Dist: pydantic-settings>=2
|
|
16
|
+
Provides-Extra: langchain
|
|
17
|
+
Requires-Dist: langchain[openai]>=1.4; extra == 'langchain'
|
|
18
|
+
Provides-Extra: mssql
|
|
19
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.66b1; extra == 'mssql'
|
|
20
|
+
Requires-Dist: pyodbc>=5.3; extra == 'mssql'
|
|
21
|
+
Requires-Dist: sqlalchemy<2.1.0,>=2.0; extra == 'mssql'
|
|
22
|
+
Provides-Extra: postgres
|
|
23
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.66b1; extra == 'postgres'
|
|
24
|
+
Requires-Dist: psycopg[binary]>=3.3; extra == 'postgres'
|
|
25
|
+
Requires-Dist: sqlalchemy<2.1.0,>=2.0; extra == 'postgres'
|
|
26
|
+
Provides-Extra: pydantic-ai
|
|
27
|
+
Requires-Dist: httpx2>=2.16; extra == 'pydantic-ai'
|
|
28
|
+
Requires-Dist: pydantic-ai-harness>=0.61; extra == 'pydantic-ai'
|
|
29
|
+
Requires-Dist: pydantic-ai[typesafe]>=2.55; extra == 'pydantic-ai'
|
|
30
|
+
Provides-Extra: snowflake
|
|
31
|
+
Requires-Dist: snowflake-sqlalchemy>=2.1; extra == 'snowflake'
|
|
32
|
+
Provides-Extra: sqlalchemy
|
|
33
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.66b1; extra == 'sqlalchemy'
|
|
34
|
+
Requires-Dist: sqlalchemy<2.1.0,>=2.0; extra == 'sqlalchemy'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# smuod - Common utilities for the SMUOD team
|
|
38
|
+
|
|
39
|
+
Join the team, work SMOUDly.
|
|
40
|
+
|
|
41
|
+
# About this project
|
|
42
|
+
|
|
43
|
+
This repository contains common utilities and helpers used across the SMUOD team's
|
|
44
|
+
projects, including Azure Foundry access, database connection management, Logging and
|
|
45
|
+
OpenTelemetry instrumentation.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Installing
|
|
49
|
+
|
|
50
|
+
You can install this package using `uv`:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv add smuod
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Configuration variables
|
|
57
|
+
|
|
58
|
+
Each configuration variable is searched for in the following locations (the first value
|
|
59
|
+
wins):
|
|
60
|
+
|
|
61
|
+
- environment variables in the current shell
|
|
62
|
+
- configuration (.env) file given by environment variable `CONFIG_FILE` or `config.env`
|
|
63
|
+
- defaults set in the `smuod.settings` module.
|
|
64
|
+
|
|
65
|
+
Relevant environment variables:
|
|
66
|
+
| Environment Variable | Description |
|
|
67
|
+
|--------------------|-------------|
|
|
68
|
+
| `CONFIG_FILE` | Path to the configuration file to load environment variables from (default: `config.env`). |
|
|
69
|
+
| `AZURE_FOUNDRY_NAME` | Name of the Azure Foundry environment to use (default: `smuod-foundry-sweden`) |
|
|
70
|
+
| `LOGLEVEL_CONSOLE` | Log level for console output (default: `INFO`). |
|
|
71
|
+
| `LOGLEVEL_OTEL` | Log level for OpenTelemetry output (default: `INFO`). |
|
|
72
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Endpoint for the OpenTelemetry OTLP exporter (default: `""`). Example: `http://localhost:4317`. The port number is assumed to be the gRPC port, and the http endpoint is assumed to +1 higher. |
|
|
73
|
+
|
|
74
|
+
# How to use this package
|
|
75
|
+
|
|
76
|
+
## Logging and OpenTelemetry instrumentation
|
|
77
|
+
|
|
78
|
+
Put the following code e.g. in your top level `__init__.py` file of your package.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from smuod import configure_logging_console, configure_otel
|
|
82
|
+
|
|
83
|
+
configure_logging_console(__package__)
|
|
84
|
+
configure_otel()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Database engines
|
|
89
|
+
|
|
90
|
+
`create_db_engine` supports SQL Server (`mssql+pyodbc`), PostgreSQL
|
|
91
|
+
(`postgresql+psycopg`), and Snowflake (`snowflake`) SQLAlchemy URLs.
|
|
92
|
+
Other drivers raise `ValueError`. Install the corresponding extra:
|
|
93
|
+
`smuod[mssql]`, `smuod[postgres]`, or `smuod[snowflake]`.
|
|
94
|
+
|
|
95
|
+
SQL Server and PostgreSQL use Microsoft Entra tokens. Snowflake uses its
|
|
96
|
+
connector's authentication options directly; the `credential` argument is unused.
|
|
97
|
+
For example, to use Azure workload identity with Snowflake:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from smuod import create_db_engine
|
|
101
|
+
|
|
102
|
+
engine = create_db_engine(
|
|
103
|
+
"snowflake://@account.privatelink/database/schema"
|
|
104
|
+
"?authenticator=WORKLOAD_IDENTITY&workload_identity_provider=AZURE"
|
|
105
|
+
)
|
|
106
|
+
# Reuse the engine and dispose of it at application shutdown.
|
|
107
|
+
engine.dispose()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## LLM example code
|
|
111
|
+
|
|
112
|
+
Both the Pydantic AI and Langchain integrations are implmented with a `get_model`
|
|
113
|
+
function to obtain the appropriate model instance. And `get_embedder` for embedding
|
|
114
|
+
models.
|
|
115
|
+
|
|
116
|
+
The argument is a string that follows the Pydantic AI naming convention (e.g.
|
|
117
|
+
`provider:model-version`). If `provider` is omitted, the default provider is
|
|
118
|
+
`azure-responses`.
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
### Pydantic AI integration
|
|
122
|
+
|
|
123
|
+
The Pydantic AI integration is an optional dependency. Install the package with the
|
|
124
|
+
following command:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv add "smuod[pydantic_ai]"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The Pydantic AI client is configured with automatic retries for return codes 429, 502,
|
|
131
|
+
503, 504 and connection failures. It will respect Retry-After headers (capped at 300
|
|
132
|
+
seconds per wait), otherwise it uses exponential backoff. Upon retries, a WARNING will
|
|
133
|
+
be logged. After 5 attempts, the client will stop retrying and raise an exception.
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
```python
|
|
137
|
+
from smuod.pydantic_ai import get_embedder, get_model
|
|
138
|
+
|
|
139
|
+
embedder = get_embedder("text-embedding-3-large")
|
|
140
|
+
gpt6 = get_model("gpt-6-luna-2026-09-22")
|
|
141
|
+
grok = get_model("grok-4.7")
|
|
142
|
+
# Decision models
|
|
143
|
+
decision1 = get_model("typesafe:Decision-1")
|
|
144
|
+
openai_decisions = get_model("openai-decisions:gpt-6-luna-2026-09-22")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
An example Agent:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from pydantic_ai import Agent
|
|
151
|
+
|
|
152
|
+
from smuod.pydantic_ai import get_model
|
|
153
|
+
|
|
154
|
+
agent = Agent(get_model("gpt-5.6-luna-2026-07-09"))
|
|
155
|
+
response = await agent.run("Hello, world!")
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
An Agent that uses the Microsoft-Decision-1 model:
|
|
160
|
+
```python
|
|
161
|
+
from pydantic_ai import Agent
|
|
162
|
+
|
|
163
|
+
from smuod.pydantic_ai import get_model
|
|
164
|
+
|
|
165
|
+
agent = Agent(
|
|
166
|
+
get_model("typesafe:Decision-1"),
|
|
167
|
+
output_type=bool,
|
|
168
|
+
instructions="Does this request contain sensitive information?",
|
|
169
|
+
)
|
|
170
|
+
result = await agent.run("My credit card number is 1234-5678-9012-3456.")
|
|
171
|
+
print(result.output)
|
|
172
|
+
print(result.response.provider_details)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
An Agent that uses the OpenAI Decisions API:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from pydantic_ai import Agent
|
|
179
|
+
|
|
180
|
+
from smuod.pydantic_ai import get_model
|
|
181
|
+
|
|
182
|
+
agent = Agent(
|
|
183
|
+
get_model("openai-decisions:gpt-6-luna-2026-09-22"),
|
|
184
|
+
output_type=bool,
|
|
185
|
+
instructions="Does this request contain sensitive information?",
|
|
186
|
+
)
|
|
187
|
+
result = await agent.run("My credit card number is 1234-5678-9012-3456.")
|
|
188
|
+
print(result.output)
|
|
189
|
+
print(result.response.provider_details)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Langchain integration
|
|
193
|
+
|
|
194
|
+
The Langchain integration is an optional dependency. Install the package with the
|
|
195
|
+
following command:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
uv add "smuod[langchain]"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
203
|
+
|
|
204
|
+
from smuod.langchain import get_model
|
|
205
|
+
|
|
206
|
+
llm = get_model("gpt-6-luna-2026-09-22")
|
|
207
|
+
chain = llm | StrOutputParser()
|
|
208
|
+
chain.invoke("Hello, world!")
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
# Contributing
|
|
212
|
+
|
|
213
|
+
Contributions to this repository are welcome. Please follow these steps:
|
|
214
|
+
|
|
215
|
+
1. Clone / fork the repository.
|
|
216
|
+
2. Create a new branch for your feature or bugfix.
|
|
217
|
+
3. Commit your changes with clear and descriptive messages.
|
|
218
|
+
4. Add appropriate tests for your changes.
|
|
219
|
+
5. Push your changes to the remote repository.
|
|
220
|
+
6. Open a pull request against the main repository.
|
|
221
|
+
|
|
222
|
+
Please ensure that your code follows the existing style and includes appropriate tests.
|
smuod-0.9.2/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# smuod - Common utilities for the SMUOD team
|
|
2
|
+
|
|
3
|
+
Join the team, work SMOUDly.
|
|
4
|
+
|
|
5
|
+
# About this project
|
|
6
|
+
|
|
7
|
+
This repository contains common utilities and helpers used across the SMUOD team's
|
|
8
|
+
projects, including Azure Foundry access, database connection management, Logging and
|
|
9
|
+
OpenTelemetry instrumentation.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Installing
|
|
13
|
+
|
|
14
|
+
You can install this package using `uv`:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv add smuod
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Configuration variables
|
|
21
|
+
|
|
22
|
+
Each configuration variable is searched for in the following locations (the first value
|
|
23
|
+
wins):
|
|
24
|
+
|
|
25
|
+
- environment variables in the current shell
|
|
26
|
+
- configuration (.env) file given by environment variable `CONFIG_FILE` or `config.env`
|
|
27
|
+
- defaults set in the `smuod.settings` module.
|
|
28
|
+
|
|
29
|
+
Relevant environment variables:
|
|
30
|
+
| Environment Variable | Description |
|
|
31
|
+
|--------------------|-------------|
|
|
32
|
+
| `CONFIG_FILE` | Path to the configuration file to load environment variables from (default: `config.env`). |
|
|
33
|
+
| `AZURE_FOUNDRY_NAME` | Name of the Azure Foundry environment to use (default: `smuod-foundry-sweden`) |
|
|
34
|
+
| `LOGLEVEL_CONSOLE` | Log level for console output (default: `INFO`). |
|
|
35
|
+
| `LOGLEVEL_OTEL` | Log level for OpenTelemetry output (default: `INFO`). |
|
|
36
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Endpoint for the OpenTelemetry OTLP exporter (default: `""`). Example: `http://localhost:4317`. The port number is assumed to be the gRPC port, and the http endpoint is assumed to +1 higher. |
|
|
37
|
+
|
|
38
|
+
# How to use this package
|
|
39
|
+
|
|
40
|
+
## Logging and OpenTelemetry instrumentation
|
|
41
|
+
|
|
42
|
+
Put the following code e.g. in your top level `__init__.py` file of your package.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from smuod import configure_logging_console, configure_otel
|
|
46
|
+
|
|
47
|
+
configure_logging_console(__package__)
|
|
48
|
+
configure_otel()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Database engines
|
|
53
|
+
|
|
54
|
+
`create_db_engine` supports SQL Server (`mssql+pyodbc`), PostgreSQL
|
|
55
|
+
(`postgresql+psycopg`), and Snowflake (`snowflake`) SQLAlchemy URLs.
|
|
56
|
+
Other drivers raise `ValueError`. Install the corresponding extra:
|
|
57
|
+
`smuod[mssql]`, `smuod[postgres]`, or `smuod[snowflake]`.
|
|
58
|
+
|
|
59
|
+
SQL Server and PostgreSQL use Microsoft Entra tokens. Snowflake uses its
|
|
60
|
+
connector's authentication options directly; the `credential` argument is unused.
|
|
61
|
+
For example, to use Azure workload identity with Snowflake:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from smuod import create_db_engine
|
|
65
|
+
|
|
66
|
+
engine = create_db_engine(
|
|
67
|
+
"snowflake://@account.privatelink/database/schema"
|
|
68
|
+
"?authenticator=WORKLOAD_IDENTITY&workload_identity_provider=AZURE"
|
|
69
|
+
)
|
|
70
|
+
# Reuse the engine and dispose of it at application shutdown.
|
|
71
|
+
engine.dispose()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## LLM example code
|
|
75
|
+
|
|
76
|
+
Both the Pydantic AI and Langchain integrations are implmented with a `get_model`
|
|
77
|
+
function to obtain the appropriate model instance. And `get_embedder` for embedding
|
|
78
|
+
models.
|
|
79
|
+
|
|
80
|
+
The argument is a string that follows the Pydantic AI naming convention (e.g.
|
|
81
|
+
`provider:model-version`). If `provider` is omitted, the default provider is
|
|
82
|
+
`azure-responses`.
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### Pydantic AI integration
|
|
86
|
+
|
|
87
|
+
The Pydantic AI integration is an optional dependency. Install the package with the
|
|
88
|
+
following command:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv add "smuod[pydantic_ai]"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The Pydantic AI client is configured with automatic retries for return codes 429, 502,
|
|
95
|
+
503, 504 and connection failures. It will respect Retry-After headers (capped at 300
|
|
96
|
+
seconds per wait), otherwise it uses exponential backoff. Upon retries, a WARNING will
|
|
97
|
+
be logged. After 5 attempts, the client will stop retrying and raise an exception.
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
```python
|
|
101
|
+
from smuod.pydantic_ai import get_embedder, get_model
|
|
102
|
+
|
|
103
|
+
embedder = get_embedder("text-embedding-3-large")
|
|
104
|
+
gpt6 = get_model("gpt-6-luna-2026-09-22")
|
|
105
|
+
grok = get_model("grok-4.7")
|
|
106
|
+
# Decision models
|
|
107
|
+
decision1 = get_model("typesafe:Decision-1")
|
|
108
|
+
openai_decisions = get_model("openai-decisions:gpt-6-luna-2026-09-22")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
An example Agent:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from pydantic_ai import Agent
|
|
115
|
+
|
|
116
|
+
from smuod.pydantic_ai import get_model
|
|
117
|
+
|
|
118
|
+
agent = Agent(get_model("gpt-5.6-luna-2026-07-09"))
|
|
119
|
+
response = await agent.run("Hello, world!")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
An Agent that uses the Microsoft-Decision-1 model:
|
|
124
|
+
```python
|
|
125
|
+
from pydantic_ai import Agent
|
|
126
|
+
|
|
127
|
+
from smuod.pydantic_ai import get_model
|
|
128
|
+
|
|
129
|
+
agent = Agent(
|
|
130
|
+
get_model("typesafe:Decision-1"),
|
|
131
|
+
output_type=bool,
|
|
132
|
+
instructions="Does this request contain sensitive information?",
|
|
133
|
+
)
|
|
134
|
+
result = await agent.run("My credit card number is 1234-5678-9012-3456.")
|
|
135
|
+
print(result.output)
|
|
136
|
+
print(result.response.provider_details)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
An Agent that uses the OpenAI Decisions API:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from pydantic_ai import Agent
|
|
143
|
+
|
|
144
|
+
from smuod.pydantic_ai import get_model
|
|
145
|
+
|
|
146
|
+
agent = Agent(
|
|
147
|
+
get_model("openai-decisions:gpt-6-luna-2026-09-22"),
|
|
148
|
+
output_type=bool,
|
|
149
|
+
instructions="Does this request contain sensitive information?",
|
|
150
|
+
)
|
|
151
|
+
result = await agent.run("My credit card number is 1234-5678-9012-3456.")
|
|
152
|
+
print(result.output)
|
|
153
|
+
print(result.response.provider_details)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Langchain integration
|
|
157
|
+
|
|
158
|
+
The Langchain integration is an optional dependency. Install the package with the
|
|
159
|
+
following command:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
uv add "smuod[langchain]"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
167
|
+
|
|
168
|
+
from smuod.langchain import get_model
|
|
169
|
+
|
|
170
|
+
llm = get_model("gpt-6-luna-2026-09-22")
|
|
171
|
+
chain = llm | StrOutputParser()
|
|
172
|
+
chain.invoke("Hello, world!")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
# Contributing
|
|
176
|
+
|
|
177
|
+
Contributions to this repository are welcome. Please follow these steps:
|
|
178
|
+
|
|
179
|
+
1. Clone / fork the repository.
|
|
180
|
+
2. Create a new branch for your feature or bugfix.
|
|
181
|
+
3. Commit your changes with clear and descriptive messages.
|
|
182
|
+
4. Add appropriate tests for your changes.
|
|
183
|
+
5. Push your changes to the remote repository.
|
|
184
|
+
6. Open a pull request against the main repository.
|
|
185
|
+
|
|
186
|
+
Please ensure that your code follows the existing style and includes appropriate tests.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "smuod"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Common utilities for SMUOD"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [{ name = "Martin Høy", email = "martin.hoy@dnv.com" }]
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
dependencies = [
|
|
11
|
+
# Main dependencies
|
|
12
|
+
"colorama>=0.4",
|
|
13
|
+
"azure-identity>=1.26",
|
|
14
|
+
"pydantic-settings>=2",
|
|
15
|
+
# Instrumentation dependencies
|
|
16
|
+
"logfire[system-metrics]>=5.3",
|
|
17
|
+
"opentelemetry-exporter-otlp-proto-grpc>=1.45",
|
|
18
|
+
"opentelemetry-instrumentation-httpx>=0.66b1",
|
|
19
|
+
"opentelemetry-instrumentation-fastapi>=0.66b1",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
sqlalchemy = [
|
|
24
|
+
"sqlalchemy>=2.0,<2.1.0",
|
|
25
|
+
"opentelemetry-instrumentation-sqlalchemy>=0.66b1",
|
|
26
|
+
]
|
|
27
|
+
mssql = ["smuod[sqlalchemy]", "pyodbc>=5.3"]
|
|
28
|
+
postgres = ["smuod[sqlalchemy]", "psycopg[binary]>=3.3"]
|
|
29
|
+
snowflake = ["snowflake-sqlalchemy>=2.1"]
|
|
30
|
+
pydantic_ai = [
|
|
31
|
+
"pydantic-ai[typesafe]>=2.55",
|
|
32
|
+
"pydantic-ai-harness>=0.61",
|
|
33
|
+
"httpx2>=2.16",
|
|
34
|
+
]
|
|
35
|
+
langchain = ["langchain[openai]>=1.4"]
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
# Development dependencies are not installed in production
|
|
39
|
+
dev = [
|
|
40
|
+
"pre-commit>=4.1.0",
|
|
41
|
+
"pytest-cov>=6.0.0",
|
|
42
|
+
"ipykernel>=6.29.5",
|
|
43
|
+
"starlette>=1.10.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[build-system]
|
|
47
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
48
|
+
build-backend = "hatchling.build"
|
|
49
|
+
|
|
50
|
+
[tool.hatch.version]
|
|
51
|
+
source = "vcs"
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
# Consider packages in the src directory as first-party code
|
|
55
|
+
src = ["src"]
|
|
56
|
+
# Run ruff on Notebooks as well
|
|
57
|
+
extend-include = ["*.ipynb"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint.per-file-ignores]
|
|
60
|
+
# Allow the use of assert in tests
|
|
61
|
+
"tests/*" = ["S101"]
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint]
|
|
64
|
+
select = [
|
|
65
|
+
"F", # Pyflakes
|
|
66
|
+
"E", # pycodestyle
|
|
67
|
+
"W", # pycodestyle
|
|
68
|
+
"C90", # mccabe
|
|
69
|
+
"I", # isort
|
|
70
|
+
"ICN", # flake8-import-conventions
|
|
71
|
+
"N", # pep8-naming
|
|
72
|
+
"D", # pydocstyle
|
|
73
|
+
"UP", # pyupgrade
|
|
74
|
+
"ANN", # flake8-annotations
|
|
75
|
+
"S", # flake8-bandit
|
|
76
|
+
"B", # flake8-bugbear
|
|
77
|
+
"A", # flake8-builtins
|
|
78
|
+
"C4", # flake8-comprehensions
|
|
79
|
+
"DTZ", # flake8-datetimez
|
|
80
|
+
"FA", # flake8-future-annotations
|
|
81
|
+
"ISC", # flake8-implicit-str-concat
|
|
82
|
+
"ICN", # flake8-import-conventions
|
|
83
|
+
"PT", # flake8-pytest-style
|
|
84
|
+
"Q", # flake8-quotes
|
|
85
|
+
"RET", # flake8-return
|
|
86
|
+
"SIM", # flake8-simplify
|
|
87
|
+
"TID", # flake8-tidy-imports
|
|
88
|
+
"ARG", # flake8-unused-arguments
|
|
89
|
+
"PTH", # flake8-use-pathlib
|
|
90
|
+
"PD", # pandas-vet
|
|
91
|
+
"PLC", # Pylint convention
|
|
92
|
+
"PLE", # Pylint error
|
|
93
|
+
# "PLR", # Pylint refactor
|
|
94
|
+
"NPY", # NumPy
|
|
95
|
+
"FAST", # FastAPI
|
|
96
|
+
"RUF", # Ruff-specific rules
|
|
97
|
+
]
|
|
98
|
+
ignore = []
|
|
99
|
+
|
|
100
|
+
[tool.ruff.lint.pydocstyle]
|
|
101
|
+
convention = "google"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# mypy configuration, taken from pydantic documentation
|
|
105
|
+
[tool.mypy]
|
|
106
|
+
plugins = ["pydantic.mypy"]
|
|
107
|
+
|
|
108
|
+
follow_imports = "silent"
|
|
109
|
+
warn_redundant_casts = true
|
|
110
|
+
warn_unused_ignores = true
|
|
111
|
+
disallow_any_generics = true
|
|
112
|
+
check_untyped_defs = true
|
|
113
|
+
no_implicit_reexport = true
|
|
114
|
+
|
|
115
|
+
disallow_untyped_defs = true
|
|
116
|
+
|
|
117
|
+
[tool.pydantic-mypy]
|
|
118
|
+
init_forbid_extra = true
|
|
119
|
+
init_typed = true
|
|
120
|
+
warn_required_dynamic_aliases = true
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# pytest configuration
|
|
124
|
+
[tool.pytest.ini_options]
|
|
125
|
+
addopts = "--cov-report term-missing:skip-covered --cov=smuod"
|
|
126
|
+
# Ignore deprecation warnings from third party packages
|
|
127
|
+
filterwarnings = []
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Initialization of smuod."""
|
|
2
|
+
|
|
3
|
+
from smuod.database import create_db_engine
|
|
4
|
+
from smuod.logsetup import configure_logging_console
|
|
5
|
+
from smuod.otelsetup import configure_otel
|
|
6
|
+
|
|
7
|
+
# List of things to expose from this package.
|
|
8
|
+
__all__ = ["configure_logging_console", "configure_otel", "create_db_engine"]
|