zambo-haystack 0.1.2__tar.gz → 0.1.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zambo-haystack
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Verifiable execution receipts for Haystack agents. Every tool call mints a verifiable AER-1 receipt at zambo.dev.
5
5
  Author-email: Zambo <brennanzambo@zambo.dev>
6
6
  License: MIT
@@ -102,3 +102,16 @@ Browse the full catalog any time with `zambo_haystack.list_tools()`, or pick too
102
102
  - What is an AI agent execution receipt?: https://zambo.dev/guides/ai-agent-execution-receipt/
103
103
 
104
104
  MIT License.
105
+
106
+ ## Local AER-1 receipts (no network needed)
107
+
108
+ The package also mints AER-1 execution receipts locally, with no API calls:
109
+
110
+ ```python
111
+ from zambo_haystack import mint_receipt, verify_receipt
112
+
113
+ receipt = mint_receipt("my_tool", {"query": "hello"}, {"answer": "hi"}, ms=42)
114
+ assert verify_receipt(receipt) # recomputes the SHA-256 locally
115
+ ```
116
+
117
+ Local receipts follow the AER-1 core members (`id`, `created_at`, `tool`, `provenance_class`, `canonical_bytes`, `output_hash`) and use the provenance class `EXECUTED BY ZAMBO-HAYSTACK`, naming this package as the recording system. They pass the same hash-recomputation check as any AER-1 receipt.
@@ -78,3 +78,16 @@ Browse the full catalog any time with `zambo_haystack.list_tools()`, or pick too
78
78
  - What is an AI agent execution receipt?: https://zambo.dev/guides/ai-agent-execution-receipt/
79
79
 
80
80
  MIT License.
81
+
82
+ ## Local AER-1 receipts (no network needed)
83
+
84
+ The package also mints AER-1 execution receipts locally, with no API calls:
85
+
86
+ ```python
87
+ from zambo_haystack import mint_receipt, verify_receipt
88
+
89
+ receipt = mint_receipt("my_tool", {"query": "hello"}, {"answer": "hi"}, ms=42)
90
+ assert verify_receipt(receipt) # recomputes the SHA-256 locally
91
+ ```
92
+
93
+ Local receipts follow the AER-1 core members (`id`, `created_at`, `tool`, `provenance_class`, `canonical_bytes`, `output_hash`) and use the provenance class `EXECUTED BY ZAMBO-HAYSTACK`, naming this package as the recording system. They pass the same hash-recomputation check as any AER-1 receipt.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "zambo-haystack"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "Verifiable execution receipts for Haystack agents. Every tool call mints a verifiable AER-1 receipt at zambo.dev."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -7,8 +7,9 @@ tier: 20 calls per tool per day.
7
7
 
8
8
  from .client import call_tool, list_tools, ZamboReceipt, ZamboResult
9
9
  from .tools import lookup_receipt, zambo_tool, zambo_tools
10
+ from .aer1 import mint_receipt, verify_receipt
10
11
 
11
- __version__ = "0.1.0"
12
+ __version__ = "0.1.3"
12
13
 
