glaium 0.1.8__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.
- glaium-0.1.8/.gitignore +56 -0
- glaium-0.1.8/LICENSE +21 -0
- glaium-0.1.8/PKG-INFO +546 -0
- glaium-0.1.8/README.md +501 -0
- glaium-0.1.8/pyproject.toml +115 -0
- glaium-0.1.8/src/glaium/__init__.py +121 -0
- glaium-0.1.8/src/glaium/_version.py +34 -0
- glaium-0.1.8/src/glaium/agent.py +638 -0
- glaium-0.1.8/src/glaium/auth.py +148 -0
- glaium-0.1.8/src/glaium/client.py +470 -0
- glaium-0.1.8/src/glaium/exceptions.py +113 -0
- glaium-0.1.8/src/glaium/extras/__init__.py +17 -0
- glaium-0.1.8/src/glaium/extras/data.py +413 -0
- glaium-0.1.8/src/glaium/extras/handsup.py +285 -0
- glaium-0.1.8/src/glaium/extras/memory.py +400 -0
- glaium-0.1.8/src/glaium/extras/verification.py +276 -0
- glaium-0.1.8/src/glaium/models.py +248 -0
- glaium-0.1.8/src/glaium/py.typed +0 -0
- glaium-0.1.8/src/glaium/retry.py +133 -0
glaium-0.1.8/.gitignore
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# These are some examples of commonly ignored file patterns.
|
|
2
|
+
# You should customize this list as applicable to your project.
|
|
3
|
+
# Learn more about .gitignore:
|
|
4
|
+
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
|
5
|
+
|
|
6
|
+
# Node artifact files
|
|
7
|
+
node_modules/
|
|
8
|
+
dist/
|
|
9
|
+
|
|
10
|
+
# Compiled Java class files
|
|
11
|
+
*.class
|
|
12
|
+
|
|
13
|
+
# Compiled Python bytecode
|
|
14
|
+
*.py[cod]
|
|
15
|
+
|
|
16
|
+
# Log files
|
|
17
|
+
*.log
|
|
18
|
+
|
|
19
|
+
# Package files
|
|
20
|
+
*.jar
|
|
21
|
+
|
|
22
|
+
# Maven
|
|
23
|
+
target/
|
|
24
|
+
dist/
|
|
25
|
+
|
|
26
|
+
# JetBrains IDE
|
|
27
|
+
.idea/
|
|
28
|
+
|
|
29
|
+
# Unit test reports
|
|
30
|
+
TEST*.xml
|
|
31
|
+
|
|
32
|
+
# Generated by MacOS
|
|
33
|
+
.DS_Store
|
|
34
|
+
|
|
35
|
+
# Generated by Windows
|
|
36
|
+
Thumbs.db
|
|
37
|
+
|
|
38
|
+
# Applications
|
|
39
|
+
*.app
|
|
40
|
+
*.exe
|
|
41
|
+
*.war
|
|
42
|
+
|
|
43
|
+
# Large media files
|
|
44
|
+
*.mp4
|
|
45
|
+
*.tiff
|
|
46
|
+
*.avi
|
|
47
|
+
*.flv
|
|
48
|
+
*.mov
|
|
49
|
+
*.wmv
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Virtual environments
|
|
53
|
+
.venv/
|
|
54
|
+
venv/
|
|
55
|
+
env/
|
|
56
|
+
src/glaium/_version.py
|
glaium-0.1.8/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Glaium
|
|
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.
|
glaium-0.1.8/PKG-INFO
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: glaium
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: Python SDK for building autonomous agents that optimize toward organizational goals
|
|
5
|
+
Project-URL: Homepage, https://glaium.com
|
|
6
|
+
Project-URL: Repository, https://bitbucket.org/glaium/sdk
|
|
7
|
+
Project-URL: Issues, https://bitbucket.org/glaium/sdk/issues
|
|
8
|
+
Author-email: Glaium <support@glaium.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,ai,autonomous,machine-learning,optimization,sdk
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: httpx>=0.25.0
|
|
26
|
+
Requires-Dist: pydantic>=2.0.0
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Requires-Dist: black>=23.0.0; extra == 'all'
|
|
29
|
+
Requires-Dist: mypy>=1.0.0; extra == 'all'
|
|
30
|
+
Requires-Dist: pyjwt>=2.0.0; extra == 'all'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == 'all'
|
|
34
|
+
Requires-Dist: ruff>=0.1.0; extra == 'all'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
42
|
+
Provides-Extra: jwt
|
|
43
|
+
Requires-Dist: pyjwt>=2.0.0; extra == 'jwt'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# Glaium SDK
|
|
47
|
+
|
|
48
|
+
Python SDK for building autonomous agents that optimize toward organizational goals using the Glaium Optimizer.
|
|
49
|
+
|
|
50
|
+
[](https://badge.fury.io/py/glaium)
|
|
51
|
+
[](https://www.python.org/downloads/)
|
|
52
|
+
[](https://opensource.org/licenses/MIT)
|
|
53
|
+
|
|
54
|
+
## What's New in v0.1.8
|
|
55
|
+
|
|
56
|
+
- **De-anonymization support**: New `deanonymize()` method in `DataClient` to reveal anonymized KPI values
|
|
57
|
+
- **Secure API routing**: SDK now routes through the API gateway for proper authorization
|
|
58
|
+
- **Table of Contents**: Improved documentation navigation
|
|
59
|
+
|
|
60
|
+
See [CHANGELOG.md](CHANGELOG.md) for full release history.
|
|
61
|
+
|
|
62
|
+
## Table of Contents
|
|
63
|
+
|
|
64
|
+
- [Installation](#installation)
|
|
65
|
+
- [Quick Start](#quick-start)
|
|
66
|
+
- [High-Level Agent Framework](#high-level-agent-framework)
|
|
67
|
+
- [Low-Level Client](#low-level-client)
|
|
68
|
+
- [Configuration](#configuration)
|
|
69
|
+
- [Async Support](#async-support)
|
|
70
|
+
- [Background Execution](#background-execution)
|
|
71
|
+
- [Optimization Response](#optimization-response)
|
|
72
|
+
- [Events](#events)
|
|
73
|
+
- [Optional Extras](#optional-extras)
|
|
74
|
+
- [DataClient](#dataclient)
|
|
75
|
+
- [Memory](#memory)
|
|
76
|
+
- [Verification](#verification)
|
|
77
|
+
- [HandsUp](#handsup)
|
|
78
|
+
- [Error Handling](#error-handling)
|
|
79
|
+
- [Development](#development)
|
|
80
|
+
- [Releasing](#releasing)
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install glaium
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Quick Start
|
|
89
|
+
|
|
90
|
+
### High-Level Agent Framework
|
|
91
|
+
|
|
92
|
+
The easiest way to create an agent:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from glaium import Agent
|
|
96
|
+
|
|
97
|
+
# Create an agent
|
|
98
|
+
agent = Agent(
|
|
99
|
+
agent_id="sales-agent",
|
|
100
|
+
declared_outputs=[{"name": "deals_closed"}],
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Handle optimization updates
|
|
104
|
+
@agent.on_optimization
|
|
105
|
+
def handle_optimization(optimization):
|
|
106
|
+
print(f"Objectives: {optimization.objectives}")
|
|
107
|
+
print(f"Constraints: {optimization.constraints}")
|
|
108
|
+
|
|
109
|
+
# Define your cycle logic
|
|
110
|
+
@agent.on_cycle
|
|
111
|
+
def run_cycle(ctx):
|
|
112
|
+
# Your agent's work happens here
|
|
113
|
+
deals = process_leads()
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
"outputs": {"deals_closed": deals},
|
|
117
|
+
"effectiveness": 0.75,
|
|
118
|
+
"efficiency": 0.85,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Run the agent
|
|
122
|
+
agent.run()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Low-Level Client
|
|
126
|
+
|
|
127
|
+
For more control, use the client directly:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from glaium import Client, CycleEndEvent
|
|
131
|
+
|
|
132
|
+
# Create client (uses GLAIUM_API_KEY env var)
|
|
133
|
+
client = Client()
|
|
134
|
+
|
|
135
|
+
# Register your agent
|
|
136
|
+
registration = client.register(
|
|
137
|
+
agent_id="my-agent",
|
|
138
|
+
declared_inputs=[
|
|
139
|
+
{"name": "leads", "source": {"agent": "marketing", "output": "qualified_leads"}}
|
|
140
|
+
],
|
|
141
|
+
declared_outputs=[{"name": "deals_closed"}],
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Use the token for authenticated requests
|
|
145
|
+
agent_client = client.with_token(registration.agent_token)
|
|
146
|
+
|
|
147
|
+
# Get optimization (objectives, constraints, search space)
|
|
148
|
+
optimization = agent_client.get_optimization()
|
|
149
|
+
print(f"Target: {optimization.objectives[0].target}")
|
|
150
|
+
|
|
151
|
+
# Submit cycle results
|
|
152
|
+
agent_client.submit_event(CycleEndEvent(
|
|
153
|
+
cycle_number=1,
|
|
154
|
+
inputs={"leads": 100},
|
|
155
|
+
outputs={"deals_closed": 15},
|
|
156
|
+
effectiveness=0.75,
|
|
157
|
+
efficiency=0.82,
|
|
158
|
+
))
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
### Environment Variables
|
|
164
|
+
|
|
165
|
+
| Variable | Description | Default |
|
|
166
|
+
|----------|-------------|---------|
|
|
167
|
+
| `GLAIUM_API_KEY` | API key for authentication | (required) |
|
|
168
|
+
| `GLAIUM_BASE_URL` | Optimizer API base URL | `https://api.glaium.com` |
|
|
169
|
+
|
|
170
|
+
### Client Options
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from glaium import Client
|
|
174
|
+
|
|
175
|
+
client = Client(
|
|
176
|
+
api_key="glaium_org123_ak_xxx", # Or use env var
|
|
177
|
+
base_url="https://api.glaium.com",
|
|
178
|
+
max_retries=3, # Retry failed requests
|
|
179
|
+
retry_delay=1.0, # Initial retry delay (seconds)
|
|
180
|
+
retry_backoff=2.0, # Exponential backoff multiplier
|
|
181
|
+
timeout=30.0, # Request timeout (seconds)
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Agent Options
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from glaium import Agent
|
|
189
|
+
|
|
190
|
+
agent = Agent(
|
|
191
|
+
agent_id="my-agent",
|
|
192
|
+
declared_inputs=[...],
|
|
193
|
+
declared_outputs=[...],
|
|
194
|
+
connections=[...],
|
|
195
|
+
formula="output = input * rate",
|
|
196
|
+
token_ttl_hours=48, # Token validity
|
|
197
|
+
poll_interval=60, # Seconds between optimization polls
|
|
198
|
+
default_cycle_interval=300, # Default cycle interval if not set by optimizer
|
|
199
|
+
)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Async Support
|
|
203
|
+
|
|
204
|
+
All methods have async variants:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
import asyncio
|
|
208
|
+
from glaium import Agent
|
|
209
|
+
|
|
210
|
+
agent = Agent(agent_id="async-agent", declared_outputs=[{"name": "result"}])
|
|
211
|
+
|
|
212
|
+
@agent.on_cycle
|
|
213
|
+
async def run_cycle(ctx):
|
|
214
|
+
result = await process_async()
|
|
215
|
+
return {"outputs": {"result": result}, "effectiveness": 0.9}
|
|
216
|
+
|
|
217
|
+
# Run async
|
|
218
|
+
asyncio.run(agent.run_async())
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Background Execution
|
|
222
|
+
|
|
223
|
+
Run the agent in a background thread:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from glaium import Agent
|
|
227
|
+
|
|
228
|
+
agent = Agent(agent_id="background-agent", declared_outputs=[{"name": "result"}])
|
|
229
|
+
|
|
230
|
+
@agent.on_cycle
|
|
231
|
+
def run_cycle(ctx):
|
|
232
|
+
return {"outputs": {"result": 42}, "effectiveness": 0.9}
|
|
233
|
+
|
|
234
|
+
# Start in background
|
|
235
|
+
thread = agent.start()
|
|
236
|
+
|
|
237
|
+
# Do other work...
|
|
238
|
+
import time
|
|
239
|
+
time.sleep(60)
|
|
240
|
+
|
|
241
|
+
# Stop the agent
|
|
242
|
+
agent.stop()
|
|
243
|
+
thread.join()
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Optimization Response
|
|
247
|
+
|
|
248
|
+
The optimizer provides objectives, constraints, and scheduling:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
@agent.on_optimization
|
|
252
|
+
def handle_optimization(opt):
|
|
253
|
+
# Objectives - what to optimize toward
|
|
254
|
+
for obj in opt.objectives:
|
|
255
|
+
print(f"Metric: {obj.metric}, Target: {obj.target}, Operator: {obj.operator}")
|
|
256
|
+
|
|
257
|
+
# Constraints - limits on behavior
|
|
258
|
+
for constraint in opt.constraints:
|
|
259
|
+
print(f"Constraint: {constraint.metric} {constraint.operator}")
|
|
260
|
+
if constraint.is_bottleneck:
|
|
261
|
+
print(" -> This agent is the system bottleneck!")
|
|
262
|
+
|
|
263
|
+
# Search space - tunable parameter ranges
|
|
264
|
+
for param, space in opt.search_space.items():
|
|
265
|
+
print(f"Param: {param}, Range: [{space.min}, {space.max}]")
|
|
266
|
+
|
|
267
|
+
# Scheduling
|
|
268
|
+
if opt.next_cycle_at:
|
|
269
|
+
print(f"Next cycle at: {opt.next_cycle_at}")
|
|
270
|
+
elif opt.cycle_interval:
|
|
271
|
+
print(f"Cycle every {opt.cycle_interval} seconds")
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Events
|
|
275
|
+
|
|
276
|
+
Submit different event types:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from glaium import (
|
|
280
|
+
CycleStartEvent,
|
|
281
|
+
CycleEndEvent,
|
|
282
|
+
CycleInterruptEvent,
|
|
283
|
+
AnomalyEvent,
|
|
284
|
+
HandsUpEvent,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Cycle lifecycle
|
|
288
|
+
client.submit_event(CycleStartEvent(cycle_number=1))
|
|
289
|
+
client.submit_event(CycleEndEvent(
|
|
290
|
+
cycle_number=1,
|
|
291
|
+
inputs={"leads": 100},
|
|
292
|
+
outputs={"deals": 15},
|
|
293
|
+
effectiveness=0.75,
|
|
294
|
+
efficiency=0.85,
|
|
295
|
+
))
|
|
296
|
+
|
|
297
|
+
# Interruption
|
|
298
|
+
client.submit_event(CycleInterruptEvent(
|
|
299
|
+
cycle_number=1,
|
|
300
|
+
reason="External API unavailable",
|
|
301
|
+
))
|
|
302
|
+
|
|
303
|
+
# Anomaly detection
|
|
304
|
+
client.submit_event(AnomalyEvent(
|
|
305
|
+
metric="conversion_rate",
|
|
306
|
+
expected=0.15,
|
|
307
|
+
actual=0.05,
|
|
308
|
+
))
|
|
309
|
+
|
|
310
|
+
# Human escalation
|
|
311
|
+
client.submit_event(HandsUpEvent(
|
|
312
|
+
severity="high",
|
|
313
|
+
reason="Budget limit approaching",
|
|
314
|
+
context={"current_spend": 9500, "limit": 10000},
|
|
315
|
+
proposed_action={"pause_campaigns": ["camp_123"]},
|
|
316
|
+
))
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Optional Extras
|
|
320
|
+
|
|
321
|
+
Additional features available via `glaium.extras`:
|
|
322
|
+
|
|
323
|
+
### DataClient
|
|
324
|
+
|
|
325
|
+
Query metrics from the Data Service:
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
from glaium.extras import DataClient
|
|
329
|
+
|
|
330
|
+
data = DataClient()
|
|
331
|
+
|
|
332
|
+
# Query metrics
|
|
333
|
+
result = await data.retrieve(
|
|
334
|
+
organization_id=123,
|
|
335
|
+
metrics=["spend", "installs", "cpi"],
|
|
336
|
+
dimensions=["campaign_id"],
|
|
337
|
+
period=["d-7", "d-1"],
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
# Convenience methods
|
|
341
|
+
campaigns = await data.get_campaign_performance(organization_id=123, days=7)
|
|
342
|
+
trends = await data.get_daily_trends(organization_id=123, metric="installs", days=14)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### De-anonymization
|
|
346
|
+
|
|
347
|
+
Some KPIs return anonymized values (pattern: `{kpi}§§{hash}`) for privacy. Use `deanonymize()` to reveal original values:
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
from glaium.extras import DataClient
|
|
351
|
+
|
|
352
|
+
data = DataClient(api_key="your-api-key")
|
|
353
|
+
|
|
354
|
+
# De-anonymize a string
|
|
355
|
+
text = "Revenue for app§§963D9D63B7701FC5 is $1000"
|
|
356
|
+
clear_text = await data.deanonymize(data=text)
|
|
357
|
+
# Returns: "Revenue for Contraction Tracker is $1000"
|
|
358
|
+
|
|
359
|
+
# De-anonymize query results
|
|
360
|
+
result = await data.retrieve(organization_id=123, metrics=["revenue"], dimensions=["app"])
|
|
361
|
+
clear_result = await data.deanonymize(data=result["data"])
|
|
362
|
+
|
|
363
|
+
# Sync version also available
|
|
364
|
+
clear_text = data.deanonymize_sync(data=text)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The organization is determined automatically from your API key, ensuring you can only de-anonymize your own organization's data.
|
|
368
|
+
|
|
369
|
+
### Memory
|
|
370
|
+
|
|
371
|
+
Persist context across cycles:
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
from glaium.extras import Memory
|
|
375
|
+
|
|
376
|
+
memory = Memory(agent_id="my-agent", organization_id=123)
|
|
377
|
+
|
|
378
|
+
# Store memories
|
|
379
|
+
await memory.store(
|
|
380
|
+
memory_type="decision",
|
|
381
|
+
content="Increased bid by 15% due to high ROAS",
|
|
382
|
+
importance=0.8,
|
|
383
|
+
tags=["bid", "optimization"],
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Recall memories
|
|
387
|
+
past = await memory.recall(
|
|
388
|
+
types=["decision", "outcome"],
|
|
389
|
+
limit=10,
|
|
390
|
+
min_importance=0.5,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# Build context for LLM
|
|
394
|
+
context = await memory.build_context()
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Verification
|
|
398
|
+
|
|
399
|
+
Risk-based decision verification:
|
|
400
|
+
|
|
401
|
+
```python
|
|
402
|
+
from glaium.extras import Verification, RiskLevel
|
|
403
|
+
|
|
404
|
+
verifier = Verification()
|
|
405
|
+
|
|
406
|
+
# Classify risk
|
|
407
|
+
risk = verifier.classify_risk(
|
|
408
|
+
action_type="budget_change",
|
|
409
|
+
parameters={"budget_change": 5000},
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
# Verify decision
|
|
413
|
+
result = await verifier.verify(
|
|
414
|
+
prompt="Should we increase budget?",
|
|
415
|
+
risk_level=risk,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
if result.requires_human:
|
|
419
|
+
# Escalate
|
|
420
|
+
pass
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### HandsUp
|
|
424
|
+
|
|
425
|
+
Human escalation helpers:
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
from glaium.extras import HandsUp, HandsUpBuilder
|
|
429
|
+
|
|
430
|
+
# Raise directly
|
|
431
|
+
raise HandsUp(
|
|
432
|
+
severity="high",
|
|
433
|
+
category="low_confidence",
|
|
434
|
+
reason="Cannot determine optimal action",
|
|
435
|
+
proposed_action={"bid_change": 0.1},
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Use builder for common patterns
|
|
439
|
+
builder = HandsUpBuilder(agent_id="my-agent")
|
|
440
|
+
|
|
441
|
+
raise builder.low_confidence(confidence=0.45, decision="increase bid")
|
|
442
|
+
raise builder.budget_exceeded(current=95000, limit=100000, proposed_spend=8000)
|
|
443
|
+
raise builder.constraint_violated(constraint="CPI <= 2.50", actual_value=2.75)
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## Error Handling
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
from glaium import Client
|
|
450
|
+
from glaium.exceptions import (
|
|
451
|
+
GlaiumError,
|
|
452
|
+
AuthenticationError,
|
|
453
|
+
RateLimitError,
|
|
454
|
+
ValidationError,
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
client = Client()
|
|
458
|
+
|
|
459
|
+
try:
|
|
460
|
+
client.register(agent_id="my-agent", declared_outputs=[])
|
|
461
|
+
except AuthenticationError:
|
|
462
|
+
print("Invalid API key")
|
|
463
|
+
except RateLimitError as e:
|
|
464
|
+
print(f"Rate limited, retry after {e.retry_after}s")
|
|
465
|
+
except ValidationError as e:
|
|
466
|
+
print(f"Invalid request: {e.message}")
|
|
467
|
+
except GlaiumError as e:
|
|
468
|
+
print(f"Glaium error: {e}")
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Exception Hierarchy
|
|
472
|
+
|
|
473
|
+
```
|
|
474
|
+
GlaiumError
|
|
475
|
+
├── AuthenticationError
|
|
476
|
+
│ └── TokenExpiredError
|
|
477
|
+
├── APIError
|
|
478
|
+
│ ├── RateLimitError
|
|
479
|
+
│ ├── ServerError
|
|
480
|
+
│ ├── ValidationError
|
|
481
|
+
│ └── NotFoundError
|
|
482
|
+
├── AgentError
|
|
483
|
+
│ ├── NotRegisteredError
|
|
484
|
+
│ ├── AlreadyRunningError
|
|
485
|
+
│ └── CycleError
|
|
486
|
+
├── ConnectionError
|
|
487
|
+
└── TimeoutError
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## Development
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
# Clone the repository
|
|
494
|
+
git clone git@bitbucket.org:glaium/sdk.git
|
|
495
|
+
cd sdk
|
|
496
|
+
|
|
497
|
+
# Install with dev dependencies
|
|
498
|
+
pip install -e ".[dev]"
|
|
499
|
+
|
|
500
|
+
# Run tests
|
|
501
|
+
pytest
|
|
502
|
+
|
|
503
|
+
# Type checking
|
|
504
|
+
mypy src/glaium
|
|
505
|
+
|
|
506
|
+
# Linting
|
|
507
|
+
ruff check src/glaium
|
|
508
|
+
black src/glaium
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Releasing
|
|
512
|
+
|
|
513
|
+
The SDK uses automatic versioning from git tags. Version numbers are derived from tags (e.g., `v0.1.2` becomes version `0.1.2`).
|
|
514
|
+
|
|
515
|
+
### Release Workflow
|
|
516
|
+
|
|
517
|
+
1. **Develop and merge to master** - Regular pushes to master only run tests (no PyPI publish)
|
|
518
|
+
|
|
519
|
+
2. **When ready to release**, create and push a version tag:
|
|
520
|
+
```bash
|
|
521
|
+
git tag v0.1.2
|
|
522
|
+
git push --tags
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
3. **Pipeline auto-publishes** - Bitbucket Pipelines will:
|
|
526
|
+
- Run tests
|
|
527
|
+
- Build the package with the tag version
|
|
528
|
+
- Publish to PyPI
|
|
529
|
+
|
|
530
|
+
### Version Format
|
|
531
|
+
|
|
532
|
+
Use semantic versioning: `vMAJOR.MINOR.PATCH`
|
|
533
|
+
|
|
534
|
+
- `v0.1.0` - Initial release
|
|
535
|
+
- `v0.1.1` - Patch/bugfix
|
|
536
|
+
- `v0.2.0` - New features (backward compatible)
|
|
537
|
+
- `v1.0.0` - Major release / breaking changes
|
|
538
|
+
|
|
539
|
+
## License
|
|
540
|
+
|
|
541
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
542
|
+
|
|
543
|
+
## Support
|
|
544
|
+
|
|
545
|
+
- Issues: https://bitbucket.org/glaium/sdk/issues
|
|
546
|
+
- Email: support@glaium.com
|