sannex-agent 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.
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.5
2
+ Name: sannex-agent
3
+ Version: 0.1.0
4
+ Summary: Official Python Telemetry & Monitoring Client for Sannex AI Operations
5
+ Project-URL: Homepage, https://sannex.ng
6
+ Project-URL: Repository, https://github.com/sannex/sannex-agent
7
+ Author-email: Sannex Tech LTD <info@sannex.ng>
8
+ License: MIT
9
+ Requires-Python: >=3.9
10
+ Requires-Dist: httpx>=0.24.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # sannex-agent (Python SDK)
14
+
15
+ Official, ultra-lightweight Python client for Sannex AI Operations, telemetry streaming, and dynamic remote configuration sync.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install sannex-agent
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```python
26
+ from sannex_agent import SannexClient
27
+
28
+ # Initialize client
29
+ client = SannexClient(api_key="your_sannex_api_key")
30
+
31
+ # Track customer interaction / transaction asynchronously
32
+ client.track(
33
+ channel="whatsapp",
34
+ customer_id="+2348012345678",
35
+ event="payment_success",
36
+ amount=25000.0,
37
+ metadata={"order_id": "ORD-123456", "gateway": "paystack"}
38
+ )
39
+
40
+ # Fetch dynamic system prompts and knowledge base configs
41
+ config = client.get_config()
42
+ print(config)
43
+
44
+ # Graceful shutdown
45
+ client.close()
46
+ ```
47
+
48
+ ## License
49
+ MIT
@@ -0,0 +1,37 @@
1
+ # sannex-agent (Python SDK)
2
+
3
+ Official, ultra-lightweight Python client for Sannex AI Operations, telemetry streaming, and dynamic remote configuration sync.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install sannex-agent
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from sannex_agent import SannexClient
15
+
16
+ # Initialize client
17
+ client = SannexClient(api_key="your_sannex_api_key")
18
+
19
+ # Track customer interaction / transaction asynchronously
20
+ client.track(
21
+ channel="whatsapp",
22
+ customer_id="+2348012345678",
23
+ event="payment_success",
24
+ amount=25000.0,
25
+ metadata={"order_id": "ORD-123456", "gateway": "paystack"}
26
+ )
27
+
28
+ # Fetch dynamic system prompts and knowledge base configs
29
+ config = client.get_config()
30
+ print(config)
31
+
32
+ # Graceful shutdown
33
+ client.close()
34
+ ```
35
+
36
+ ## License
37
+ MIT
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "sannex-agent"
7
+ version = "0.1.0"
8
+ description = "Official Python Telemetry & Monitoring Client for Sannex AI Operations"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Sannex Tech LTD", email = "info@sannex.ng" }
14
+ ]
15
+ dependencies = [
16
+ "httpx>=0.24.0",
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://sannex.ng"
21
+ Repository = "https://github.com/sannex/sannex-agent"
@@ -0,0 +1,3 @@
1
+ from .client import SannexClient
2
+
3
+ __all__ = ["SannexClient"]
@@ -0,0 +1,119 @@
1
+ import queue
2
+ import threading
3
+ import time
4
+ from typing import Optional, Dict, Any, List
5
+ import httpx
6
+
7
+
8
+ class SannexClient:
9
+ """Official Python Telemetry & Remote Config Client for Sannex AI Operations."""
10
+
11
+ def __init__(
12
+ self,
13
+ api_key: str,
14
+ host: str = "https://api.sannex.ng",
15
+ flush_interval: float = 1.0,
16
+ max_queue_size: int = 1000,
17
+ ):
18
+ if not api_key:
19
+ raise ValueError("api_key must not be empty.")
20
+ self.api_key = api_key
21
+ self.host = host.rstrip("/")
22
+ self.endpoint = f"{self.host}/v1/events"
23
+ self.config_endpoint = f"{self.host}/v1/tenants/config"
24
+ self.flush_interval = flush_interval
25
+ self._queue: queue.Queue = queue.Queue(maxsize=max_queue_size)
26
+ self._running = True
27
+ self._client = httpx.Client(timeout=5.0)
28
+
29
+ self._worker_thread = threading.Thread(target=self._worker, daemon=True)
30
+ self._worker_thread.start()
31
+
32
+ def track(
33
+ self,
34
+ channel: str,
35
+ customer_id: str,
36
+ event: str,
37
+ status: str = "success",
38
+ amount: float = 0.0,
39
+ metadata: Optional[Dict[str, Any]] = None,
40
+ ) -> None:
41
+ """Enqueue an event to be dispatched asynchronously without blocking the caller."""
42
+ payload = {
43
+ "channel": channel,
44
+ "customer_id": str(customer_id),
45
+ "event": event,
46
+ "status": status,
47
+ "amount": float(amount),
48
+ "metadata": metadata or {},
49
+ "timestamp": time.time(),
50
+ }
51
+ try:
52
+ self._queue.put_nowait(payload)
53
+ except queue.Full:
54
+ pass # Drop telemetry safely rather than degrading host performance
55
+
56
+ def get_config(self) -> Dict[str, Any]:
57
+ """Fetches dynamic system prompts, LLM parameters, catalogs, and knowledge base documents."""
58
+ try:
59
+ res = self._client.get(
60
+ self.config_endpoint,
61
+ headers={"Authorization": f"Bearer {self.api_key}"},
62
+ )
63
+ if res.status_code == 200:
64
+ return res.json()
65
+ except Exception:
66
+ pass
67
+ return {"status": "error", "config": {}, "knowledge_docs": [], "catalog_items": []}
68
+
69
+ def _worker(self) -> None:
70
+ while self._running:
71
+ events: List[Dict[str, Any]] = []
72
+ while not self._queue.empty() and len(events) < 50:
73
+ try:
74
+ events.append(self._queue.get_nowait())
75
+ except queue.Empty:
76
+ break
77
+
78
+ if events:
79
+ self._flush(events)
80
+ time.sleep(self.flush_interval)
81
+
82
+ def _flush(self, events: List[Dict[str, Any]]) -> None:
83
+ try:
84
+ self._client.post(
85
+ self.endpoint,
86
+ json={"batch": events},
87
+ headers={
88
+ "Authorization": f"Bearer {self.api_key}",
89
+ "Content-Type": "application/json",
90
+ },
91
+ )
92
+ except Exception:
93
+ pass # Swallowed silently so it never breaks host server execution
94
+
95
+ def flush(self) -> None:
96
+ """Manually flushes all currently queued events immediately."""
97
+ events: List[Dict[str, Any]] = []
98
+ while not self._queue.empty():
99
+ try:
100
+ events.append(self._queue.get_nowait())
101
+ except queue.Empty:
102
+ break
103
+ if events:
104
+ self._flush(events)
105
+
106
+ def close(self) -> None:
107
+ self._running = False
108
+ self.flush()
109
+ try:
110
+ self._worker_thread.join(timeout=2.0)
111
+ self._client.close()
112
+ except Exception:
113
+ pass
114
+
115
+ def __enter__(self):
116
+ return self
117
+
118
+ def __exit__(self, exc_type, exc_val, exc_tb):
119
+ self.close()
@@ -0,0 +1 @@
1
+ # Marker file for PEP 561