13
14
  __all__ = [
14
15
  "call_tool",
@@ -18,4 +19,6 @@ __all__ = [
18
19
  "zambo_tools",
19
20
  "ZamboReceipt",
20
21
  "ZamboResult",
22
+ "mint_receipt",
23
+ "verify_receipt",
21
24
  ]
@@ -0,0 +1,71 @@
1
+ """Local AER-1 execution receipt emitter.
2
+
3
+ Mints verifiable AER-1 execution receipts locally, with no network calls and
4
+ no dependencies beyond the Python standard library.
5
+
6
+ AER-1 (AI Agent Execution Receipt) is the verifiable execution receipt
7
+ standard created by Brennan Zambo (IETF draft-zambo-aer1-02). A receipt is a
8
+ small JSON record describing one tool call: what ran, what it was given, what
9
+ it returned, and when. The receipt carries a SHA-256 hash over its canonical
10
+ bytes, so anyone can recompute the hash and confirm the record is intact.
11
+
12
+ Receipts minted here use the provenance class ``EXECUTED BY ZAMBO-HAYSTACK``,
13
+ naming this package as the recording system, per Section 5 of the draft
14
+ ("another implementation substitutes its own system name").
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import base64
20
+ import hashlib
21
+ import json
22
+ import uuid
23
+ from datetime import datetime, timezone
24
+ from typing import Any
25
+
26
+ SCHEMA_VERSION = "0.3"
27
+ PROVENANCE_CLASS = "EXECUTED BY ZAMBO-HAYSTACK"
28
+
29
+
30
+ def _utc_now_rfc3339() -> str:
31
+ now = datetime.now(timezone.utc)
32
+ return now.strftime("%Y-%m-%dT%H:%M:%S.") + "%03dZ" % (now.microsecond // 1000)
33
+
34
+
35
+ def _canonical_bytes(tool_name: str, inputs: dict, outputs: dict, ms: int) -> bytes:
36
+ payload = {"tool": tool_name, "inputs": inputs, "outputs": outputs, "ms": ms}
37
+ return json.dumps(payload, separators=(",", ":"), ensure_ascii=True).encode("utf-8")
38
+
39
+
40
+ def mint_receipt(
41
+ tool_name: str,
42
+ inputs: Any = None,
43
+ outputs: Any = None,
44
+ ms: int = 0,
45
+ tool_version: str = "0.1.3",
46
+ scope: str = "local",
47
+ ) -> dict:
48
+ """Mint a verifiable AER-1 execution receipt for one tool call."""
49
+ inputs_obj = inputs if isinstance(inputs, dict) else {"value": inputs}
50
+ outputs_obj = outputs if isinstance(outputs, dict) else {"value": outputs}
51
+ raw = _canonical_bytes(tool_name, inputs_obj, outputs_obj, int(ms))
52
+ return {
53
+ "id": str(uuid.uuid4()),
54
+ "receipt_schema_version": SCHEMA_VERSION,
55
+ "created_at": _utc_now_rfc3339(),
56
+ "tool": {"name": tool_name, "version": tool_version, "scope": scope},
57
+ "provenance_class": PROVENANCE_CLASS,
58
+ "canonical_bytes": base64.b64encode(raw).decode("ascii"),
59
+ "output_hash": "sha256:" + hashlib.sha256(raw).hexdigest(),
60
+ "verification_status": "verified",
61
+ }
62
+
63
+
64
+ def verify_receipt(receipt: dict) -> bool:
65
+ """Check a receipt's integrity by recomputing its hash locally."""
66
+ try:
67
+ raw = base64.b64decode(receipt["canonical_bytes"])
68
+ digest = "sha256:" + hashlib.sha256(raw).hexdigest()
69
+ return digest == receipt["output_hash"]
70
+ except Exception:
71
+ return False
@@ -13,6 +13,8 @@ import urllib.error
13
13
  import urllib.request
14
14
  from dataclasses import dataclass, field
15
15
 
16
+ from .aer1 import mint_receipt, verify_receipt
17
+
16
18
  MCP_URL = "https://zambo.dev/api/mcp"
17
19
  _TIMEOUT = 60
18
20
  _MAX_ATTEMPTS = 6
@@ -33,6 +35,7 @@ class ZamboResult:
33
35
  text: str
34
36
  receipt: ZamboReceipt | None = None
35
37
  raw: dict = field(default_factory=dict)
38
+ local_receipt: dict | None = None
36
39
 
37
40
 
38
41
  def _post(payload: dict) -> dict:
@@ -62,7 +65,7 @@ def _post(payload: dict) -> dict:
62
65
  raise RuntimeError(f"Zambo API call failed after {_MAX_ATTEMPTS} attempts: {last_error}")
63
66
 
64
67
 
65
- def call_tool(tool_name: str, arguments: dict) -> ZamboResult:
68
+ def call_tool(tool_name: str, arguments: dict, emit_aer1: bool = False) -> ZamboResult:
66
69
  """Call a Zambo MCP tool and return its result plus the receipt.
67
70
 
68
71
  Every call mints a verifiable AER-1 receipt: a public URL carrying the
@@ -98,7 +101,15 @@ def call_tool(tool_name: str, arguments: dict) -> ZamboResult:
98
101
  text_parts.append(str(item["text"]))
99
102
  text = "\n".join(text_parts).strip()
100
103
 
101
- return ZamboResult(text=text, receipt=receipt, raw=result)
104
+ local_receipt = None
105
+ if emit_aer1:
106
+ local_receipt = mint_receipt(
107
+ tool_name=tool_name,
108
+ inputs={"arguments": arguments},
109
+ outputs={"text": text},
110
+ scope="local",
111
+ )
112
+ return ZamboResult(text=text, receipt=receipt, raw=result, local_receipt=local_receipt)
102
113
 
103
114
 
104
115
  def list_tools() -> list[dict]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zambo-haystack
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Verifiable execution receipts for Haystack agents. Every tool call mints a verifiable AER-1 receipt at zambo.dev.
5
5
  Author-email: Zambo <brennanzambo@zambo.dev>
6
6
  License: MIT
@@ -102,3 +102,16 @@ Browse the full catalog any time with `zambo_haystack.list_tools()`, or pick too
102
102
  - What is an AI agent execution receipt?: https://zambo.dev/guides/ai-agent-execution-receipt/
103
103
 
104
104
  MIT License.
105
+
106
+ ## Local AER-1 receipts (no network needed)
107
+
108
+ The package also mints AER-1 execution receipts locally, with no API calls:
109
+
110
+ ```python
111
+ from zambo_haystack import mint_receipt, verify_receipt
112
+
113
+ receipt = mint_receipt("my_tool", {"query": "hello"}, {"answer": "hi"}, ms=42)
114
+ assert verify_receipt(receipt) # recomputes the SHA-256 locally
115
+ ```
116
+
117
+ Local receipts follow the AER-1 core members (`id`, `created_at`, `tool`, `provenance_class`, `canonical_bytes`, `output_hash`) and use the provenance class `EXECUTED BY ZAMBO-HAYSTACK`, naming this package as the recording system. They pass the same hash-recomputation check as any AER-1 receipt.
@@ -1,7 +1,9 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
+ setup.cfg
4
5
  src/zambo_haystack/__init__.py
6
+ src/zambo_haystack/aer1.py
5
7
  src/zambo_haystack/client.py
6
8
  src/zambo_haystack/tools.py
7
9
  src/zambo_haystack.egg-info/PKG-INFO
File without changes
File without changes