hiveloop 0.1.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.
- hiveloop/__init__.py +194 -0
- hiveloop/_agent.py +1177 -0
- hiveloop/_transport.py +309 -0
- hiveloop-0.1.0.dist-info/METADATA +448 -0
- hiveloop-0.1.0.dist-info/RECORD +7 -0
- hiveloop-0.1.0.dist-info/WHEEL +5 -0
- hiveloop-0.1.0.dist-info/top_level.txt +1 -0
hiveloop/__init__.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""HiveLoop SDK — framework-agnostic agent instrumentation.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
import hiveloop
|
|
5
|
+
|
|
6
|
+
hb = hiveloop.init(api_key="hb_live_...", environment="production")
|
|
7
|
+
agent = hb.agent("my-agent", type="sales", version="1.0.0")
|
|
8
|
+
|
|
9
|
+
with agent.task("task-123", project="sales-pipeline") as task:
|
|
10
|
+
task.llm_call("reason", "claude-sonnet-4-20250514", tokens_in=1200, tokens_out=350, cost=0.008)
|
|
11
|
+
task.plan("Process lead", ["Score", "Enrich", "Route"])
|
|
12
|
+
task.plan_step(0, "completed", "Scored lead 42", turns=2, tokens=3200)
|
|
13
|
+
|
|
14
|
+
hiveloop.shutdown()
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Any, Callable
|
|
21
|
+
|
|
22
|
+
from ._agent import Agent, Task, HiveLoopError, SDK_VERSION
|
|
23
|
+
from ._transport import Transport
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"init",
|
|
27
|
+
"shutdown",
|
|
28
|
+
"reset",
|
|
29
|
+
"flush",
|
|
30
|
+
"HiveBoard",
|
|
31
|
+
"Agent",
|
|
32
|
+
"Task",
|
|
33
|
+
"HiveLoopError",
|
|
34
|
+
"SDK_VERSION",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger("hiveloop")
|
|
38
|
+
|
|
39
|
+
# Module-level singleton
|
|
40
|
+
_instance: HiveBoard | None = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class HiveBoard:
|
|
44
|
+
"""HiveLoop client — manages transport, agents, and global config."""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
api_key: str,
|
|
49
|
+
endpoint: str = "https://api.hiveboard.io",
|
|
50
|
+
environment: str = "production",
|
|
51
|
+
group: str = "default",
|
|
52
|
+
flush_interval: float = 5.0,
|
|
53
|
+
batch_size: int = 100,
|
|
54
|
+
max_queue_size: int = 10_000,
|
|
55
|
+
debug: bool = False,
|
|
56
|
+
) -> None:
|
|
57
|
+
self._api_key = api_key
|
|
58
|
+
self._endpoint = endpoint
|
|
59
|
+
self._environment = environment
|
|
60
|
+
self._group = group
|
|
61
|
+
self._debug = debug
|
|
62
|
+
|
|
63
|
+
if debug:
|
|
64
|
+
logging.getLogger("hiveloop").setLevel(logging.DEBUG)
|
|
65
|
+
|
|
66
|
+
self._transport = Transport(
|
|
67
|
+
endpoint=endpoint,
|
|
68
|
+
api_key=api_key,
|
|
69
|
+
flush_interval=flush_interval,
|
|
70
|
+
batch_size=batch_size,
|
|
71
|
+
max_queue_size=max_queue_size,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Agent registry
|
|
75
|
+
self._agents: dict[str, Agent] = {}
|
|
76
|
+
|
|
77
|
+
def agent(
|
|
78
|
+
self,
|
|
79
|
+
agent_id: str,
|
|
80
|
+
type: str = "general",
|
|
81
|
+
version: str | None = None,
|
|
82
|
+
framework: str = "custom",
|
|
83
|
+
heartbeat_interval: float = 30.0,
|
|
84
|
+
stuck_threshold: int = 300,
|
|
85
|
+
heartbeat_payload: Callable[[], dict[str, Any] | None] | None = None,
|
|
86
|
+
queue_provider: Callable[[], dict[str, Any] | None] | None = None,
|
|
87
|
+
) -> Agent:
|
|
88
|
+
"""Create or retrieve an agent.
|
|
89
|
+
|
|
90
|
+
Idempotent: same agent_id returns existing instance (updates metadata).
|
|
91
|
+
"""
|
|
92
|
+
if agent_id in self._agents:
|
|
93
|
+
existing = self._agents[agent_id]
|
|
94
|
+
# Update metadata if different
|
|
95
|
+
existing.agent_type = type
|
|
96
|
+
existing.version = version
|
|
97
|
+
existing.framework = framework
|
|
98
|
+
existing._heartbeat_payload_cb = heartbeat_payload
|
|
99
|
+
existing._queue_provider_cb = queue_provider
|
|
100
|
+
return existing
|
|
101
|
+
|
|
102
|
+
ag = Agent(
|
|
103
|
+
agent_id=agent_id,
|
|
104
|
+
transport=self._transport,
|
|
105
|
+
agent_type=type,
|
|
106
|
+
version=version,
|
|
107
|
+
framework=framework,
|
|
108
|
+
heartbeat_interval=heartbeat_interval,
|
|
109
|
+
stuck_threshold=stuck_threshold,
|
|
110
|
+
heartbeat_payload=heartbeat_payload,
|
|
111
|
+
queue_provider=queue_provider,
|
|
112
|
+
environment=self._environment,
|
|
113
|
+
group=self._group,
|
|
114
|
+
)
|
|
115
|
+
self._agents[agent_id] = ag
|
|
116
|
+
ag._register()
|
|
117
|
+
return ag
|
|
118
|
+
|
|
119
|
+
def get_agent(self, agent_id: str) -> Agent | None:
|
|
120
|
+
"""Look up a registered agent by ID."""
|
|
121
|
+
return self._agents.get(agent_id)
|
|
122
|
+
|
|
123
|
+
def flush(self) -> None:
|
|
124
|
+
"""Trigger an immediate flush of all queued events."""
|
|
125
|
+
self._transport.flush()
|
|
126
|
+
|
|
127
|
+
def shutdown(self, timeout: float = 5.0) -> None:
|
|
128
|
+
"""Shut down all agents and transport."""
|
|
129
|
+
for ag in self._agents.values():
|
|
130
|
+
ag._stop_heartbeat()
|
|
131
|
+
self._transport.shutdown(timeout=timeout)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def init(
|
|
135
|
+
api_key: str,
|
|
136
|
+
environment: str = "production",
|
|
137
|
+
group: str = "default",
|
|
138
|
+
endpoint: str = "https://api.hiveboard.io",
|
|
139
|
+
flush_interval: float = 5.0,
|
|
140
|
+
batch_size: int = 100,
|
|
141
|
+
max_queue_size: int = 10_000,
|
|
142
|
+
debug: bool = False,
|
|
143
|
+
) -> HiveBoard:
|
|
144
|
+
"""Initialize the HiveLoop SDK singleton.
|
|
145
|
+
|
|
146
|
+
Validates api_key starts with 'hb_'. Subsequent calls log a warning
|
|
147
|
+
and return the existing instance.
|
|
148
|
+
"""
|
|
149
|
+
global _instance
|
|
150
|
+
|
|
151
|
+
if not api_key.startswith("hb_"):
|
|
152
|
+
raise HiveLoopError(
|
|
153
|
+
f"Invalid API key format: must start with 'hb_' (got '{api_key[:10]}...')"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if _instance is not None:
|
|
157
|
+
logger.warning(
|
|
158
|
+
"hiveloop.init() called again — returning existing instance. "
|
|
159
|
+
"Call hiveloop.reset() first to reinitialize."
|
|
160
|
+
)
|
|
161
|
+
return _instance
|
|
162
|
+
|
|
163
|
+
_instance = HiveBoard(
|
|
164
|
+
api_key=api_key,
|
|
165
|
+
endpoint=endpoint,
|
|
166
|
+
environment=environment,
|
|
167
|
+
group=group,
|
|
168
|
+
flush_interval=flush_interval,
|
|
169
|
+
batch_size=batch_size,
|
|
170
|
+
max_queue_size=max_queue_size,
|
|
171
|
+
debug=debug,
|
|
172
|
+
)
|
|
173
|
+
return _instance
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def shutdown(timeout: float = 5.0) -> None:
|
|
177
|
+
"""Shut down the HiveLoop SDK."""
|
|
178
|
+
global _instance
|
|
179
|
+
if _instance is not None:
|
|
180
|
+
_instance.shutdown(timeout=timeout)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def reset() -> None:
|
|
184
|
+
"""Shut down and clear the singleton. Allows re-initialization."""
|
|
185
|
+
global _instance
|
|
186
|
+
if _instance is not None:
|
|
187
|
+
_instance.shutdown(timeout=5.0)
|
|
188
|
+
_instance = None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def flush() -> None:
|
|
192
|
+
"""Flush all queued events immediately."""
|
|
193
|
+
if _instance is not None:
|
|
194
|
+
_instance.flush()
|