a2ea-py 0.1.0__tar.gz → 0.2.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.
- a2ea_py-0.2.0/PKG-INFO +195 -0
- a2ea_py-0.2.0/README.md +180 -0
- a2ea_py-0.2.0/a2ea/client.py +414 -0
- a2ea_py-0.2.0/a2ea/integrations/__init__.py +8 -0
- a2ea_py-0.2.0/a2ea/integrations/_common.py +129 -0
- a2ea_py-0.2.0/a2ea/integrations/crewai.py +151 -0
- a2ea_py-0.2.0/a2ea/integrations/langchain.py +128 -0
- a2ea_py-0.2.0/a2ea/integrations/openai_agents.py +132 -0
- a2ea_py-0.2.0/a2ea_py.egg-info/PKG-INFO +195 -0
- a2ea_py-0.2.0/a2ea_py.egg-info/SOURCES.txt +27 -0
- a2ea_py-0.2.0/a2ea_py.egg-info/requires.txt +10 -0
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/pyproject.toml +6 -1
- a2ea_py-0.2.0/tests/test_client.py +177 -0
- a2ea_py-0.2.0/tests/test_integrations.py +240 -0
- a2ea_py-0.1.0/PKG-INFO +0 -81
- a2ea_py-0.1.0/README.md +0 -72
- a2ea_py-0.1.0/a2ea/client.py +0 -223
- a2ea_py-0.1.0/a2ea_py.egg-info/PKG-INFO +0 -81
- a2ea_py-0.1.0/a2ea_py.egg-info/SOURCES.txt +0 -16
- a2ea_py-0.1.0/a2ea_py.egg-info/requires.txt +0 -1
- a2ea_py-0.1.0/tests/test_client.py +0 -46
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/a2ea/__init__.py +0 -0
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/a2ea/decorators.py +0 -0
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/a2ea_py.egg-info/dependency_links.txt +0 -0
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/a2ea_py.egg-info/top_level.txt +0 -0
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/setup.cfg +0 -0
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/setup.py +0 -0
- {a2ea_py-0.1.0 → a2ea_py-0.2.0}/tests/test_decorators.py +0 -0
a2ea_py-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: a2ea-py
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python client for the A2EA telemetry and attribution platform.
|
|
5
|
+
Author: AI Blockchain Ventures
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: requests>=2.31.0
|
|
9
|
+
Provides-Extra: langchain
|
|
10
|
+
Requires-Dist: langchain-core>=0.3; extra == "langchain"
|
|
11
|
+
Provides-Extra: crewai
|
|
12
|
+
Requires-Dist: crewai>=1.0; extra == "crewai"
|
|
13
|
+
Provides-Extra: openai-agents
|
|
14
|
+
Requires-Dist: openai-agents>=0.2; extra == "openai-agents"
|
|
15
|
+
|
|
16
|
+
# a2ea-py
|
|
17
|
+
|
|
18
|
+
Official Python SDK for sending A2EA telemetry to the backend.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install a2ea-py
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from a2ea import A2EAClient
|
|
30
|
+
|
|
31
|
+
client = A2EAClient(
|
|
32
|
+
api_key="your X-A2EA-API-Key"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@client.track_agent(task_type="data_processing")
|
|
37
|
+
def run_agent(payload):
|
|
38
|
+
return process(payload)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
client.flush(timeout=5)
|
|
42
|
+
client.close()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Send many completed tasks in one request when a worker already has a result batch:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
client.send_batch("data_processing", [
|
|
49
|
+
{"event_id": "evt_101", "duration_ms": 840, "outcome": "success"},
|
|
50
|
+
{"event_id": "evt_102", "duration_ms": 910, "outcome": "success"},
|
|
51
|
+
])
|
|
52
|
+
client.flush(timeout=5)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Optional configuration
|
|
56
|
+
|
|
57
|
+
- `base_url`
|
|
58
|
+
- Defaults to `https://a2ea-backend-526z2hkxna-uc.a.run.app`
|
|
59
|
+
- Set this when testing locally against `http://127.0.0.1:4080`
|
|
60
|
+
- `agent_id`
|
|
61
|
+
- Optional override if you want to assign a fixed agent identifier instead of the default host/process-based value
|
|
62
|
+
|
|
63
|
+
Example for local development:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from a2ea import A2EAClient
|
|
67
|
+
|
|
68
|
+
client = A2EAClient(
|
|
69
|
+
api_key="your X-A2EA-API-Key",
|
|
70
|
+
base_url="http://127.0.0.1:4080",
|
|
71
|
+
agent_id="agent_pipeline_014",
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use `A2EA_BASE_URL=https://a2ea-backend-526z2hkxna-uc.a.run.app` explicitly if your deployment pins runtime configuration through environment variables.
|
|
76
|
+
|
|
77
|
+
## Route work by A2EA shares
|
|
78
|
+
|
|
79
|
+
When workload management is on for a task type (Dashboard > Workload), A2EA publishes a routing share for each agent. A managing agent can pick the next agent with:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
agent_id = client.choose_agent("invoice_review", eligible_agent_ids=agents_allowed_for_this_task)
|
|
83
|
+
if agent_id is None:
|
|
84
|
+
agent_id = my_default_router(task) # workload management is off or no share applies
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`choose_agent` only picks among `eligible_agent_ids`, so your own permission rules still decide which agents may run a task. Use `client.get_allocation(task_type)` to read the full policy, shares and history. In automatic mode, reading the shares applies the next change once the cooldown has passed.
|
|
88
|
+
|
|
89
|
+
## Credit shared jobs
|
|
90
|
+
|
|
91
|
+
When several agents work on one job, give each of their events the same `workflow_id`. A2EA splits the job's value among them (by share of events, time, or criticality) and the credits always add up to the job total.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
client.send_telemetry("invoice_intake", 8000, "success", workflow_id="invoice_6040")
|
|
95
|
+
review_client.send_telemetry("invoice_review", 30000, "success", workflow_id="invoice_6040", criticality=5)
|
|
96
|
+
|
|
97
|
+
split = client.get_workflow("invoice_6040") # credits under the saved method
|
|
98
|
+
preview = client.get_workflow("invoice_6040", method="time_share")
|
|
99
|
+
totals = client.get_workflow_credits(days=30) # each agent's credit across workflows
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`criticality` is a whole number from 1 to 10 (1 when not set). Both fields are signed with the event. Batch events accept `workflow_id` and `criticality` keys too.
|
|
103
|
+
|
|
104
|
+
## Check a score
|
|
105
|
+
|
|
106
|
+
Every request that stores new events saves a score record with all its inputs. The score response includes `scoreRecordId` and `recordHash`.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
records = client.list_score_records(agent_id="agent_review")
|
|
110
|
+
record = client.get_score_record(records["records"][0]["id"])
|
|
111
|
+
for step in record["explanation"]:
|
|
112
|
+
print(step["title"], step["value"])
|
|
113
|
+
|
|
114
|
+
result = client.verify_score_record(record["id"]) # recomputes the score, checks hash, chain and events
|
|
115
|
+
assert result["valid"]
|
|
116
|
+
|
|
117
|
+
# Re-check the hash yourself:
|
|
118
|
+
assert A2EAClient.score_record_hash(record["recordJson"], record["previousHash"]) == record["recordHash"]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Weekly report
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
report = client.get_weekly_report() # last complete week, as a dict
|
|
125
|
+
csv_text = client.get_weekly_report(week_start="2026-10-26", as_csv=True)
|
|
126
|
+
print(report["summary"]["netValueUsd"], report["reportHash"])
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The JSON and CSV carry the same `reportHash`, report version and calculation version.
|
|
130
|
+
|
|
131
|
+
## Framework connectors
|
|
132
|
+
|
|
133
|
+
Thin connectors record agent framework runs with the same A2EA fields as the client: task type, agent, duration, success or failure, and a workflow ID so agents working on one job can share credit. Telemetry problems are logged and never raised into your agent. Each run gets a stable event ID, so it is never counted twice.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
pip install "a2ea-py[langchain]" # or [crewai], [openai-agents]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**LangChain**: a callback handler. One result per top-level run (chain, agent, model or tool); `track="tools"` records each tool call instead.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from a2ea.integrations.langchain import A2EACallbackHandler
|
|
143
|
+
|
|
144
|
+
handler = A2EACallbackHandler(task_type="invoice_review", agent_id="invoice_chain")
|
|
145
|
+
chain.invoke(invoice, config={"callbacks": [handler], "metadata": {"a2ea_workflow_id": f"invoice_{invoice_id}"}})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**CrewAI** (1.x): an event listener. One result per task, with the agent's role as the agent ID; tasks in one kickoff share a workflow ID. `track="crews"` records whole kickoffs.
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from a2ea.integrations.crewai import instrument
|
|
152
|
+
|
|
153
|
+
instrument(task_type="market_research", task_types={"summary": "report_writing"}) # once
|
|
154
|
+
crew.kickoff()
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**OpenAI Agents SDK**: a tracing processor. One result per agent that runs (including handoffs); a run's `group_id` becomes the workflow ID. `track="tools"` records function-tool calls.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from a2ea.integrations.openai_agents import install
|
|
161
|
+
|
|
162
|
+
install(task_type="support_triage") # once
|
|
163
|
+
await Runner.run(agent, "...", run_config=RunConfig(group_id=f"ticket_{ticket_id}"))
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Per run, metadata keys `a2ea_task_type`, `a2ea_workflow_id` and `a2ea_criticality` override the defaults (LangChain also reads `a2ea_agent_id`). Runnable examples, which work offline with stand-in models, are in `examples/`.
|
|
167
|
+
|
|
168
|
+
## What the client sends
|
|
169
|
+
|
|
170
|
+
Each tracked function call produces:
|
|
171
|
+
|
|
172
|
+
- `eventId`
|
|
173
|
+
- `agentId`
|
|
174
|
+
- `timestamp`
|
|
175
|
+
- `durationMs`
|
|
176
|
+
- `taskType`
|
|
177
|
+
- `outcome`
|
|
178
|
+
- `verification`
|
|
179
|
+
- `workflowId`, `criticality` and `parentEventId` (the step this one came from), when set (and a per-result `agent_id` when one client reports several agents)
|
|
180
|
+
|
|
181
|
+
The verification payload is signed locally with HMAC-SHA256 using the workspace API key so the backend can verify the telemetry envelope.
|
|
182
|
+
|
|
183
|
+
## Reliability behavior
|
|
184
|
+
|
|
185
|
+
- submission runs in a background thread
|
|
186
|
+
- transient failures are retried with backoff
|
|
187
|
+
- exhausted events are retained in a local fallback buffer
|
|
188
|
+
|
|
189
|
+
## Local package test commands
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
python -m unittest discover tests
|
|
193
|
+
python -m build
|
|
194
|
+
pip install dist/a2ea_py-0.1.0-py3-none-any.whl
|
|
195
|
+
```
|
a2ea_py-0.2.0/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# a2ea-py
|
|
2
|
+
|
|
3
|
+
Official Python SDK for sending A2EA telemetry to the backend.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install a2ea-py
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from a2ea import A2EAClient
|
|
15
|
+
|
|
16
|
+
client = A2EAClient(
|
|
17
|
+
api_key="your X-A2EA-API-Key"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@client.track_agent(task_type="data_processing")
|
|
22
|
+
def run_agent(payload):
|
|
23
|
+
return process(payload)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
client.flush(timeout=5)
|
|
27
|
+
client.close()
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Send many completed tasks in one request when a worker already has a result batch:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
client.send_batch("data_processing", [
|
|
34
|
+
{"event_id": "evt_101", "duration_ms": 840, "outcome": "success"},
|
|
35
|
+
{"event_id": "evt_102", "duration_ms": 910, "outcome": "success"},
|
|
36
|
+
])
|
|
37
|
+
client.flush(timeout=5)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Optional configuration
|
|
41
|
+
|
|
42
|
+
- `base_url`
|
|
43
|
+
- Defaults to `https://a2ea-backend-526z2hkxna-uc.a.run.app`
|
|
44
|
+
- Set this when testing locally against `http://127.0.0.1:4080`
|
|
45
|
+
- `agent_id`
|
|
46
|
+
- Optional override if you want to assign a fixed agent identifier instead of the default host/process-based value
|
|
47
|
+
|
|
48
|
+
Example for local development:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from a2ea import A2EAClient
|
|
52
|
+
|
|
53
|
+
client = A2EAClient(
|
|
54
|
+
api_key="your X-A2EA-API-Key",
|
|
55
|
+
base_url="http://127.0.0.1:4080",
|
|
56
|
+
agent_id="agent_pipeline_014",
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use `A2EA_BASE_URL=https://a2ea-backend-526z2hkxna-uc.a.run.app` explicitly if your deployment pins runtime configuration through environment variables.
|
|
61
|
+
|
|
62
|
+
## Route work by A2EA shares
|
|
63
|
+
|
|
64
|
+
When workload management is on for a task type (Dashboard > Workload), A2EA publishes a routing share for each agent. A managing agent can pick the next agent with:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
agent_id = client.choose_agent("invoice_review", eligible_agent_ids=agents_allowed_for_this_task)
|
|
68
|
+
if agent_id is None:
|
|
69
|
+
agent_id = my_default_router(task) # workload management is off or no share applies
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`choose_agent` only picks among `eligible_agent_ids`, so your own permission rules still decide which agents may run a task. Use `client.get_allocation(task_type)` to read the full policy, shares and history. In automatic mode, reading the shares applies the next change once the cooldown has passed.
|
|
73
|
+
|
|
74
|
+
## Credit shared jobs
|
|
75
|
+
|
|
76
|
+
When several agents work on one job, give each of their events the same `workflow_id`. A2EA splits the job's value among them (by share of events, time, or criticality) and the credits always add up to the job total.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
client.send_telemetry("invoice_intake", 8000, "success", workflow_id="invoice_6040")
|
|
80
|
+
review_client.send_telemetry("invoice_review", 30000, "success", workflow_id="invoice_6040", criticality=5)
|
|
81
|
+
|
|
82
|
+
split = client.get_workflow("invoice_6040") # credits under the saved method
|
|
83
|
+
preview = client.get_workflow("invoice_6040", method="time_share")
|
|
84
|
+
totals = client.get_workflow_credits(days=30) # each agent's credit across workflows
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`criticality` is a whole number from 1 to 10 (1 when not set). Both fields are signed with the event. Batch events accept `workflow_id` and `criticality` keys too.
|
|
88
|
+
|
|
89
|
+
## Check a score
|
|
90
|
+
|
|
91
|
+
Every request that stores new events saves a score record with all its inputs. The score response includes `scoreRecordId` and `recordHash`.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
records = client.list_score_records(agent_id="agent_review")
|
|
95
|
+
record = client.get_score_record(records["records"][0]["id"])
|
|
96
|
+
for step in record["explanation"]:
|
|
97
|
+
print(step["title"], step["value"])
|
|
98
|
+
|
|
99
|
+
result = client.verify_score_record(record["id"]) # recomputes the score, checks hash, chain and events
|
|
100
|
+
assert result["valid"]
|
|
101
|
+
|
|
102
|
+
# Re-check the hash yourself:
|
|
103
|
+
assert A2EAClient.score_record_hash(record["recordJson"], record["previousHash"]) == record["recordHash"]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Weekly report
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
report = client.get_weekly_report() # last complete week, as a dict
|
|
110
|
+
csv_text = client.get_weekly_report(week_start="2026-10-26", as_csv=True)
|
|
111
|
+
print(report["summary"]["netValueUsd"], report["reportHash"])
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The JSON and CSV carry the same `reportHash`, report version and calculation version.
|
|
115
|
+
|
|
116
|
+
## Framework connectors
|
|
117
|
+
|
|
118
|
+
Thin connectors record agent framework runs with the same A2EA fields as the client: task type, agent, duration, success or failure, and a workflow ID so agents working on one job can share credit. Telemetry problems are logged and never raised into your agent. Each run gets a stable event ID, so it is never counted twice.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pip install "a2ea-py[langchain]" # or [crewai], [openai-agents]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**LangChain**: a callback handler. One result per top-level run (chain, agent, model or tool); `track="tools"` records each tool call instead.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from a2ea.integrations.langchain import A2EACallbackHandler
|
|
128
|
+
|
|
129
|
+
handler = A2EACallbackHandler(task_type="invoice_review", agent_id="invoice_chain")
|
|
130
|
+
chain.invoke(invoice, config={"callbacks": [handler], "metadata": {"a2ea_workflow_id": f"invoice_{invoice_id}"}})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**CrewAI** (1.x): an event listener. One result per task, with the agent's role as the agent ID; tasks in one kickoff share a workflow ID. `track="crews"` records whole kickoffs.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from a2ea.integrations.crewai import instrument
|
|
137
|
+
|
|
138
|
+
instrument(task_type="market_research", task_types={"summary": "report_writing"}) # once
|
|
139
|
+
crew.kickoff()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**OpenAI Agents SDK**: a tracing processor. One result per agent that runs (including handoffs); a run's `group_id` becomes the workflow ID. `track="tools"` records function-tool calls.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from a2ea.integrations.openai_agents import install
|
|
146
|
+
|
|
147
|
+
install(task_type="support_triage") # once
|
|
148
|
+
await Runner.run(agent, "...", run_config=RunConfig(group_id=f"ticket_{ticket_id}"))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Per run, metadata keys `a2ea_task_type`, `a2ea_workflow_id` and `a2ea_criticality` override the defaults (LangChain also reads `a2ea_agent_id`). Runnable examples, which work offline with stand-in models, are in `examples/`.
|
|
152
|
+
|
|
153
|
+
## What the client sends
|
|
154
|
+
|
|
155
|
+
Each tracked function call produces:
|
|
156
|
+
|
|
157
|
+
- `eventId`
|
|
158
|
+
- `agentId`
|
|
159
|
+
- `timestamp`
|
|
160
|
+
- `durationMs`
|
|
161
|
+
- `taskType`
|
|
162
|
+
- `outcome`
|
|
163
|
+
- `verification`
|
|
164
|
+
- `workflowId`, `criticality` and `parentEventId` (the step this one came from), when set (and a per-result `agent_id` when one client reports several agents)
|
|
165
|
+
|
|
166
|
+
The verification payload is signed locally with HMAC-SHA256 using the workspace API key so the backend can verify the telemetry envelope.
|
|
167
|
+
|
|
168
|
+
## Reliability behavior
|
|
169
|
+
|
|
170
|
+
- submission runs in a background thread
|
|
171
|
+
- transient failures are retried with backoff
|
|
172
|
+
- exhausted events are retained in a local fallback buffer
|
|
173
|
+
|
|
174
|
+
## Local package test commands
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
python -m unittest discover tests
|
|
178
|
+
python -m build
|
|
179
|
+
pip install dist/a2ea_py-0.1.0-py3-none-any.whl
|
|
180
|
+
```
|