rootmark 0.1.0__tar.gz → 0.1.1__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.
- rootmark-0.1.1/CHANGELOG.md +90 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/PKG-INFO +67 -1
- rootmark-0.1.1/README.md +162 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/pyproject.toml +58 -56
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/__init__.py +300 -234
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/config.py +267 -267
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/core/emitter.py +650 -514
- rootmark-0.1.1/src/rootmark/core/lifecycle.py +94 -0
- rootmark-0.1.1/src/rootmark/core/step.py +72 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/_bedrock_helpers.py +207 -169
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/boto3_interceptor.py +624 -615
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/crewai.py +480 -480
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/strands.py +685 -669
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/strands_multiagent.py +576 -571
- rootmark-0.1.1/src/rootmark/schema/trace_event.py +129 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/core/test_emitter_bind_context.py +130 -129
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/core/test_emitter_session.py +106 -105
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/patches/test_agentcore.py +545 -544
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/patches/test_strands.py +300 -299
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_boto3_interceptor.py +1282 -1216
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_config_override.py +229 -221
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_crewai_patch.py +182 -180
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_langchain_handler.py +99 -98
- rootmark-0.1.1/tests/test_span_lifecycle.py +255 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_span_tags.py +218 -201
- rootmark-0.1.1/tests/test_step_contract.py +53 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_strands_agent_live.py +320 -303
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_strands_multiagent.py +472 -454
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_strands_multiagent_live.py +404 -387
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_strands_patch.py +676 -675
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_strands_session.py +307 -290
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_suppress_payloads.py +202 -185
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_trace_and_span.py +384 -383
- rootmark-0.1.0/README.md +0 -96
- rootmark-0.1.0/src/rootmark/schema/trace_event.py +0 -83
- {rootmark-0.1.0 → rootmark-0.1.1}/.gitignore +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/audit/__init__.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/audit/hashing.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/core/__init__.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/core/processor.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/__init__.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/agentcore.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/langchain.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/openai.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/patches/strands_session.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/src/rootmark/schema/__init__.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/core/test_processor_envelope.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/integration/conftest.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/integration/test_strands_e2e.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/patches/__init__.py +0 -0
- {rootmark-0.1.0 → rootmark-0.1.1}/tests/test_roundtrip_ingestion.py +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to the `rootmark` SDK. Versions follow the collector's own
|
|
4
|
+
releases loosely; where the two must match, it says so.
|
|
5
|
+
|
|
6
|
+
## 0.1.1
|
|
7
|
+
|
|
8
|
+
**Upgrading from 0.1.0 requires a collector upgrade first.** See *Compatibility*
|
|
9
|
+
below — this is not optional, and getting the order wrong loses data.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Spans are sent when they start, not only when they finish.** Before this a
|
|
14
|
+
span reached Rootmark only from `end_span`, so anything in flight was simply
|
|
15
|
+
absent — and a step paused for human review was indistinguishable from one
|
|
16
|
+
wedged against a model that had stopped answering. A run is now visible from
|
|
17
|
+
the moment its first span opens.
|
|
18
|
+
- `rootmark.mark_waiting(span, reason)` — say a span is deliberately not
|
|
19
|
+
working, and what it is waiting on. `reason` is free text in the caller's own
|
|
20
|
+
vocabulary (`"human_review"`, `"rate_limit"`, `"upstream"`); the SDK does not
|
|
21
|
+
fix the set, because encoding one application's review workflow into the
|
|
22
|
+
schema would make it everyone's.
|
|
23
|
+
- `heartbeat=` on `rootmark.span()` — re-announce a long span on an interval so
|
|
24
|
+
a killed process can be told apart from a slow one. Opt-in per span, because
|
|
25
|
+
a heartbeat on a 200 ms call is volume spent to say nothing. Intervals below
|
|
26
|
+
five seconds are raised to five rather than refused.
|
|
27
|
+
- `step_key` / `step_kind` on spans, and `SpanEmitter.set_step()` for callers
|
|
28
|
+
that learn what a step is only after starting it. `step_key` is a stable
|
|
29
|
+
identity for which logical step a span is; `step_kind` is what it was for.
|
|
30
|
+
A kind the SDK does not recognise is kept, not dropped.
|
|
31
|
+
- `SpanStatus` gains `running`, `waiting` and `cancelled`, plus an `is_terminal`
|
|
32
|
+
property. The first three values are unchanged.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **`TraceEvent` carries `row_version` and `waiting_reason`.** `row_version`
|
|
37
|
+
orders the several writes a span now produces so the collector can keep the
|
|
38
|
+
latest: start is 1, heartbeats climb, the end row takes a sentinel above every
|
|
39
|
+
possible heartbeat. A rank rather than a timestamp, because rows arrive from
|
|
40
|
+
different processes over an at-least-once channel and a clock cannot order
|
|
41
|
+
them.
|
|
42
|
+
- **Bedrock guardrail traces no longer include `modelOutput`**, and the size cap
|
|
43
|
+
rose from 4 KB to 16 KB. Bedrock echoes the model's whole answer back inside
|
|
44
|
+
the guardrail trace; on a long answer that alone overran the old cap, so the
|
|
45
|
+
payload was stored as a truncated string and the `inputAssessment` naming the
|
|
46
|
+
policy that fired was never reached. A real deployment lost a `PROMPT_ATTACK`
|
|
47
|
+
block that way. The removal is recorded under `rootmarkOmitted`, so a reader
|
|
48
|
+
can tell a pruned trace from one Bedrock never sent the key for.
|
|
49
|
+
**If you read `extra.bedrock_guardrail_trace` expecting `modelOutput`, read
|
|
50
|
+
the model's response from the span's outputs instead.**
|
|
51
|
+
- A non-terminal span reports `duration_ms` of 0 and an `end_time` equal to its
|
|
52
|
+
`start_time`. **Read the status, not the duration**, to decide whether a span
|
|
53
|
+
has finished — a 0 ms span is otherwise a plausible-looking lie.
|
|
54
|
+
|
|
55
|
+
### Compatibility
|
|
56
|
+
|
|
57
|
+
| | |
|
|
58
|
+
|---|---|
|
|
59
|
+
| Collector | Needs ClickHouse migrations `007`–`009`. Upgrade it **before** the SDK. |
|
|
60
|
+
| Python | 3.12+, unchanged |
|
|
61
|
+
| Wire format | Additive; `row_version` and `waiting_reason` are new fields |
|
|
62
|
+
|
|
63
|
+
A collector older than this release deduplicates buffered spans on
|
|
64
|
+
`(project_id, span_id)` and keeps the **first** row it sees. With a 0.1.1
|
|
65
|
+
client that first row is the *start* row, so the end row — the one carrying the
|
|
66
|
+
result, the duration and the token counts — is discarded. Runs appear and never
|
|
67
|
+
finish. Where the two rows land in different batches instead, the old storage
|
|
68
|
+
engine has no version column to order them by and the survivor is undefined.
|
|
69
|
+
|
|
70
|
+
Neither failure reports an error. Upgrade the collector first, or stay on 0.1.0.
|
|
71
|
+
|
|
72
|
+
### Cost
|
|
73
|
+
|
|
74
|
+
Each span is now sent at least twice, so **a run produces roughly twice the
|
|
75
|
+
rows** it did on 0.1.0, plus one per heartbeat where heartbeats are enabled.
|
|
76
|
+
The collector collapses the pair when it merges, so stored size settles back;
|
|
77
|
+
what doubles is what crosses the network and passes through ingest. There is no
|
|
78
|
+
flag to turn it off — the alternative is being blind to work in progress, which
|
|
79
|
+
is the whole point of the release.
|
|
80
|
+
|
|
81
|
+
## 0.1.0
|
|
82
|
+
|
|
83
|
+
First public release. Span tree recording, framework instrumentation for
|
|
84
|
+
OpenAI, LangChain, CrewAI, Strands and Bedrock via botocore, batched background
|
|
85
|
+
delivery with a spill file and replay-safe batch identity, and
|
|
86
|
+
`suppress_payloads` for collectors outside the trust boundary.
|
|
87
|
+
|
|
88
|
+
No licence is declared, which means all rights reserved. That is unchanged in
|
|
89
|
+
0.1.1 and remains the thing to fix before this package is genuinely installable
|
|
90
|
+
by anyone outside the organisation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: rootmark
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Rootmark SDK -- agent instrumentation
|
|
5
5
|
Project-URL: Homepage, https://rootmark.dev
|
|
6
6
|
Project-URL: Documentation, https://rootmark.dev/docs/
|
|
@@ -51,6 +51,29 @@ pip install rootmark
|
|
|
51
51
|
|
|
52
52
|
Requires Python 3.12 or newer.
|
|
53
53
|
|
|
54
|
+
> **Upgrading from 0.1.0 needs a collector upgrade too.** Since 0.1.1 a span is
|
|
55
|
+
> sent when it *starts* as well as when it ends, so that work in progress is
|
|
56
|
+
> visible at all. A collector older than this release stores the first row it
|
|
57
|
+
> sees for a span and discards the rest — which is the end row, the one
|
|
58
|
+
> carrying the result and the duration. Runs would appear and never finish.
|
|
59
|
+
> Upgrade the collector first (it needs ClickHouse migrations `007`–`009`), or
|
|
60
|
+
> stay on 0.1.0.
|
|
61
|
+
|
|
62
|
+
## What changed in 0.1.1
|
|
63
|
+
|
|
64
|
+
- **Runs are visible while they are still going.** A span is recorded when it
|
|
65
|
+
starts, not only when it finishes.
|
|
66
|
+
- **`rootmark.mark_waiting(span, reason)`** distinguishes a step paused on a
|
|
67
|
+
human from one wedged against a model that stopped answering.
|
|
68
|
+
- **`heartbeat=`** on `rootmark.span()`, so a long step keeps saying it is alive.
|
|
69
|
+
- **Steps**: `step_key` and `step_kind` name which logical step a span is, so
|
|
70
|
+
the collector can aggregate the same step across runs.
|
|
71
|
+
- Bedrock guardrail traces no longer carry `modelOutput`, and the size cap rose
|
|
72
|
+
from 4 KB to 16 KB. A long answer used to overrun the cap and take the policy
|
|
73
|
+
assessment with it. Read the model's response from the span's outputs instead.
|
|
74
|
+
|
|
75
|
+
See `CHANGELOG.md` for the full list.
|
|
76
|
+
|
|
54
77
|
## Quickstart
|
|
55
78
|
|
|
56
79
|
```python
|
|
@@ -85,6 +108,49 @@ agent loop spawns, because the binding follows the async context.
|
|
|
85
108
|
`workflow_name` should not be `"default"`: that value lets framework
|
|
86
109
|
instrumentation override the name you bind per run with its own agent id.
|
|
87
110
|
|
|
111
|
+
## Naming the steps of a run
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
with rootmark.span("fetch_clauses", run_type=RunType.TOOL,
|
|
115
|
+
step_key="fetch_clauses", step_kind="retrieval"):
|
|
116
|
+
...
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`step_key` is a stable identity for *which* logical step this is, constant
|
|
120
|
+
across runs; `step_kind` is what it was for (`retrieval`, `generation`,
|
|
121
|
+
`routing`, `review`, `tool`, `memory`). Together they let the collector answer
|
|
122
|
+
"which step is slow" without anyone reading individual traces.
|
|
123
|
+
|
|
124
|
+
Both are optional, and a kind the SDK does not recognise is kept rather than
|
|
125
|
+
dropped — your vocabulary is allowed to be ahead of ours. Derive `step_key`
|
|
126
|
+
from something stable: a key built from a timestamp or a request id fragments
|
|
127
|
+
its own aggregate, and nothing can detect that for you.
|
|
128
|
+
|
|
129
|
+
## Long-running steps
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
with rootmark.span("await_approval", run_type=RunType.TOOL, heartbeat=30) as span:
|
|
133
|
+
rootmark.mark_waiting(span, "human_review")
|
|
134
|
+
decision = wait_for_approval() # minutes, or hours
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`mark_waiting` says the step is deliberately not working, and what it is
|
|
138
|
+
waiting on. The reason is free text in your own vocabulary — `human_review`,
|
|
139
|
+
`rate_limit`, `upstream` — and is shown back verbatim. It is the difference
|
|
140
|
+
between a run somebody has to investigate and one they can leave alone.
|
|
141
|
+
|
|
142
|
+
`heartbeat` re-announces the span every N seconds while it runs. Leave it unset
|
|
143
|
+
for anything that finishes quickly: a span is already visible from the moment
|
|
144
|
+
it starts, and a heartbeat for a 200 ms call is bandwidth spent to say nothing.
|
|
145
|
+
Set it on the long ones, where the useful question is not "is it running" but
|
|
146
|
+
"is anyone still on the other end" — without it, a step whose process was
|
|
147
|
+
killed looks exactly like one that is simply taking its time. Intervals below
|
|
148
|
+
five seconds are raised to five rather than refused.
|
|
149
|
+
|
|
150
|
+
**This roughly doubles the rows a run produces**, since each span is now sent
|
|
151
|
+
at least twice. The collector collapses the pair when it merges, so stored size
|
|
152
|
+
settles back; what doubles is what crosses the network.
|
|
153
|
+
|
|
88
154
|
## Framework support
|
|
89
155
|
|
|
90
156
|
The base install instruments nothing on its own beyond direct use. Install the
|
rootmark-0.1.1/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Rootmark SDK
|
|
2
|
+
|
|
3
|
+
Agent instrumentation for [Rootmark](https://rootmark.dev) — observe, verify and
|
|
4
|
+
govern agent runs.
|
|
5
|
+
|
|
6
|
+
The SDK records what your agents actually did: the span tree of a run, the model
|
|
7
|
+
calls inside it, token counts, and the payloads you choose to keep. It sends
|
|
8
|
+
them to a Rootmark collector, which you can self-host.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install rootmark
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Requires Python 3.12 or newer.
|
|
15
|
+
|
|
16
|
+
> **Upgrading from 0.1.0 needs a collector upgrade too.** Since 0.1.1 a span is
|
|
17
|
+
> sent when it *starts* as well as when it ends, so that work in progress is
|
|
18
|
+
> visible at all. A collector older than this release stores the first row it
|
|
19
|
+
> sees for a span and discards the rest — which is the end row, the one
|
|
20
|
+
> carrying the result and the duration. Runs would appear and never finish.
|
|
21
|
+
> Upgrade the collector first (it needs ClickHouse migrations `007`–`009`), or
|
|
22
|
+
> stay on 0.1.0.
|
|
23
|
+
|
|
24
|
+
## What changed in 0.1.1
|
|
25
|
+
|
|
26
|
+
- **Runs are visible while they are still going.** A span is recorded when it
|
|
27
|
+
starts, not only when it finishes.
|
|
28
|
+
- **`rootmark.mark_waiting(span, reason)`** distinguishes a step paused on a
|
|
29
|
+
human from one wedged against a model that stopped answering.
|
|
30
|
+
- **`heartbeat=`** on `rootmark.span()`, so a long step keeps saying it is alive.
|
|
31
|
+
- **Steps**: `step_key` and `step_kind` name which logical step a span is, so
|
|
32
|
+
the collector can aggregate the same step across runs.
|
|
33
|
+
- Bedrock guardrail traces no longer carry `modelOutput`, and the size cap rose
|
|
34
|
+
from 4 KB to 16 KB. A long answer used to overrun the cap and take the policy
|
|
35
|
+
assessment with it. Read the model's response from the span's outputs instead.
|
|
36
|
+
|
|
37
|
+
See `CHANGELOG.md` for the full list.
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import rootmark
|
|
43
|
+
|
|
44
|
+
rootmark.init(
|
|
45
|
+
api_key="pk_...", # project API key
|
|
46
|
+
project_id="...", # UUID
|
|
47
|
+
api_key_id="...", # UUID
|
|
48
|
+
api_url="https://rootmark.example.com",
|
|
49
|
+
workflow_name="my-service", # not "default" -- see below
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
rootmark.patch() # instrument supported frameworks in place
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
That is the whole integration for the common case. `patch()` installs
|
|
56
|
+
instrumentation process-wide, so call it once at startup and before the first
|
|
57
|
+
agent invocation.
|
|
58
|
+
|
|
59
|
+
## Grouping and labelling runs
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
with rootmark.workflow_override("contract-review"):
|
|
63
|
+
with rootmark.bind_context(tenant_id="acme", session_id=session, tags=["surface:chat"]):
|
|
64
|
+
result = agent(prompt)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`bind_context` applies to everything that runs inside it, including tasks the
|
|
68
|
+
agent loop spawns, because the binding follows the async context.
|
|
69
|
+
|
|
70
|
+
`workflow_name` should not be `"default"`: that value lets framework
|
|
71
|
+
instrumentation override the name you bind per run with its own agent id.
|
|
72
|
+
|
|
73
|
+
## Naming the steps of a run
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
with rootmark.span("fetch_clauses", run_type=RunType.TOOL,
|
|
77
|
+
step_key="fetch_clauses", step_kind="retrieval"):
|
|
78
|
+
...
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`step_key` is a stable identity for *which* logical step this is, constant
|
|
82
|
+
across runs; `step_kind` is what it was for (`retrieval`, `generation`,
|
|
83
|
+
`routing`, `review`, `tool`, `memory`). Together they let the collector answer
|
|
84
|
+
"which step is slow" without anyone reading individual traces.
|
|
85
|
+
|
|
86
|
+
Both are optional, and a kind the SDK does not recognise is kept rather than
|
|
87
|
+
dropped — your vocabulary is allowed to be ahead of ours. Derive `step_key`
|
|
88
|
+
from something stable: a key built from a timestamp or a request id fragments
|
|
89
|
+
its own aggregate, and nothing can detect that for you.
|
|
90
|
+
|
|
91
|
+
## Long-running steps
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
with rootmark.span("await_approval", run_type=RunType.TOOL, heartbeat=30) as span:
|
|
95
|
+
rootmark.mark_waiting(span, "human_review")
|
|
96
|
+
decision = wait_for_approval() # minutes, or hours
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`mark_waiting` says the step is deliberately not working, and what it is
|
|
100
|
+
waiting on. The reason is free text in your own vocabulary — `human_review`,
|
|
101
|
+
`rate_limit`, `upstream` — and is shown back verbatim. It is the difference
|
|
102
|
+
between a run somebody has to investigate and one they can leave alone.
|
|
103
|
+
|
|
104
|
+
`heartbeat` re-announces the span every N seconds while it runs. Leave it unset
|
|
105
|
+
for anything that finishes quickly: a span is already visible from the moment
|
|
106
|
+
it starts, and a heartbeat for a 200 ms call is bandwidth spent to say nothing.
|
|
107
|
+
Set it on the long ones, where the useful question is not "is it running" but
|
|
108
|
+
"is anyone still on the other end" — without it, a step whose process was
|
|
109
|
+
killed looks exactly like one that is simply taking its time. Intervals below
|
|
110
|
+
five seconds are raised to five rather than refused.
|
|
111
|
+
|
|
112
|
+
**This roughly doubles the rows a run produces**, since each span is now sent
|
|
113
|
+
at least twice. The collector collapses the pair when it merges, so stored size
|
|
114
|
+
settles back; what doubles is what crosses the network.
|
|
115
|
+
|
|
116
|
+
## Framework support
|
|
117
|
+
|
|
118
|
+
The base install instruments nothing on its own beyond direct use. Install the
|
|
119
|
+
extra for what you actually run:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install "rootmark[openai]" # OpenAI 1.x and 2.x
|
|
123
|
+
pip install "rootmark[langchain]" # LangChain callback handler
|
|
124
|
+
pip install "rootmark[crewai]" # CrewAI event listeners
|
|
125
|
+
pip install "rootmark[strands]" # Strands agents / AWS Bedrock
|
|
126
|
+
pip install "rootmark[boto3]" # Bedrock via botocore, no code change
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
For boto3, a session built by hand rather than the default one needs
|
|
130
|
+
`rootmark.patch_session(session)` — the botocore hooks only reach the default
|
|
131
|
+
session otherwise.
|
|
132
|
+
|
|
133
|
+
## Delivery
|
|
134
|
+
|
|
135
|
+
Spans are batched on a background thread and flushed on a timer, so nothing
|
|
136
|
+
blocks a request. A failed export is written to a spill file and retried,
|
|
137
|
+
keeping its batch identity so the collector can recognise a replay rather than
|
|
138
|
+
double-count it. Ordering is preserved: a batch that cannot be delivered holds
|
|
139
|
+
back the ones behind it.
|
|
140
|
+
|
|
141
|
+
Point `spill_file` somewhere writable. The default is relative to the working
|
|
142
|
+
directory, which in a container is frequently owned by root while the process
|
|
143
|
+
runs as someone else — and a spill file that cannot be written is buffering you
|
|
144
|
+
silently do not have.
|
|
145
|
+
|
|
146
|
+
Call `rootmark.flush()` before a process exits if you want outstanding spans
|
|
147
|
+
delivered, and `rootmark.shutdown()` to stop the processor.
|
|
148
|
+
|
|
149
|
+
## Keeping payloads out
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
rootmark.init(..., suppress_payloads=True)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Spans are still recorded — timings, token counts, the shape of the run — but
|
|
156
|
+
prompt and completion text is not sent. Use this when the collector sits outside
|
|
157
|
+
the trust boundary that the data does not leave.
|
|
158
|
+
|
|
159
|
+
## Links
|
|
160
|
+
|
|
161
|
+
- Documentation: <https://rootmark.dev/docs/>
|
|
162
|
+
- Source: <https://github.com/IvanAdactic/rootmark>
|
|
@@ -1,56 +1,58 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "rootmark"
|
|
3
|
-
version = "0.1.
|
|
4
|
-
description = "Rootmark SDK -- agent instrumentation"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.12"
|
|
7
|
-
# No licence declared
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"Programming Language :: Python :: 3
|
|
15
|
-
"Programming Language :: Python :: 3.
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
[
|
|
44
|
-
|
|
45
|
-
build-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
[
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
[project]
|
|
2
|
+
name = "rootmark"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Rootmark SDK -- agent instrumentation"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
# No licence declared as of 0.1.1; to be added in a later release. Without
|
|
8
|
+
# one the package defaults to all rights reserved -- see the 0.1.0 release
|
|
9
|
+
# commit (329e16c) for the reasoning and why it still needs revisiting.
|
|
10
|
+
keywords = ["agents", "observability", "tracing", "llm", "instrumentation"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
"Programming Language :: Python :: 3.14",
|
|
18
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
19
|
+
"Topic :: System :: Monitoring",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"pydantic>=2.10.0",
|
|
24
|
+
"httpx>=0.28.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://rootmark.dev"
|
|
29
|
+
Documentation = "https://rootmark.dev/docs/"
|
|
30
|
+
Source = "https://github.com/IvanAdactic/rootmark"
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
# OpenAI integration (Group 4); tested with 1.x and 2.x
|
|
34
|
+
openai = ["openai>=1.40.0,<3"]
|
|
35
|
+
# LangChain callback handler (D4.7); BaseCallbackHandler is stable across 0.3.x and 1.x
|
|
36
|
+
langchain = ["langchain-core>=0.3,<2"]
|
|
37
|
+
# CrewAI event listeners (D5.6); BaseEventListener + crewai_event_bus (tested 0.86–1.x)
|
|
38
|
+
crewai = ["crewai>=0.86,<2"]
|
|
39
|
+
# Strands SDK / AWS Bedrock agent patch (D6.1)
|
|
40
|
+
strands = ["strands-agents>=1.26.0"]
|
|
41
|
+
# Boto3 / botocore interceptor for Bedrock zero-code-change path (D6.3)
|
|
42
|
+
boto3 = ["boto3>=1.35.0"]
|
|
43
|
+
dev = ["pytest>=8.0", "pytest-asyncio>=0.24", "openai>=1.40.0,<3", "langchain-core>=0.3,<2", "crewai>=0.86,<2"]
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["hatchling"]
|
|
47
|
+
build-backend = "hatchling.build"
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.wheel]
|
|
50
|
+
packages = ["src/rootmark"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
pythonpath = ["src"]
|
|
55
|
+
markers = [
|
|
56
|
+
"integration: needs docker-compose (API, worker, ClickHouse, Postgres)",
|
|
57
|
+
]
|
|
58
|
+
|