hb-eval-sdk 2.0.0__py3-none-any.whl

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,24 @@
1
+ from .client import HBEvalClient
2
+ from .langchain_integration import HBEvalCallback
3
+ from .models import EvaluationResult, MemoryMatch
4
+ from .exceptions import (
5
+ HBEvalError,
6
+ ConnectionError,
7
+ AuthenticationError,
8
+ EncryptionError,
9
+ SafeHaltError,
10
+ )
11
+
12
+ __version__ = "2.0.0"
13
+
14
+ __all__ = [
15
+ "HBEvalClient",
16
+ "HBEvalCallback",
17
+ "EvaluationResult",
18
+ "MemoryMatch",
19
+ "HBEvalError",
20
+ "ConnectionError",
21
+ "AuthenticationError",
22
+ "EncryptionError",
23
+ "SafeHaltError",
24
+ ]
hb_eval_sdk/client.py ADDED
@@ -0,0 +1,112 @@
1
+ import json
2
+ import time
3
+ import hashlib
4
+ import base64
5
+ import requests
6
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
7
+ from .exceptions import ConnectionError, AuthenticationError, EncryptionError, SafeHaltError
8
+ from .models import EvaluationResult, MemoryMatch, Metrics, Verdict
9
+ from .utils import generate_nonce, generate_timestamp, compute_hmac
10
+ from . import config
11
+
12
+
13
+ class HBEvalClient:
14
+ def __init__(
15
+ self,
16
+ api_key: str,
17
+ aes_key: str,
18
+ gateway_url: str = None,
19
+ timeout: int = None,
20
+ max_retries: int = None,
21
+ ):
22
+ self.api_key = api_key
23
+ self.timeout = timeout or config.DEFAULT_TIMEOUT
24
+ self.max_retries = max_retries or config.DEFAULT_MAX_RETRIES
25
+ self.gateway_url = (gateway_url or config.DEFAULT_GATEWAY_URL).rstrip("/")
26
+ try:
27
+ self.aes_key = base64.b64decode(aes_key)
28
+ if len(self.aes_key) != 32:
29
+ raise ValueError("AES key must be 32 bytes after base64 decoding.")
30
+ except Exception as e:
31
+ raise EncryptionError(f"Invalid AES key: {e}")
32
+ self.hmac_secret = hashlib.sha256(self.api_key.encode()).digest()
33
+
34
+ def _encrypt(self, payload: dict):
35
+ nonce_hex = generate_nonce(12)
36
+ nonce_bytes = nonce_hex.encode("utf-8")
37
+ aesgcm = AESGCM(self.aes_key)
38
+ plaintext = json.dumps(payload).encode("utf-8")
39
+ ciphertext = aesgcm.encrypt(nonce_bytes, plaintext, None)
40
+ return nonce_hex, ciphertext.hex()
41
+
42
+ def _build_headers(self, nonce: str, timestamp: str) -> dict:
43
+ signature = compute_hmac(self.hmac_secret, f"{timestamp}.{nonce}")
44
+ return {
45
+ "Authorization": f"Bearer {self.api_key}",
46
+ "X-HBEval-Nonce": nonce,
47
+ "X-HBEval-Timestamp": timestamp,
48
+ "X-HBEval-Signature": signature,
49
+ "Content-Type": "application/json",
50
+ }
51
+
52
+ def evaluate(self, payload: dict, max_retries: int = None) -> EvaluationResult:
53
+ retries = max_retries if max_retries is not None else self.max_retries
54
+ for attempt in range(retries + 1):
55
+ try:
56
+ nonce, ciphertext = self._encrypt(payload)
57
+ timestamp = generate_timestamp()
58
+ headers = self._build_headers(nonce, timestamp)
59
+ response = requests.post(
60
+ f"{self.gateway_url}/evaluate",
61
+ json={"ciphertext": ciphertext},
62
+ headers=headers,
63
+ timeout=self.timeout,
64
+ )
65
+ if response.status_code == 401:
66
+ raise AuthenticationError("Invalid API key or signature.")
67
+ if response.status_code == 403:
68
+ raise AuthenticationError("Replay attack detected or request expired.")
69
+ if response.status_code != 200:
70
+ raise ConnectionError(
71
+ f"Gateway error {response.status_code}: {response.text}"
72
+ )
73
+ data = response.json()
74
+ metrics_data = data.get("metrics", {"pei": 0.0, "irs": 0.0})
75
+ return EvaluationResult(
76
+ verdict=Verdict(data.get("verdict", "UNSAFE")),
77
+ metrics=Metrics(**metrics_data),
78
+ safe_halt=data.get("safe_halt", False),
79
+ reason=data.get("reason", None),
80
+ )
81
+ except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
82
+ if attempt < retries:
83
+ time.sleep(1 * (attempt + 1))
84
+ continue
85
+ raise SafeHaltError(
86
+ f"Connection lost after {retries + 1} attempt(s): {e}"
87
+ )
88
+ except (AuthenticationError, EncryptionError):
89
+ raise
90
+ except Exception as e:
91
+ raise ConnectionError(f"Unexpected error during evaluation: {e}")
92
+
93
+ def retrieve_memory(self, context: str, project_id: str = None) -> list:
94
+ if project_id is None:
95
+ project_id = config.DEFAULT_PROJECT_ID
96
+ nonce, ciphertext = self._encrypt(
97
+ {"context": context, "project_id": project_id}
98
+ )
99
+ timestamp = generate_timestamp()
100
+ headers = self._build_headers(nonce, timestamp)
101
+ response = requests.post(
102
+ f"{self.gateway_url}/api/v1/memory/retrieve",
103
+ json={"ciphertext": ciphertext},
104
+ headers=headers,
105
+ timeout=self.timeout,
106
+ )
107
+ if response.status_code != 200:
108
+ raise ConnectionError(
109
+ f"Memory retrieval failed with status {response.status_code}: {response.text}"
110
+ )
111
+ data = response.json()
112
+ return [MemoryMatch(**m) for m in data.get("matches", [])]
hb_eval_sdk/config.py ADDED
@@ -0,0 +1,4 @@
1
+ DEFAULT_GATEWAY_URL = "https://hbeval-reliability-os-production.up.railway.app"
2
+ DEFAULT_TIMEOUT = 10
3
+ DEFAULT_MAX_RETRIES = 2
4
+ DEFAULT_PROJECT_ID = "00000000-0000-0000-0000-000000000001"
@@ -0,0 +1,26 @@
1
+ class HBEvalError(Exception):
2
+ """Base exception for HB-Eval SDK."""
3
+ pass
4
+
5
+
6
+ class ConnectionError(HBEvalError):
7
+ """Raised when connection to Gateway fails."""
8
+ pass
9
+
10
+
11
+ class AuthenticationError(HBEvalError):
12
+ """Raised when API key or signature is invalid."""
13
+ pass
14
+
15
+
16
+ class EncryptionError(HBEvalError):
17
+ """Raised when encryption/decryption fails."""
18
+ pass
19
+
20
+
21
+ class SafeHaltError(HBEvalError):
22
+ """Raised when Safe Halt protocol is activated."""
23
+
24
+ def __init__(self, message: str, reason: str = "CONNECTION_LOST_MANDATORY_HALT"):
25
+ super().__init__(message)
26
+ self.reason = reason
@@ -0,0 +1,76 @@
1
+ import time
2
+ from typing import Dict, Any, List, Optional
3
+
4
+ from langchain_core.callbacks.base import BaseCallbackHandler
5
+
6
+ from .client import HBEvalClient
7
+
8
+
9
+ class HBEvalCallback(BaseCallbackHandler):
10
+ """LangChain callback handler that evaluates agent trajectories via HB-Eval."""
11
+
12
+ def __init__(
13
+ self,
14
+ api_key: str,
15
+ aes_key: str,
16
+ gateway_url: Optional[str] = None,
17
+ ):
18
+ super().__init__()
19
+ self.client = HBEvalClient(api_key, aes_key, gateway_url)
20
+ self.trajectory: List[Dict[str, Any]] = []
21
+ self.start_time: float = 0.0
22
+
23
+ def on_chain_start(
24
+ self,
25
+ serialized: Dict[str, Any],
26
+ inputs: Dict[str, Any],
27
+ **kwargs: Any,
28
+ ) -> None:
29
+ self.trajectory = []
30
+ self.start_time = time.time()
31
+ self.trajectory.append(
32
+ {
33
+ "step": 1,
34
+ "action": "chain_start",
35
+ "input": str(inputs)[:500],
36
+ }
37
+ )
38
+
39
+ def on_tool_start(
40
+ self,
41
+ serialized: Dict[str, Any],
42
+ input_str: str,
43
+ **kwargs: Any,
44
+ ) -> None:
45
+ self.trajectory.append(
46
+ {
47
+ "step": len(self.trajectory) + 1,
48
+ "action": "tool_call",
49
+ "tool": serialized.get("name", "unknown"),
50
+ "input": input_str[:500],
51
+ }
52
+ )
53
+
54
+ def on_tool_end(self, output: str, **kwargs: Any) -> None:
55
+ if self.trajectory and self.trajectory[-1].get("action") == "tool_call":
56
+ self.trajectory[-1]["output"] = output[:500]
57
+ self.trajectory[-1]["status"] = "success"
58
+
59
+ def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> None:
60
+ self.trajectory.append(
61
+ {
62
+ "step": len(self.trajectory) + 1,
63
+ "action": "chain_end",
64
+ "output": str(outputs)[:500],
65
+ }
66
+ )
67
+ self.client.evaluate(
68
+ {
69
+ "trajectory": self.trajectory,
70
+ "sub_tasks": len(self.trajectory),
71
+ "constraint_violations": 0,
72
+ "recovery_attempts": 1,
73
+ "context": "LangChain agent execution",
74
+ "agent_id": "langchain-agent",
75
+ }
76
+ )
hb_eval_sdk/models.py ADDED
@@ -0,0 +1,28 @@
1
+ from pydantic import BaseModel
2
+ from typing import Optional
3
+ from enum import Enum
4
+
5
+
6
+ class Verdict(str, Enum):
7
+ SAFE = "SAFE"
8
+ UNSAFE = "UNSAFE"
9
+
10
+
11
+ class Metrics(BaseModel):
12
+ pei: float
13
+ irs: float
14
+
15
+
16
+ class EvaluationResult(BaseModel):
17
+ verdict: Verdict
18
+ metrics: Metrics
19
+ safe_halt: Optional[bool] = False
20
+ reason: Optional[str] = None
21
+
22
+
23
+ class MemoryMatch(BaseModel):
24
+ id: str
25
+ trajectory_summary: str
26
+ pei_score: float
27
+ irs_score: float
28
+ similarity: float
hb_eval_sdk/utils.py ADDED
@@ -0,0 +1,19 @@
1
+ import secrets
2
+ import time
3
+ import hmac
4
+ import hashlib
5
+
6
+
7
+ def generate_nonce(length: int = 12) -> str:
8
+ """Generate cryptographically secure nonce as hex string."""
9
+ return secrets.token_hex(length)
10
+
11
+
12
+ def generate_timestamp() -> str:
13
+ """Return current UTC timestamp as string."""
14
+ return str(int(time.time()))
15
+
16
+
17
+ def compute_hmac(secret_key: bytes, message: str) -> str:
18
+ """Compute HMAC-SHA256 signature."""
19
+ return hmac.new(secret_key, message.encode(), hashlib.sha256).hexdigest()
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: hb-eval-sdk
3
+ Version: 2.0.0
4
+ Summary: HB-Eval SDK for reliable agent evaluation, semantic memory, and LangChain/LangGraph integration
5
+ Author-email: Abuelgasim Mohamed Ibrahim Adam <abuelgasim.hbeval@outlook.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/hb-evalSystem/HB-System
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: requests>=2.28.0
17
+ Requires-Dist: cryptography>=41.0.0
18
+ Requires-Dist: pydantic>=2.0.0
19
+ Requires-Dist: langchain-core>=0.1.0
20
+ Requires-Dist: langgraph>=0.0.10
21
+ Dynamic: license-file
22
+
23
+ # HB-Eval SDK
24
+
25
+ Secure Python SDK for **HB-Eval Reliability OS** — the first cognitive runtime for evaluating and ensuring AI agent reliability.
26
+
27
+ ## Features
28
+
29
+ - AES-256-GCM encrypted payloads for all API communication
30
+ - HMAC-SHA256 request signing with replay-attack protection
31
+ - Safe Halt protocol — automatic failure handling on connection loss
32
+ - Semantic memory retrieval for agent context
33
+ - Native LangChain / LangGraph callback integration
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install hb-eval-sdk
39
+ ```
40
+
41
+ ## Quick Start
42
+
43
+ ```python
44
+ from hb_eval_sdk import HBEvalClient
45
+
46
+ client = HBEvalClient(
47
+ api_key="your_api_key",
48
+ aes_key="your_base64_encoded_32byte_aes_key",
49
+ )
50
+
51
+ result = client.evaluate({
52
+ "trajectory": [{"step": 1, "action": "query_memory", "input": "..."}],
53
+ "sub_tasks": 1,
54
+ "constraint_violations": 0,
55
+ "recovery_attempts": 1,
56
+ "context": "my agent task",
57
+ "agent_id": "my-agent",
58
+ })
59
+
60
+ print(result.verdict) # Verdict.SAFE or Verdict.UNSAFE
61
+ print(result.metrics.pei) # float
62
+ print(result.metrics.irs) # float
63
+ ```
64
+
65
+ ## Memory Retrieval
66
+
67
+ ```python
68
+ matches = client.retrieve_memory(context="my task context")
69
+ for match in matches:
70
+ print(match.trajectory_summary, match.similarity)
71
+ ```
72
+
73
+ ## LangChain Integration
74
+
75
+ ```python
76
+ from hb_eval_sdk import HBEvalCallback
77
+
78
+ callback = HBEvalCallback(
79
+ api_key="your_api_key",
80
+ aes_key="your_base64_encoded_32byte_aes_key",
81
+ )
82
+
83
+ # Pass the callback to your LangChain agent
84
+ agent.invoke({"input": "..."}, config={"callbacks": [callback]})
85
+ ```
86
+
87
+ ## Exception Handling
88
+
89
+ ```python
90
+ from hb_eval_sdk import (
91
+ HBEvalError,
92
+ AuthenticationError,
93
+ EncryptionError,
94
+ SafeHaltError,
95
+ ConnectionError,
96
+ )
97
+
98
+ try:
99
+ result = client.evaluate({...})
100
+ except SafeHaltError as e:
101
+ print(f"Safe halt triggered: {e.reason}")
102
+ except AuthenticationError as e:
103
+ print(f"Auth failed: {e}")
104
+ except ConnectionError as e:
105
+ print(f"Connection error: {e}")
106
+ ```
107
+
108
+ ## Requirements
109
+
110
+ - Python >= 3.8
111
+ - requests >= 2.28.0
112
+ - cryptography >= 41.0.0
113
+ - pydantic >= 2.0.0
114
+ - langchain-core >= 0.1.0
115
+ - langgraph >= 0.0.10
116
+
117
+ ## Documentation
118
+
119
+ Full documentation: https://hb-eval.readthedocs.io
120
+
121
+ ## License
122
+
123
+ MIT License — Copyright (c) 2026 Abuelgasim Mohamed Ibrahim Adam
@@ -0,0 +1,12 @@
1
+ hb_eval_sdk/__init__.py,sha256=WJSxoYtdayN829Ty4tBHU09KGgE-rw_6WSwlkexIDP8,500
2
+ hb_eval_sdk/client.py,sha256=I5BrCXRhaiqJAqa9ZTsI_OMNCfiW628PCvDF_hm9UTM,4813
3
+ hb_eval_sdk/config.py,sha256=1uOn2P4tsVZGdwhdHb_gnvGxMhX_XX0QW457g2jgQ_w,185
4
+ hb_eval_sdk/exceptions.py,sha256=1dwAQqsaKayCoA2SMKlwbbVOB7Z6N3WoaujIUue8aPM,624
5
+ hb_eval_sdk/langchain_integration.py,sha256=ZczMaukfWPPfRnpebEqu_EX7F5f8TbtxDS6e_faEfFI,2272
6
+ hb_eval_sdk/models.py,sha256=9Mn_E4c9wDj7hkCLUyKzGmCUJzApcQAQoZYOS8--rrM,493
7
+ hb_eval_sdk/utils.py,sha256=_FccCJUcnHQC9s8KrmS-Fd3VTZIMENjDvrpFQxJtfnY,500
8
+ hb_eval_sdk-2.0.0.dist-info/licenses/LICENSE,sha256=ZfCDUPOLy6lA-nBeRx13WXqPEpKbOMXaS-vtwLT2ASM,1088
9
+ hb_eval_sdk-2.0.0.dist-info/METADATA,sha256=sx0_R1Gw7rBWCZ1b62bbsW8ndJG0dgAOkixvKFb2G3o,3210
10
+ hb_eval_sdk-2.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
11
+ hb_eval_sdk-2.0.0.dist-info/top_level.txt,sha256=U33w8Kxg0hBdbytquGTlTe98T15Qep4PPbVQYkbXciQ,12
12
+ hb_eval_sdk-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abuelgasim Mohamed Ibrahim Adam
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 @@
1
+ hb_eval_sdk