structguard 0.1.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.
- structguard-0.1.2/.env.example +25 -0
- structguard-0.1.2/LICENSE +21 -0
- structguard-0.1.2/PKG-INFO +126 -0
- structguard-0.1.2/README.md +84 -0
- structguard-0.1.2/examples/README.md +42 -0
- structguard-0.1.2/examples/anthropic_example.py +63 -0
- structguard-0.1.2/examples/dataclass_example.py +54 -0
- structguard-0.1.2/examples/error_handling_example.py +80 -0
- structguard-0.1.2/examples/gemini_example.py +78 -0
- structguard-0.1.2/examples/groq_example.py +77 -0
- structguard-0.1.2/examples/json_schema_example.py +53 -0
- structguard-0.1.2/examples/ollama_example.py +67 -0
- structguard-0.1.2/examples/openai_example.py +63 -0
- structguard-0.1.2/examples/typeddict_example.py +49 -0
- structguard-0.1.2/pyproject.toml +49 -0
- structguard-0.1.2/src/structguard/__init__.py +34 -0
- structguard-0.1.2/src/structguard/adapters/__init__.py +35 -0
- structguard-0.1.2/src/structguard/adapters/anthropic_adapter.py +34 -0
- structguard-0.1.2/src/structguard/adapters/base.py +42 -0
- structguard-0.1.2/src/structguard/adapters/gemini_adapter.py +36 -0
- structguard-0.1.2/src/structguard/adapters/groq_adapter.py +38 -0
- structguard-0.1.2/src/structguard/adapters/ollama_adapter.py +35 -0
- structguard-0.1.2/src/structguard/adapters/openai_adapter.py +38 -0
- structguard-0.1.2/src/structguard/core.py +153 -0
- structguard-0.1.2/src/structguard/exceptions.py +62 -0
- structguard-0.1.2/src/structguard/logging_utils.py +47 -0
- structguard-0.1.2/src/structguard/repair.py +125 -0
- structguard-0.1.2/src/structguard/report.py +62 -0
- structguard-0.1.2/src/structguard/retry.py +15 -0
- structguard-0.1.2/src/structguard/schema/__init__.py +3 -0
- structguard-0.1.2/src/structguard/schema/engine.py +100 -0
- structguard-0.1.2/src/structguard/validator.py +59 -0
- structguard-0.1.2/tests/test_core.py +74 -0
- structguard-0.1.2/tests/test_repair.py +47 -0
- structguard-0.1.2/tests/test_schema_engine.py +71 -0
- structguard-0.1.2/tests/test_validator.py +40 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# ---------------------------------------------------------------------------
|
|
2
|
+
# StructGuard examples — dummy environment file
|
|
3
|
+
# Copy to `.env` (already named .env here) and replace with your real keys.
|
|
4
|
+
# Loaded automatically by each example via python-dotenv's load_dotenv().
|
|
5
|
+
# NEVER commit a real .env file to version control — add it to .gitignore.
|
|
6
|
+
# ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
# --- Groq ---
|
|
9
|
+
GROQ_API_KEY=gsk_dummy_replace_with_your_real_groq_api_key
|
|
10
|
+
GROQ_MODEL=llama-3.3-70b-versatile # Replace with any other model
|
|
11
|
+
|
|
12
|
+
# --- OpenAI ---
|
|
13
|
+
OPENAI_API_KEY=sk-dummy_replace_with_your_real_openai_api_key
|
|
14
|
+
OPENAI_MODEL=gpt-4o-mini # Replace with any other model
|
|
15
|
+
|
|
16
|
+
# --- Anthropic ---
|
|
17
|
+
ANTHROPIC_API_KEY=sk-ant-dummy_replace_with_your_real_anthropic_api_key
|
|
18
|
+
ANTHROPIC_MODEL=claude-sonnet-4-6 # Replace with any other model
|
|
19
|
+
|
|
20
|
+
# --- Google Gemini ---
|
|
21
|
+
GOOGLE_API_KEY=dummy_replace_with_your_real_google_api_key
|
|
22
|
+
GEMINI_MODEL=gemini-1.5-pro # Replace with any other model
|
|
23
|
+
|
|
24
|
+
# --- Ollama (local, no key needed, only model name matters) ---
|
|
25
|
+
OLLAMA_MODEL=llama3.1 # Replace with any other model
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sujan Ghosh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: structguard
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Reliable, provider-agnostic structured outputs for production LLM applications.
|
|
5
|
+
Project-URL: Homepage, https://github.com/yourusername/structguard
|
|
6
|
+
Project-URL: Repository, https://github.com/yourusername/structguard
|
|
7
|
+
Project-URL: Issues, https://github.com/yourusername/structguard/issues
|
|
8
|
+
Author-email: Sujan Ghosh <rupubally@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: anthropic,gemini,json,llm,openai,pydantic,structured-output,validation
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: pydantic>=2.0
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: anthropic>=0.30; extra == 'all'
|
|
25
|
+
Requires-Dist: google-generativeai>=0.5; extra == 'all'
|
|
26
|
+
Requires-Dist: groq>=0.5; extra == 'all'
|
|
27
|
+
Requires-Dist: openai>=1.0; extra == 'all'
|
|
28
|
+
Provides-Extra: anthropic
|
|
29
|
+
Requires-Dist: anthropic>=0.30; extra == 'anthropic'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: build; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: twine; extra == 'dev'
|
|
35
|
+
Provides-Extra: gemini
|
|
36
|
+
Requires-Dist: google-generativeai>=0.5; extra == 'gemini'
|
|
37
|
+
Provides-Extra: groq
|
|
38
|
+
Requires-Dist: groq>=0.5; extra == 'groq'
|
|
39
|
+
Provides-Extra: openai
|
|
40
|
+
Requires-Dist: openai>=1.0; extra == 'openai'
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# StructGuard
|
|
44
|
+
|
|
45
|
+
**Reliable, provider-agnostic structured outputs for production LLM applications.**
|
|
46
|
+
|
|
47
|
+
StructGuard makes sure every LLM response conforms to a schema you define —
|
|
48
|
+
`Pydantic`, `dataclass`, `TypedDict`, or raw JSON Schema — before it reaches
|
|
49
|
+
your application code. It handles prompting, JSON repair, validation, and
|
|
50
|
+
intelligent retries in one call, across OpenAI, Anthropic, Gemini, Groq, and
|
|
51
|
+
Ollama.
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install structguard # core only
|
|
57
|
+
pip install structguard[openai] # + OpenAI SDK
|
|
58
|
+
pip install structguard[anthropic] # + Anthropic SDK
|
|
59
|
+
pip install structguard[all] # every provider SDK
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from pydantic import BaseModel
|
|
66
|
+
from openai import OpenAI
|
|
67
|
+
from structguard import generate
|
|
68
|
+
|
|
69
|
+
class Incident(BaseModel):
|
|
70
|
+
summary: str
|
|
71
|
+
priority: str
|
|
72
|
+
confidence: float
|
|
73
|
+
|
|
74
|
+
client = OpenAI()
|
|
75
|
+
|
|
76
|
+
incident = generate(
|
|
77
|
+
llm=client,
|
|
78
|
+
prompt="Summarize this incident: Database connection pool exhausted, causing 500s.",
|
|
79
|
+
schema=Incident,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
print(incident.summary, incident.priority, incident.confidence)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## With a full report
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
result = generate(llm=client, prompt=prompt, schema=Incident, return_report=True)
|
|
89
|
+
print(result.data)
|
|
90
|
+
print(result.report) # ✓ Success | provider=OpenAI model=gpt-4o-mini retries=0 ...
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Error handling
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from structguard import RetryLimitExceeded, SchemaValidationError
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
incident = generate(llm=client, prompt=prompt, schema=Incident, max_retries=2)
|
|
100
|
+
except RetryLimitExceeded as e:
|
|
101
|
+
print(f"Gave up after {e.attempts} attempts: {e.last_error}")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Supported schema types
|
|
105
|
+
|
|
106
|
+
- `pydantic.BaseModel`
|
|
107
|
+
- stdlib `@dataclass`
|
|
108
|
+
- `TypedDict`
|
|
109
|
+
- raw JSON Schema `dict`
|
|
110
|
+
|
|
111
|
+
## Supported providers
|
|
112
|
+
|
|
113
|
+
OpenAI · Anthropic · Google Gemini · Groq · Ollama
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
git clone https://github.com/sujanrupu/structguard
|
|
119
|
+
cd structguard
|
|
120
|
+
pip install -e ".[dev]"
|
|
121
|
+
pytest
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# StructGuard
|
|
2
|
+
|
|
3
|
+
**Reliable, provider-agnostic structured outputs for production LLM applications.**
|
|
4
|
+
|
|
5
|
+
StructGuard makes sure every LLM response conforms to a schema you define —
|
|
6
|
+
`Pydantic`, `dataclass`, `TypedDict`, or raw JSON Schema — before it reaches
|
|
7
|
+
your application code. It handles prompting, JSON repair, validation, and
|
|
8
|
+
intelligent retries in one call, across OpenAI, Anthropic, Gemini, Groq, and
|
|
9
|
+
Ollama.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install structguard # core only
|
|
15
|
+
pip install structguard[openai] # + OpenAI SDK
|
|
16
|
+
pip install structguard[anthropic] # + Anthropic SDK
|
|
17
|
+
pip install structguard[all] # every provider SDK
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from pydantic import BaseModel
|
|
24
|
+
from openai import OpenAI
|
|
25
|
+
from structguard import generate
|
|
26
|
+
|
|
27
|
+
class Incident(BaseModel):
|
|
28
|
+
summary: str
|
|
29
|
+
priority: str
|
|
30
|
+
confidence: float
|
|
31
|
+
|
|
32
|
+
client = OpenAI()
|
|
33
|
+
|
|
34
|
+
incident = generate(
|
|
35
|
+
llm=client,
|
|
36
|
+
prompt="Summarize this incident: Database connection pool exhausted, causing 500s.",
|
|
37
|
+
schema=Incident,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
print(incident.summary, incident.priority, incident.confidence)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## With a full report
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
result = generate(llm=client, prompt=prompt, schema=Incident, return_report=True)
|
|
47
|
+
print(result.data)
|
|
48
|
+
print(result.report) # ✓ Success | provider=OpenAI model=gpt-4o-mini retries=0 ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Error handling
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from structguard import RetryLimitExceeded, SchemaValidationError
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
incident = generate(llm=client, prompt=prompt, schema=Incident, max_retries=2)
|
|
58
|
+
except RetryLimitExceeded as e:
|
|
59
|
+
print(f"Gave up after {e.attempts} attempts: {e.last_error}")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Supported schema types
|
|
63
|
+
|
|
64
|
+
- `pydantic.BaseModel`
|
|
65
|
+
- stdlib `@dataclass`
|
|
66
|
+
- `TypedDict`
|
|
67
|
+
- raw JSON Schema `dict`
|
|
68
|
+
|
|
69
|
+
## Supported providers
|
|
70
|
+
|
|
71
|
+
OpenAI · Anthropic · Google Gemini · Groq · Ollama
|
|
72
|
+
|
|
73
|
+
## Development
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/sujanrupu/structguard
|
|
77
|
+
cd structguard
|
|
78
|
+
pip install -e ".[dev]"
|
|
79
|
+
pytest
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# StructGuard Examples
|
|
2
|
+
|
|
3
|
+
Each script is runnable on its own once you `pip install` the right extra and
|
|
4
|
+
export the relevant API key.
|
|
5
|
+
|
|
6
|
+
| Script | Shows |
|
|
7
|
+
|---|---|
|
|
8
|
+
| `openai_example.py` | Basic usage with OpenAI + Pydantic schema + report |
|
|
9
|
+
| `anthropic_example.py` | Same, with Anthropic (drop-in client swap) |
|
|
10
|
+
| `gemini_example.py` | Gemini — note on `GenerativeModel` instance requirement |
|
|
11
|
+
| `groq_example.py` | Groq — note on model selection / JSON mode support |
|
|
12
|
+
| `ollama_example.py` | Fully local, no API key |
|
|
13
|
+
| `dataclass_example.py` | Using a stdlib `@dataclass` instead of Pydantic |
|
|
14
|
+
| `typeddict_example.py` | Using `TypedDict` |
|
|
15
|
+
| `json_schema_example.py` | Using a raw JSON Schema dict (no Python class) |
|
|
16
|
+
| `error_handling_example.py` | Catching `RetryLimitExceeded`, `SchemaValidationError`, etc. + debug logging |
|
|
17
|
+
|
|
18
|
+
## Run any example
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install structguard[openai] # or [anthropic], [gemini], [groq]
|
|
22
|
+
export OPENAI_API_KEY=sk-... # matching env var for the provider
|
|
23
|
+
python examples/openai_example.py
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Gemini quirk to know
|
|
27
|
+
|
|
28
|
+
`llm=` must be a `genai.GenerativeModel(...)` instance, not the `google.generativeai`
|
|
29
|
+
module itself:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import google.generativeai as genai
|
|
33
|
+
genai.configure()
|
|
34
|
+
model = genai.GenerativeModel("gemini-1.5-pro") # <- pass this to generate()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Groq quirk to know
|
|
38
|
+
|
|
39
|
+
Groq's client is OpenAI-compatible (`.chat.completions.create()`), but the
|
|
40
|
+
`model=` string must match a currently-hosted Groq model ID that supports
|
|
41
|
+
JSON mode — check https://console.groq.com/docs/models since the lineup
|
|
42
|
+
changes over time.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StructGuard + Anthropic
|
|
3
|
+
=========================
|
|
4
|
+
Install:
|
|
5
|
+
pip install structguard[anthropic] python-dotenv
|
|
6
|
+
|
|
7
|
+
Setup:
|
|
8
|
+
Put ANTHROPIC_API_KEY and ANTHROPIC_MODEL in a `.env` file next to this
|
|
9
|
+
script (see the provided dummy .env for the expected format).
|
|
10
|
+
|
|
11
|
+
Run:
|
|
12
|
+
python anthropic_example.py
|
|
13
|
+
"""
|
|
14
|
+
import os
|
|
15
|
+
from dotenv import load_dotenv
|
|
16
|
+
from pydantic import BaseModel
|
|
17
|
+
from anthropic import Anthropic
|
|
18
|
+
|
|
19
|
+
from structguard import generate, RetryLimitExceeded
|
|
20
|
+
|
|
21
|
+
load_dotenv()
|
|
22
|
+
|
|
23
|
+
MODEL_NAME = os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-6")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Incident(BaseModel):
|
|
27
|
+
summary: str
|
|
28
|
+
priority: str
|
|
29
|
+
confidence: float
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main():
|
|
33
|
+
client = Anthropic() # reads ANTHROPIC_API_KEY from env (loaded via load_dotenv above)
|
|
34
|
+
|
|
35
|
+
prompt = (
|
|
36
|
+
"Summarize this incident: Database connection pool exhausted, "
|
|
37
|
+
"causing intermittent 500 errors on the checkout service since 14:02 UTC."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
result = generate(
|
|
42
|
+
llm=client,
|
|
43
|
+
prompt=prompt,
|
|
44
|
+
schema=Incident,
|
|
45
|
+
model=MODEL_NAME,
|
|
46
|
+
max_retries=2,
|
|
47
|
+
return_report=True,
|
|
48
|
+
)
|
|
49
|
+
except RetryLimitExceeded as e:
|
|
50
|
+
print(f"Gave up after {e.attempts} attempts: {e.last_error}")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
incident = result.data
|
|
54
|
+
print("Model used:", MODEL_NAME)
|
|
55
|
+
print("Summary: ", incident.summary)
|
|
56
|
+
print("Priority: ", incident.priority)
|
|
57
|
+
print("Confidence:", incident.confidence)
|
|
58
|
+
print()
|
|
59
|
+
print("Report:", result.report)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
main()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StructGuard with a stdlib dataclass schema (works with any provider — Groq shown here).
|
|
3
|
+
|
|
4
|
+
Install:
|
|
5
|
+
pip install structguard[groq] python-dotenv
|
|
6
|
+
|
|
7
|
+
Setup:
|
|
8
|
+
Put GROQ_API_KEY and GROQ_MODEL in a `.env` file next to this script.
|
|
9
|
+
|
|
10
|
+
Run:
|
|
11
|
+
python dataclass_example.py
|
|
12
|
+
"""
|
|
13
|
+
import os
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from dotenv import load_dotenv
|
|
16
|
+
from groq import Groq
|
|
17
|
+
|
|
18
|
+
from structguard import generate
|
|
19
|
+
|
|
20
|
+
load_dotenv()
|
|
21
|
+
|
|
22
|
+
MODEL_NAME = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class TicketSummary:
|
|
27
|
+
title: str
|
|
28
|
+
category: str
|
|
29
|
+
urgent: bool
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main():
|
|
33
|
+
client = Groq(api_key=os.getenv("GROQ_API_KEY"))
|
|
34
|
+
|
|
35
|
+
ticket_text = (
|
|
36
|
+
"User reports they cannot check out — payment page hangs indefinitely "
|
|
37
|
+
"after clicking 'Pay Now'. Happening for all users since this morning."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
result = generate(
|
|
41
|
+
llm=client,
|
|
42
|
+
prompt=f"Classify this support ticket:\n\n{ticket_text}",
|
|
43
|
+
schema=TicketSummary,
|
|
44
|
+
model=MODEL_NAME,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
print("Model used:", MODEL_NAME)
|
|
48
|
+
print(f"Title: {result.title}")
|
|
49
|
+
print(f"Category: {result.category}")
|
|
50
|
+
print(f"Urgent: {result.urgent}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
main()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StructGuard — error handling, retries, and debug logging.
|
|
3
|
+
|
|
4
|
+
Install:
|
|
5
|
+
pip install structguard[anthropic] python-dotenv
|
|
6
|
+
|
|
7
|
+
Setup:
|
|
8
|
+
Put ANTHROPIC_API_KEY and ANTHROPIC_MODEL in a `.env` file next to this
|
|
9
|
+
script.
|
|
10
|
+
|
|
11
|
+
Run:
|
|
12
|
+
python error_handling_example.py
|
|
13
|
+
"""
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from dotenv import load_dotenv
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
from anthropic import Anthropic
|
|
20
|
+
|
|
21
|
+
from structguard import (
|
|
22
|
+
generate,
|
|
23
|
+
RetryLimitExceeded,
|
|
24
|
+
SchemaValidationError,
|
|
25
|
+
JsonRepairError,
|
|
26
|
+
UnsupportedProviderError,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
load_dotenv()
|
|
30
|
+
|
|
31
|
+
MODEL_NAME = os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-6")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Incident(BaseModel):
|
|
35
|
+
summary: str
|
|
36
|
+
priority: str
|
|
37
|
+
confidence: float
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main():
|
|
41
|
+
# Turn on debug logging to see: raw LLM output, whether repair was applied,
|
|
42
|
+
# validation errors per attempt, and retry counts.
|
|
43
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
44
|
+
|
|
45
|
+
client = Anthropic()
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
result = generate(
|
|
49
|
+
llm=client,
|
|
50
|
+
prompt="Summarize this incident: API gateway returning 502s intermittently.",
|
|
51
|
+
schema=Incident,
|
|
52
|
+
model=MODEL_NAME,
|
|
53
|
+
max_retries=2, # 3 total attempts
|
|
54
|
+
debug=True,
|
|
55
|
+
return_report=True,
|
|
56
|
+
)
|
|
57
|
+
print("Model used:", MODEL_NAME)
|
|
58
|
+
print("Success:", result.data)
|
|
59
|
+
print("Retries needed:", result.report.retries)
|
|
60
|
+
|
|
61
|
+
except RetryLimitExceeded as e:
|
|
62
|
+
# All attempts exhausted — inspect e.attempts and e.last_error
|
|
63
|
+
print(f"Gave up after {e.attempts} attempts.")
|
|
64
|
+
print("Last underlying error:", e.last_error)
|
|
65
|
+
|
|
66
|
+
except SchemaValidationError as e:
|
|
67
|
+
# Only reachable if you call validate()/generate() in a way that
|
|
68
|
+
# surfaces a single validation failure directly (e.g. max_retries=0)
|
|
69
|
+
for err in e.errors:
|
|
70
|
+
print(f" {err['field']}: {err['issue']}")
|
|
71
|
+
|
|
72
|
+
except JsonRepairError as e:
|
|
73
|
+
print("Could not parse the model's output as JSON at all:", e.raw_output[:200])
|
|
74
|
+
|
|
75
|
+
except UnsupportedProviderError:
|
|
76
|
+
print("The `llm=` client isn't a recognized OpenAI/Anthropic/Gemini/Groq/Ollama client.")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
main()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StructGuard + Google Gemini
|
|
3
|
+
==============================
|
|
4
|
+
Install:
|
|
5
|
+
pip install structguard[gemini] python-dotenv
|
|
6
|
+
|
|
7
|
+
Setup:
|
|
8
|
+
Put GOOGLE_API_KEY and GEMINI_MODEL in a `.env` file next to this script
|
|
9
|
+
(see the provided dummy .env for the expected format).
|
|
10
|
+
|
|
11
|
+
Run:
|
|
12
|
+
python gemini_example.py
|
|
13
|
+
|
|
14
|
+
IMPORTANT — Gemini client shape is different from OpenAI/Anthropic:
|
|
15
|
+
Those SDKs give you one client object you call `.chat.completions.create()`
|
|
16
|
+
or `.messages.create()` on. Gemini instead wants you to construct a
|
|
17
|
+
`GenerativeModel` instance *per model name* first, then call
|
|
18
|
+
`.generate_content()` on THAT object.
|
|
19
|
+
|
|
20
|
+
So for StructGuard, the `llm=` argument must be the `GenerativeModel`
|
|
21
|
+
instance (not the top-level `google.generativeai` module, and not the
|
|
22
|
+
result of `genai.configure(...)`, which returns None). That's why we
|
|
23
|
+
build the model using MODEL_NAME from .env below, then pass the model
|
|
24
|
+
object itself into generate().
|
|
25
|
+
"""
|
|
26
|
+
import os
|
|
27
|
+
from dotenv import load_dotenv
|
|
28
|
+
from pydantic import BaseModel
|
|
29
|
+
import google.generativeai as genai
|
|
30
|
+
|
|
31
|
+
from structguard import generate, RetryLimitExceeded
|
|
32
|
+
|
|
33
|
+
load_dotenv()
|
|
34
|
+
|
|
35
|
+
MODEL_NAME = os.getenv("GEMINI_MODEL", "gemini-1.5-pro")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Incident(BaseModel):
|
|
39
|
+
summary: str
|
|
40
|
+
priority: str
|
|
41
|
+
confidence: float
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main():
|
|
45
|
+
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
|
|
46
|
+
|
|
47
|
+
# Build the model instance yourself, using the model name from .env —
|
|
48
|
+
# this IS the object you pass as llm=.
|
|
49
|
+
model = genai.GenerativeModel(MODEL_NAME)
|
|
50
|
+
|
|
51
|
+
prompt = (
|
|
52
|
+
"Summarize this incident: Database connection pool exhausted, "
|
|
53
|
+
"causing intermittent 500 errors on the checkout service since 14:02 UTC."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
result = generate(
|
|
58
|
+
llm=model, # <-- the GenerativeModel instance, not the genai module
|
|
59
|
+
prompt=prompt,
|
|
60
|
+
schema=Incident,
|
|
61
|
+
max_retries=2,
|
|
62
|
+
return_report=True,
|
|
63
|
+
)
|
|
64
|
+
except RetryLimitExceeded as e:
|
|
65
|
+
print(f"Gave up after {e.attempts} attempts: {e.last_error}")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
incident = result.data
|
|
69
|
+
print("Model used:", MODEL_NAME)
|
|
70
|
+
print("Summary: ", incident.summary)
|
|
71
|
+
print("Priority: ", incident.priority)
|
|
72
|
+
print("Confidence:", incident.confidence)
|
|
73
|
+
print()
|
|
74
|
+
print("Report:", result.report)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
main()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StructGuard + Groq
|
|
3
|
+
=====================
|
|
4
|
+
Install:
|
|
5
|
+
pip install structguard[groq] python-dotenv
|
|
6
|
+
|
|
7
|
+
Setup:
|
|
8
|
+
Put GROQ_API_KEY and GROQ_MODEL in a `.env` file next to this script
|
|
9
|
+
(see the provided dummy .env for the expected format).
|
|
10
|
+
|
|
11
|
+
Run:
|
|
12
|
+
python groq_example.py
|
|
13
|
+
|
|
14
|
+
NOTE on models: Groq hosts several open-weight models (Llama, Mixtral, etc.)
|
|
15
|
+
behind an OpenAI-compatible chat API, so the client shape is identical to
|
|
16
|
+
OpenAI's — `.chat.completions.create()`. The main thing to get right is the
|
|
17
|
+
GROQ_MODEL value in your .env, since it must match one of Groq's currently
|
|
18
|
+
hosted model IDs (these change over time — check
|
|
19
|
+
https://console.groq.com/docs/models for the current list). Not every
|
|
20
|
+
hosted model supports JSON mode; StructGuard requests
|
|
21
|
+
`response_format={"type": "json_object"}` automatically, so pick a model
|
|
22
|
+
that's documented as JSON-mode compatible (most current Llama 3.x models
|
|
23
|
+
on Groq are).
|
|
24
|
+
"""
|
|
25
|
+
import os
|
|
26
|
+
from dotenv import load_dotenv
|
|
27
|
+
from pydantic import BaseModel
|
|
28
|
+
from groq import Groq
|
|
29
|
+
|
|
30
|
+
from structguard import generate, RetryLimitExceeded
|
|
31
|
+
|
|
32
|
+
load_dotenv()
|
|
33
|
+
|
|
34
|
+
MODEL_NAME = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Incident(BaseModel):
|
|
38
|
+
summary: str
|
|
39
|
+
priority: str
|
|
40
|
+
confidence: float
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def main():
|
|
44
|
+
client = Groq(api_key=os.getenv("GROQ_API_KEY")) # explicit, but Groq() also auto-reads GROQ_API_KEY
|
|
45
|
+
|
|
46
|
+
prompt = (
|
|
47
|
+
"Summarize this incident: Database connection pool exhausted, "
|
|
48
|
+
"causing intermittent 500 errors on the checkout service since 14:02 UTC."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
result = generate(
|
|
53
|
+
llm=client,
|
|
54
|
+
prompt=prompt,
|
|
55
|
+
schema=Incident,
|
|
56
|
+
model=MODEL_NAME, # <-- pulled from GROQ_MODEL in .env
|
|
57
|
+
max_retries=2,
|
|
58
|
+
return_report=True,
|
|
59
|
+
)
|
|
60
|
+
except RetryLimitExceeded as e:
|
|
61
|
+
print(f"Gave up after {e.attempts} attempts: {e.last_error}")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
incident = result.data
|
|
65
|
+
print("Model used:", MODEL_NAME)
|
|
66
|
+
print("Summary: ", incident.summary)
|
|
67
|
+
print("Priority: ", incident.priority)
|
|
68
|
+
print("Confidence:", incident.confidence)
|
|
69
|
+
print()
|
|
70
|
+
print("Report:", result.report)
|
|
71
|
+
print()
|
|
72
|
+
print(f"Tokens in/out: {result.report.tokens_input}/{result.report.tokens_output}"
|
|
73
|
+
" (Groq's inference speed makes this a good fit for high-retry-tolerance pipelines)")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
main()
|