cartha-sdk 0.1.0__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.
- cartha_sdk-0.1.0/LICENSE +21 -0
- cartha_sdk-0.1.0/PKG-INFO +156 -0
- cartha_sdk-0.1.0/README.md +128 -0
- cartha_sdk-0.1.0/cartha/__init__.py +142 -0
- cartha_sdk-0.1.0/cartha_sdk.egg-info/PKG-INFO +156 -0
- cartha_sdk-0.1.0/cartha_sdk.egg-info/SOURCES.txt +14 -0
- cartha_sdk-0.1.0/cartha_sdk.egg-info/dependency_links.txt +1 -0
- cartha_sdk-0.1.0/cartha_sdk.egg-info/requires.txt +1 -0
- cartha_sdk-0.1.0/cartha_sdk.egg-info/top_level.txt +2 -0
- cartha_sdk-0.1.0/nexus/__init__.py +8 -0
- cartha_sdk-0.1.0/nexus/ops.py +749 -0
- cartha_sdk-0.1.0/pyproject.toml +56 -0
- cartha_sdk-0.1.0/setup.cfg +4 -0
cartha_sdk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maulik Jadav / Cartha
|
|
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,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cartha-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cartha SDK — memory, traces, costs, and policies for AI agents
|
|
5
|
+
Author-email: Maulik Jadav <maulikjadav239@gmail.com>
|
|
6
|
+
Maintainer-email: Maulik Jadav <maulikjadav239@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://cartha.in
|
|
9
|
+
Project-URL: Documentation, https://cartha.in
|
|
10
|
+
Project-URL: Repository, https://github.com/maulik-jadav/nexus
|
|
11
|
+
Project-URL: Issues, https://github.com/maulik-jadav/nexus/issues
|
|
12
|
+
Project-URL: Source, https://github.com/maulik-jadav/nexus/tree/main/nexus/packages/sdk-python
|
|
13
|
+
Keywords: cartha,agents,llm,observability,tracing,memory,ai
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: httpx>=0.27
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# Cartha SDK (Python)
|
|
30
|
+
|
|
31
|
+
Instrument AI agents with memory, traces, costs, and policies on [Cartha](https://cartha.in).
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install cartha-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires Python 3.10+.
|
|
40
|
+
|
|
41
|
+
From source (development):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install "git+https://github.com/maulik-jadav/nexus.git#subdirectory=nexus/packages/sdk-python"
|
|
45
|
+
# or from a local clone:
|
|
46
|
+
pip install -e nexus/packages/sdk-python
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export CARTHA_API_KEY="cartha_..." # from the dashboard → Keys
|
|
53
|
+
export CARTHA_API_BASE="https://cartha.in" # your Cartha deployment
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import cartha
|
|
58
|
+
|
|
59
|
+
cartha.init() # picks up CARTHA_* env vars
|
|
60
|
+
|
|
61
|
+
@cartha.trace()
|
|
62
|
+
def run_agent(prompt: str):
|
|
63
|
+
return "..."
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`@cartha.trace()` works on sync and async functions. Every call registers the
|
|
67
|
+
agent, sends heartbeats, and records a full trace (start → steps → finish),
|
|
68
|
+
including failures.
|
|
69
|
+
|
|
70
|
+
## Memory
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import cartha
|
|
74
|
+
|
|
75
|
+
# async agents
|
|
76
|
+
await cartha.remember(user_id="u1", content="Prefers dark mode", scope="user")
|
|
77
|
+
hits = await cartha.recall(user_id="u1", context="UI preferences", scope=["user", "team"])
|
|
78
|
+
|
|
79
|
+
# sync code
|
|
80
|
+
cartha.remember_sync(user_id="u1", content="Prefers dark mode", scope="user")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Scopes: `agent` (private) · `user` (all agents serving that user) ·
|
|
84
|
+
`team` (same `team_id`) · `org` (everyone in your org).
|
|
85
|
+
|
|
86
|
+
## Nested agents (multi-agent loops)
|
|
87
|
+
|
|
88
|
+
By default, `@cartha.trace()` **auto-nests**: if a traced agent calls another
|
|
89
|
+
traced agent, the child run links via `parent_trace_id`. No env plumbing
|
|
90
|
+
required for in-process sub-agents.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
@cartha.trace(id="parent")
|
|
94
|
+
async def orchestrator(user_id: str):
|
|
95
|
+
await cartha.ops.delegate(to_agent_id="worker", task_description="handle")
|
|
96
|
+
return await worker(user_id)
|
|
97
|
+
|
|
98
|
+
@cartha.trace(id="worker") # nests under orchestrator automatically
|
|
99
|
+
async def worker(user_id: str):
|
|
100
|
+
...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Cost per completed task
|
|
104
|
+
|
|
105
|
+
Share a `task_id` across retries so the dashboard rolls failed redoes into one outcome:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
tid = cartha.task_context()
|
|
109
|
+
for attempt in range(3):
|
|
110
|
+
try:
|
|
111
|
+
await run_agent(..., cartha_task_id=tid)
|
|
112
|
+
break
|
|
113
|
+
except Exception:
|
|
114
|
+
continue
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Memory denial mode
|
|
118
|
+
|
|
119
|
+
Org setting (`PATCH /org` → `memory_denial_mode`):
|
|
120
|
+
|
|
121
|
+
- `denied_hint` (default): recall returns explicit denials without content so agents don't invent over a hole.
|
|
122
|
+
- `silent`: empty hits only (no existence leak).
|
|
123
|
+
|
|
124
|
+
## Advanced
|
|
125
|
+
|
|
126
|
+
The full instrumentation API lives in `cartha.ops` (tool calls, cost events,
|
|
127
|
+
A2A delegation, policy checks, run nesting):
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from cartha import ops
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Legacy alias:** older code may `from nexus import ops`. That still works for
|
|
134
|
+
compatibility, but new code should use `cartha`.
|
|
135
|
+
|
|
136
|
+
**Trace diff:** `GET /api/v1/traces/diff?left=…&right=…` finds the first
|
|
137
|
+
diverging step between two runs (also on the dashboard).
|
|
138
|
+
|
|
139
|
+
## Publishing (maintainers)
|
|
140
|
+
|
|
141
|
+
Releases are published from GitHub Actions when you push a tag matching
|
|
142
|
+
`sdk-python-v*` (for example `sdk-python-v0.1.0`).
|
|
143
|
+
|
|
144
|
+
Manual upload (emergency / first release):
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
cd nexus/packages/sdk-python
|
|
148
|
+
python -m pip install --upgrade build twine
|
|
149
|
+
python -m build
|
|
150
|
+
# TestPyPI first (recommended):
|
|
151
|
+
python -m twine upload --repository testpypi dist/*
|
|
152
|
+
# Production PyPI:
|
|
153
|
+
python -m twine upload dist/*
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Use a PyPI API token (`pypi-...`), not your account password.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Cartha SDK (Python)
|
|
2
|
+
|
|
3
|
+
Instrument AI agents with memory, traces, costs, and policies on [Cartha](https://cartha.in).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install cartha-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Python 3.10+.
|
|
12
|
+
|
|
13
|
+
From source (development):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install "git+https://github.com/maulik-jadav/nexus.git#subdirectory=nexus/packages/sdk-python"
|
|
17
|
+
# or from a local clone:
|
|
18
|
+
pip install -e nexus/packages/sdk-python
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export CARTHA_API_KEY="cartha_..." # from the dashboard → Keys
|
|
25
|
+
export CARTHA_API_BASE="https://cartha.in" # your Cartha deployment
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import cartha
|
|
30
|
+
|
|
31
|
+
cartha.init() # picks up CARTHA_* env vars
|
|
32
|
+
|
|
33
|
+
@cartha.trace()
|
|
34
|
+
def run_agent(prompt: str):
|
|
35
|
+
return "..."
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`@cartha.trace()` works on sync and async functions. Every call registers the
|
|
39
|
+
agent, sends heartbeats, and records a full trace (start → steps → finish),
|
|
40
|
+
including failures.
|
|
41
|
+
|
|
42
|
+
## Memory
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import cartha
|
|
46
|
+
|
|
47
|
+
# async agents
|
|
48
|
+
await cartha.remember(user_id="u1", content="Prefers dark mode", scope="user")
|
|
49
|
+
hits = await cartha.recall(user_id="u1", context="UI preferences", scope=["user", "team"])
|
|
50
|
+
|
|
51
|
+
# sync code
|
|
52
|
+
cartha.remember_sync(user_id="u1", content="Prefers dark mode", scope="user")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Scopes: `agent` (private) · `user` (all agents serving that user) ·
|
|
56
|
+
`team` (same `team_id`) · `org` (everyone in your org).
|
|
57
|
+
|
|
58
|
+
## Nested agents (multi-agent loops)
|
|
59
|
+
|
|
60
|
+
By default, `@cartha.trace()` **auto-nests**: if a traced agent calls another
|
|
61
|
+
traced agent, the child run links via `parent_trace_id`. No env plumbing
|
|
62
|
+
required for in-process sub-agents.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
@cartha.trace(id="parent")
|
|
66
|
+
async def orchestrator(user_id: str):
|
|
67
|
+
await cartha.ops.delegate(to_agent_id="worker", task_description="handle")
|
|
68
|
+
return await worker(user_id)
|
|
69
|
+
|
|
70
|
+
@cartha.trace(id="worker") # nests under orchestrator automatically
|
|
71
|
+
async def worker(user_id: str):
|
|
72
|
+
...
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Cost per completed task
|
|
76
|
+
|
|
77
|
+
Share a `task_id` across retries so the dashboard rolls failed redoes into one outcome:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
tid = cartha.task_context()
|
|
81
|
+
for attempt in range(3):
|
|
82
|
+
try:
|
|
83
|
+
await run_agent(..., cartha_task_id=tid)
|
|
84
|
+
break
|
|
85
|
+
except Exception:
|
|
86
|
+
continue
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Memory denial mode
|
|
90
|
+
|
|
91
|
+
Org setting (`PATCH /org` → `memory_denial_mode`):
|
|
92
|
+
|
|
93
|
+
- `denied_hint` (default): recall returns explicit denials without content so agents don't invent over a hole.
|
|
94
|
+
- `silent`: empty hits only (no existence leak).
|
|
95
|
+
|
|
96
|
+
## Advanced
|
|
97
|
+
|
|
98
|
+
The full instrumentation API lives in `cartha.ops` (tool calls, cost events,
|
|
99
|
+
A2A delegation, policy checks, run nesting):
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from cartha import ops
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Legacy alias:** older code may `from nexus import ops`. That still works for
|
|
106
|
+
compatibility, but new code should use `cartha`.
|
|
107
|
+
|
|
108
|
+
**Trace diff:** `GET /api/v1/traces/diff?left=…&right=…` finds the first
|
|
109
|
+
diverging step between two runs (also on the dashboard).
|
|
110
|
+
|
|
111
|
+
## Publishing (maintainers)
|
|
112
|
+
|
|
113
|
+
Releases are published from GitHub Actions when you push a tag matching
|
|
114
|
+
`sdk-python-v*` (for example `sdk-python-v0.1.0`).
|
|
115
|
+
|
|
116
|
+
Manual upload (emergency / first release):
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cd nexus/packages/sdk-python
|
|
120
|
+
python -m pip install --upgrade build twine
|
|
121
|
+
python -m build
|
|
122
|
+
# TestPyPI first (recommended):
|
|
123
|
+
python -m twine upload --repository testpypi dist/*
|
|
124
|
+
# Production PyPI:
|
|
125
|
+
python -m twine upload dist/*
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use a PyPI API token (`pypi-...`), not your account password.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cartha SDK — instrument AI agents with memory, traces, costs, and policies.
|
|
3
|
+
|
|
4
|
+
Quickstart (mirrors the dashboard Settings page):
|
|
5
|
+
|
|
6
|
+
export CARTHA_API_KEY="cartha_..."
|
|
7
|
+
export CARTHA_API_BASE="https://cartha.in"
|
|
8
|
+
|
|
9
|
+
import cartha
|
|
10
|
+
cartha.init()
|
|
11
|
+
|
|
12
|
+
@cartha.trace()
|
|
13
|
+
def run_agent(prompt: str):
|
|
14
|
+
return "..."
|
|
15
|
+
|
|
16
|
+
`cartha.trace()` works on both sync and async functions. Async functions keep
|
|
17
|
+
their full signature and are awaited as usual; sync functions are executed
|
|
18
|
+
through the same instrumentation pipeline via a private event loop.
|
|
19
|
+
|
|
20
|
+
Advanced users can access the underlying instrumentation API directly:
|
|
21
|
+
|
|
22
|
+
from cartha import ops
|
|
23
|
+
await ops.remember(...)
|
|
24
|
+
await ops.recall(...)
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import asyncio
|
|
29
|
+
import os
|
|
30
|
+
from typing import Any, Callable, Literal
|
|
31
|
+
|
|
32
|
+
from nexus import ops
|
|
33
|
+
from nexus.ops import ( # noqa: F401 — re-exported public API
|
|
34
|
+
PolicyViolation,
|
|
35
|
+
cost,
|
|
36
|
+
cost_sync,
|
|
37
|
+
delegate,
|
|
38
|
+
delegate_sync,
|
|
39
|
+
propagation_context,
|
|
40
|
+
recall,
|
|
41
|
+
recall_sync,
|
|
42
|
+
remember,
|
|
43
|
+
remember_sync,
|
|
44
|
+
task_context,
|
|
45
|
+
tool_call,
|
|
46
|
+
tool_call_sync,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"init",
|
|
51
|
+
"trace",
|
|
52
|
+
"ops",
|
|
53
|
+
"PolicyViolation",
|
|
54
|
+
"recall", "remember", "tool_call", "cost", "delegate", "propagation_context",
|
|
55
|
+
"task_context",
|
|
56
|
+
"recall_sync", "remember_sync", "tool_call_sync", "cost_sync", "delegate_sync",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
__version__ = "0.1.0"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def init(
|
|
63
|
+
api_key: str | None = None,
|
|
64
|
+
api_base: str | None = None,
|
|
65
|
+
org_id: str | None = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Configure the SDK. Values are resolved in priority order:
|
|
69
|
+
explicit argument > CARTHA_* env var > existing NEXUS_* env var.
|
|
70
|
+
|
|
71
|
+
Safe to call multiple times; later calls override earlier ones.
|
|
72
|
+
"""
|
|
73
|
+
key = api_key or os.getenv("CARTHA_API_KEY")
|
|
74
|
+
if key:
|
|
75
|
+
os.environ["NEXUS_API_KEY"] = key
|
|
76
|
+
|
|
77
|
+
base = api_base or os.getenv("CARTHA_API_BASE")
|
|
78
|
+
if base:
|
|
79
|
+
os.environ["NEXUS_ENDPOINT"] = base.rstrip("/")
|
|
80
|
+
|
|
81
|
+
org = org_id or os.getenv("CARTHA_ORG_ID")
|
|
82
|
+
if org:
|
|
83
|
+
os.environ["NEXUS_ORG_ID"] = org
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def trace(
|
|
87
|
+
id: str | None = None,
|
|
88
|
+
*,
|
|
89
|
+
team: str | None = None,
|
|
90
|
+
org: str | None = None,
|
|
91
|
+
protocols: list[Literal["mcp", "a2a"]] | None = None,
|
|
92
|
+
description: str = "",
|
|
93
|
+
task_id: str | None = None,
|
|
94
|
+
nest: bool = True,
|
|
95
|
+
) -> Callable:
|
|
96
|
+
"""
|
|
97
|
+
Instrument an agent function: registration, heartbeats, trace
|
|
98
|
+
start/steps/finish, and failure capture — all automatic.
|
|
99
|
+
|
|
100
|
+
nest=True (default): if called inside another traced agent, this run
|
|
101
|
+
becomes a child trace (parent_trace_id) — required for multi-agent loops.
|
|
102
|
+
|
|
103
|
+
task_id: group retries of the same logical outcome for cost-per-task.
|
|
104
|
+
Pass cartha_task_id= at call time to override per-invocation.
|
|
105
|
+
"""
|
|
106
|
+
team_id = team or os.getenv("CARTHA_TEAM_ID", "default_team")
|
|
107
|
+
|
|
108
|
+
def decorator(fn: Callable) -> Callable:
|
|
109
|
+
agent_key = id or fn.__name__
|
|
110
|
+
ops_decorator = ops.agent(
|
|
111
|
+
id=agent_key,
|
|
112
|
+
team=team_id,
|
|
113
|
+
org=org,
|
|
114
|
+
protocols=protocols,
|
|
115
|
+
description=description,
|
|
116
|
+
task_id=task_id,
|
|
117
|
+
nest=nest,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if asyncio.iscoroutinefunction(fn):
|
|
121
|
+
return ops_decorator(fn)
|
|
122
|
+
|
|
123
|
+
# Sync function: route it through the async instrumentation pipeline.
|
|
124
|
+
# The body runs in a worker thread so that sync helpers like
|
|
125
|
+
# remember_sync()/recall_sync() (which call asyncio.run internally)
|
|
126
|
+
# aren't executed inside this already-running event loop. to_thread
|
|
127
|
+
# copies contextvars, so the trace context follows into the thread.
|
|
128
|
+
async def _async_shim(*args: Any, **kwargs: Any) -> Any:
|
|
129
|
+
return await asyncio.to_thread(fn, *args, **kwargs)
|
|
130
|
+
|
|
131
|
+
_async_shim.__name__ = fn.__name__
|
|
132
|
+
_async_shim.__doc__ = fn.__doc__
|
|
133
|
+
instrumented = ops_decorator(_async_shim)
|
|
134
|
+
|
|
135
|
+
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
136
|
+
return asyncio.run(instrumented(*args, **kwargs))
|
|
137
|
+
|
|
138
|
+
sync_wrapper.__name__ = fn.__name__
|
|
139
|
+
sync_wrapper.__doc__ = fn.__doc__
|
|
140
|
+
return sync_wrapper
|
|
141
|
+
|
|
142
|
+
return decorator
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cartha-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cartha SDK — memory, traces, costs, and policies for AI agents
|
|
5
|
+
Author-email: Maulik Jadav <maulikjadav239@gmail.com>
|
|
6
|
+
Maintainer-email: Maulik Jadav <maulikjadav239@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://cartha.in
|
|
9
|
+
Project-URL: Documentation, https://cartha.in
|
|
10
|
+
Project-URL: Repository, https://github.com/maulik-jadav/nexus
|
|
11
|
+
Project-URL: Issues, https://github.com/maulik-jadav/nexus/issues
|
|
12
|
+
Project-URL: Source, https://github.com/maulik-jadav/nexus/tree/main/nexus/packages/sdk-python
|
|
13
|
+
Keywords: cartha,agents,llm,observability,tracing,memory,ai
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: httpx>=0.27
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# Cartha SDK (Python)
|
|
30
|
+
|
|
31
|
+
Instrument AI agents with memory, traces, costs, and policies on [Cartha](https://cartha.in).
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install cartha-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires Python 3.10+.
|
|
40
|
+
|
|
41
|
+
From source (development):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install "git+https://github.com/maulik-jadav/nexus.git#subdirectory=nexus/packages/sdk-python"
|
|
45
|
+
# or from a local clone:
|
|
46
|
+
pip install -e nexus/packages/sdk-python
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export CARTHA_API_KEY="cartha_..." # from the dashboard → Keys
|
|
53
|
+
export CARTHA_API_BASE="https://cartha.in" # your Cartha deployment
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import cartha
|
|
58
|
+
|
|
59
|
+
cartha.init() # picks up CARTHA_* env vars
|
|
60
|
+
|
|
61
|
+
@cartha.trace()
|
|
62
|
+
def run_agent(prompt: str):
|
|
63
|
+
return "..."
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`@cartha.trace()` works on sync and async functions. Every call registers the
|
|
67
|
+
agent, sends heartbeats, and records a full trace (start → steps → finish),
|
|
68
|
+
including failures.
|
|
69
|
+
|
|
70
|
+
## Memory
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import cartha
|
|
74
|
+
|
|
75
|
+
# async agents
|
|
76
|
+
await cartha.remember(user_id="u1", content="Prefers dark mode", scope="user")
|
|
77
|
+
hits = await cartha.recall(user_id="u1", context="UI preferences", scope=["user", "team"])
|
|
78
|
+
|
|
79
|
+
# sync code
|
|
80
|
+
cartha.remember_sync(user_id="u1", content="Prefers dark mode", scope="user")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Scopes: `agent` (private) · `user` (all agents serving that user) ·
|
|
84
|
+
`team` (same `team_id`) · `org` (everyone in your org).
|
|
85
|
+
|
|
86
|
+
## Nested agents (multi-agent loops)
|
|
87
|
+
|
|
88
|
+
By default, `@cartha.trace()` **auto-nests**: if a traced agent calls another
|
|
89
|
+
traced agent, the child run links via `parent_trace_id`. No env plumbing
|
|
90
|
+
required for in-process sub-agents.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
@cartha.trace(id="parent")
|
|
94
|
+
async def orchestrator(user_id: str):
|
|
95
|
+
await cartha.ops.delegate(to_agent_id="worker", task_description="handle")
|
|
96
|
+
return await worker(user_id)
|
|
97
|
+
|
|
98
|
+
@cartha.trace(id="worker") # nests under orchestrator automatically
|
|
99
|
+
async def worker(user_id: str):
|
|
100
|
+
...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Cost per completed task
|
|
104
|
+
|
|
105
|
+
Share a `task_id` across retries so the dashboard rolls failed redoes into one outcome:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
tid = cartha.task_context()
|
|
109
|
+
for attempt in range(3):
|
|
110
|
+
try:
|
|
111
|
+
await run_agent(..., cartha_task_id=tid)
|
|
112
|
+
break
|
|
113
|
+
except Exception:
|
|
114
|
+
continue
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Memory denial mode
|
|
118
|
+
|
|
119
|
+
Org setting (`PATCH /org` → `memory_denial_mode`):
|
|
120
|
+
|
|
121
|
+
- `denied_hint` (default): recall returns explicit denials without content so agents don't invent over a hole.
|
|
122
|
+
- `silent`: empty hits only (no existence leak).
|
|
123
|
+
|
|
124
|
+
## Advanced
|
|
125
|
+
|
|
126
|
+
The full instrumentation API lives in `cartha.ops` (tool calls, cost events,
|
|
127
|
+
A2A delegation, policy checks, run nesting):
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from cartha import ops
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Legacy alias:** older code may `from nexus import ops`. That still works for
|
|
134
|
+
compatibility, but new code should use `cartha`.
|
|
135
|
+
|
|
136
|
+
**Trace diff:** `GET /api/v1/traces/diff?left=…&right=…` finds the first
|
|
137
|
+
diverging step between two runs (also on the dashboard).
|
|
138
|
+
|
|
139
|
+
## Publishing (maintainers)
|
|
140
|
+
|
|
141
|
+
Releases are published from GitHub Actions when you push a tag matching
|
|
142
|
+
`sdk-python-v*` (for example `sdk-python-v0.1.0`).
|
|
143
|
+
|
|
144
|
+
Manual upload (emergency / first release):
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
cd nexus/packages/sdk-python
|
|
148
|
+
python -m pip install --upgrade build twine
|
|
149
|
+
python -m build
|
|
150
|
+
# TestPyPI first (recommended):
|
|
151
|
+
python -m twine upload --repository testpypi dist/*
|
|
152
|
+
# Production PyPI:
|
|
153
|
+
python -m twine upload dist/*
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Use a PyPI API token (`pypi-...`), not your account password.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
./cartha/__init__.py
|
|
5
|
+
./nexus/__init__.py
|
|
6
|
+
./nexus/ops.py
|
|
7
|
+
cartha/__init__.py
|
|
8
|
+
cartha_sdk.egg-info/PKG-INFO
|
|
9
|
+
cartha_sdk.egg-info/SOURCES.txt
|
|
10
|
+
cartha_sdk.egg-info/dependency_links.txt
|
|
11
|
+
cartha_sdk.egg-info/requires.txt
|
|
12
|
+
cartha_sdk.egg-info/top_level.txt
|
|
13
|
+
nexus/__init__.py
|
|
14
|
+
nexus/ops.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx>=0.27
|