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.
@@ -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
+ [![PyPI version](https://badge.fury.io/py/glaium.svg)](https://badge.fury.io/py/glaium)
51
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
52
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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