hebbrix 2.0.1__tar.gz → 2.0.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.
- {hebbrix-2.0.1 → hebbrix-2.0.3}/PKG-INFO +1 -1
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix/__init__.py +1 -1
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix/chat.py +48 -15
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix/client.py +18 -23
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix/resources.py +75 -10
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix.egg-info/PKG-INFO +1 -1
- {hebbrix-2.0.1 → hebbrix-2.0.3}/pyproject.toml +1 -1
- {hebbrix-2.0.1 → hebbrix-2.0.3}/setup.py +1 -1
- {hebbrix-2.0.1 → hebbrix-2.0.3}/MANIFEST.in +0 -0
- {hebbrix-2.0.1 → hebbrix-2.0.3}/README.md +0 -0
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix/exceptions.py +0 -0
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix.egg-info/SOURCES.txt +0 -0
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix.egg-info/dependency_links.txt +0 -0
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix.egg-info/requires.txt +0 -0
- {hebbrix-2.0.1 → hebbrix-2.0.3}/hebbrix.egg-info/top_level.txt +0 -0
- {hebbrix-2.0.1 → hebbrix-2.0.3}/setup.cfg +0 -0
|
@@ -9,9 +9,10 @@ Example:
|
|
|
9
9
|
chat = MemoryChat(api_key="mem_sk_...")
|
|
10
10
|
response = chat.send("What's my favorite food?", session_id="user_123")
|
|
11
11
|
"""
|
|
12
|
-
from typing import Optional, Dict, Any, List
|
|
13
|
-
import requests
|
|
14
12
|
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
import requests
|
|
15
16
|
from hebbrix.client import MemoryClient
|
|
16
17
|
|
|
17
18
|
|
|
@@ -53,6 +54,7 @@ class MemoryChat:
|
|
|
53
54
|
learning: Enable RL trajectory logging (default: True)
|
|
54
55
|
prompt_strategy: Prompt template strategy (default: "default")
|
|
55
56
|
memory_limit: Max memories to retrieve per message (default: 10)
|
|
57
|
+
source: Collection source ("app" or "api") for data isolation. Sent as X-Hebbrix-Source header.
|
|
56
58
|
"""
|
|
57
59
|
|
|
58
60
|
def __init__(
|
|
@@ -66,12 +68,14 @@ class MemoryChat:
|
|
|
66
68
|
learning: bool = True,
|
|
67
69
|
prompt_strategy: str = "default",
|
|
68
70
|
memory_limit: int = 10,
|
|
71
|
+
source: Optional[str] = None,
|
|
69
72
|
):
|
|
70
73
|
self.api_key = api_key
|
|
71
74
|
self.base_url = base_url.rstrip("/")
|
|
72
75
|
self.model = model
|
|
73
76
|
self.prompt_strategy = prompt_strategy
|
|
74
77
|
self.memory_limit = memory_limit
|
|
78
|
+
self.source = source
|
|
75
79
|
|
|
76
80
|
# Feature configuration
|
|
77
81
|
self.features = {
|
|
@@ -82,7 +86,7 @@ class MemoryChat:
|
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
# Also create full client for advanced operations
|
|
85
|
-
self.client = MemoryClient(api_key=api_key, base_url=base_url)
|
|
89
|
+
self.client = MemoryClient(api_key=api_key, base_url=base_url, source=source)
|
|
86
90
|
|
|
87
91
|
def send(
|
|
88
92
|
self,
|
|
@@ -127,6 +131,8 @@ class MemoryChat:
|
|
|
127
131
|
"Authorization": f"Bearer {self.api_key}",
|
|
128
132
|
"Content-Type": "application/json",
|
|
129
133
|
}
|
|
134
|
+
if self.source:
|
|
135
|
+
headers["X-Hebbrix-Source"] = self.source
|
|
130
136
|
|
|
131
137
|
response = requests.post(url, json=payload, headers=headers)
|
|
132
138
|
response.raise_for_status()
|
|
@@ -179,6 +185,8 @@ class MemoryChat:
|
|
|
179
185
|
"Authorization": f"Bearer {self.api_key}",
|
|
180
186
|
"Content-Type": "application/json",
|
|
181
187
|
}
|
|
188
|
+
if self.source:
|
|
189
|
+
headers["X-Hebbrix-Source"] = self.source
|
|
182
190
|
|
|
183
191
|
response = requests.post(url, json=payload, headers=headers)
|
|
184
192
|
response.raise_for_status()
|
|
@@ -286,7 +294,11 @@ class MemoryChat:
|
|
|
286
294
|
response.raise_for_status()
|
|
287
295
|
|
|
288
296
|
# Convenience methods for memory operations
|
|
289
|
-
|
|
297
|
+
# These use synchronous requests.post() (same as send/send_with_context)
|
|
298
|
+
# instead of the async MemoryClient to avoid coroutine issues.
|
|
299
|
+
def add_memory(
|
|
300
|
+
self, content: str, collection_id: str, importance: float = 0.5
|
|
301
|
+
) -> Dict[str, Any]:
|
|
290
302
|
"""
|
|
291
303
|
Add a memory manually.
|
|
292
304
|
|
|
@@ -298,13 +310,24 @@ class MemoryChat:
|
|
|
298
310
|
Returns:
|
|
299
311
|
Created memory dict
|
|
300
312
|
"""
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
313
|
+
url = f"{self.base_url}/v1/memories"
|
|
314
|
+
headers = {
|
|
315
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
316
|
+
"Content-Type": "application/json",
|
|
317
|
+
}
|
|
318
|
+
payload = {
|
|
319
|
+
"content": content,
|
|
320
|
+
"collection_id": collection_id,
|
|
321
|
+
"importance": importance,
|
|
322
|
+
}
|
|
306
323
|
|
|
307
|
-
|
|
324
|
+
response = requests.post(url, json=payload, headers=headers)
|
|
325
|
+
response.raise_for_status()
|
|
326
|
+
return response.json()
|
|
327
|
+
|
|
328
|
+
def search_memories(
|
|
329
|
+
self, query: str, collection_id: Optional[str] = None, limit: int = 10
|
|
330
|
+
) -> List[Dict[str, Any]]:
|
|
308
331
|
"""
|
|
309
332
|
Search memories manually.
|
|
310
333
|
|
|
@@ -316,11 +339,21 @@ class MemoryChat:
|
|
|
316
339
|
Returns:
|
|
317
340
|
List of memory dicts
|
|
318
341
|
"""
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
342
|
+
url = f"{self.base_url}/v1/search"
|
|
343
|
+
headers = {
|
|
344
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
345
|
+
"Content-Type": "application/json",
|
|
346
|
+
}
|
|
347
|
+
payload: Dict[str, Any] = {
|
|
348
|
+
"query": query,
|
|
349
|
+
"limit": limit,
|
|
350
|
+
}
|
|
351
|
+
if collection_id:
|
|
352
|
+
payload["collection_id"] = collection_id
|
|
353
|
+
|
|
354
|
+
response = requests.post(url, json=payload, headers=headers)
|
|
355
|
+
response.raise_for_status()
|
|
356
|
+
return response.json().get("results", [])
|
|
324
357
|
|
|
325
358
|
|
|
326
359
|
# Convenience function for even simpler usage
|
|
@@ -3,30 +3,19 @@ Hebbrix Client
|
|
|
3
3
|
|
|
4
4
|
Main client class for interacting with the Hebbrix api.
|
|
5
5
|
"""
|
|
6
|
-
from typing import Optional, Dict, Any, List
|
|
7
|
-
import httpx
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
from hebbrix.exceptions import (
|
|
23
|
-
HebbrixError,
|
|
24
|
-
AuthenticationError,
|
|
25
|
-
ValidationError,
|
|
26
|
-
NotFoundError,
|
|
27
|
-
RateLimitError,
|
|
28
|
-
ServerError,
|
|
29
|
-
)
|
|
7
|
+
import os
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
from hebbrix.exceptions import (AuthenticationError, HebbrixError,
|
|
12
|
+
NotFoundError, RateLimitError, ServerError,
|
|
13
|
+
ValidationError)
|
|
14
|
+
from hebbrix.resources import (AuthResource, CollectionsResource,
|
|
15
|
+
ConsolidationResource, MemoriesResource,
|
|
16
|
+
MemoryToolsResource, ProceduralResource,
|
|
17
|
+
RLResource, SearchResource, TemporalResource,
|
|
18
|
+
WorkingMemoryResource, WorldModelResource)
|
|
30
19
|
|
|
31
20
|
|
|
32
21
|
class MemoryClient:
|
|
@@ -37,6 +26,7 @@ class MemoryClient:
|
|
|
37
26
|
api_key: API key for authentication
|
|
38
27
|
base_url: Base URL of the API (default: https://api.hebbrix.com)
|
|
39
28
|
timeout: Request timeout in seconds (default: 30)
|
|
29
|
+
source: Collection source ("app" or "api") for data isolation. Falls back to HEBBRIX_SOURCE env var.
|
|
40
30
|
|
|
41
31
|
Example:
|
|
42
32
|
>>> client = MemoryClient(api_key="mem_sk_...")
|
|
@@ -51,10 +41,12 @@ class MemoryClient:
|
|
|
51
41
|
api_key: Optional[str] = None,
|
|
52
42
|
base_url: str = "https://api.hebbrix.com",
|
|
53
43
|
timeout: float = 120.0,
|
|
44
|
+
source: Optional[str] = None,
|
|
54
45
|
):
|
|
55
46
|
self.api_key = api_key
|
|
56
47
|
self.base_url = base_url.rstrip("/")
|
|
57
48
|
self.timeout = timeout
|
|
49
|
+
self.source = source or os.getenv("HEBBRIX_SOURCE")
|
|
58
50
|
|
|
59
51
|
# HTTP client
|
|
60
52
|
self._client = httpx.AsyncClient(
|
|
@@ -86,6 +78,9 @@ class MemoryClient:
|
|
|
86
78
|
if self.api_key:
|
|
87
79
|
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
88
80
|
|
|
81
|
+
if self.source:
|
|
82
|
+
headers["X-Hebbrix-Source"] = self.source
|
|
83
|
+
|
|
89
84
|
return headers
|
|
90
85
|
|
|
91
86
|
def _handle_error(self, response: httpx.Response) -> None:
|
|
@@ -3,7 +3,7 @@ API Resource classes
|
|
|
3
3
|
|
|
4
4
|
Each resource class wraps a specific set of API endpoints.
|
|
5
5
|
"""
|
|
6
|
-
from typing import Optional, Dict, Any, List, TYPE_CHECKING
|
|
6
|
+
from typing import Optional, Dict, Any, List, AsyncIterator, TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from hebbrix.client import MemoryClient
|
|
@@ -227,29 +227,94 @@ class MemoriesResource(BaseResource):
|
|
|
227
227
|
},
|
|
228
228
|
)
|
|
229
229
|
|
|
230
|
-
async def
|
|
230
|
+
async def list_page(
|
|
231
231
|
self,
|
|
232
232
|
collection_id: Optional[str] = None,
|
|
233
|
-
|
|
234
|
-
limit: int =
|
|
235
|
-
) ->
|
|
233
|
+
cursor: Optional[str] = None,
|
|
234
|
+
limit: int = 50,
|
|
235
|
+
) -> Dict[str, Any]:
|
|
236
236
|
"""
|
|
237
|
-
List memories.
|
|
237
|
+
List memories with full pagination metadata.
|
|
238
|
+
|
|
239
|
+
Returns the raw cursor-paginated response from the backend so callers
|
|
240
|
+
can iterate pages explicitly.
|
|
238
241
|
|
|
239
242
|
Args:
|
|
240
243
|
collection_id: Optional collection filter
|
|
241
|
-
|
|
242
|
-
limit:
|
|
244
|
+
cursor: Opaque cursor from a previous page (None for the first page)
|
|
245
|
+
limit: Page size (backend max: 100)
|
|
243
246
|
|
|
244
247
|
Returns:
|
|
245
|
-
|
|
248
|
+
Dict with keys:
|
|
249
|
+
- items: List[Dict[str, Any]] — memories in this page
|
|
250
|
+
- next_cursor: Optional[str] — pass to the next call to continue
|
|
251
|
+
- has_more: bool — True if more pages exist
|
|
252
|
+
- total_count: int — total number of memories matching the filter
|
|
246
253
|
"""
|
|
247
|
-
params = {"
|
|
254
|
+
params: Dict[str, Any] = {"limit": limit}
|
|
248
255
|
if collection_id:
|
|
249
256
|
params["collection_id"] = collection_id
|
|
257
|
+
if cursor:
|
|
258
|
+
params["cursor"] = cursor
|
|
250
259
|
|
|
251
260
|
return await self.client.get("/v1/memories", params=params)
|
|
252
261
|
|
|
262
|
+
async def list(
|
|
263
|
+
self,
|
|
264
|
+
collection_id: Optional[str] = None,
|
|
265
|
+
cursor: Optional[str] = None,
|
|
266
|
+
limit: int = 50,
|
|
267
|
+
) -> List[Dict[str, Any]]:
|
|
268
|
+
"""
|
|
269
|
+
List memories (single page, items only).
|
|
270
|
+
|
|
271
|
+
Back-compat convenience that drops pagination metadata. For
|
|
272
|
+
page-at-a-time access use `list_page`; for iteration across all
|
|
273
|
+
pages use `iter_all`.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
collection_id: Optional collection filter
|
|
277
|
+
cursor: Opaque cursor from a previous page
|
|
278
|
+
limit: Page size (backend max: 100)
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
List of memory dicts for the requested page
|
|
282
|
+
"""
|
|
283
|
+
page = await self.list_page(
|
|
284
|
+
collection_id=collection_id, cursor=cursor, limit=limit
|
|
285
|
+
)
|
|
286
|
+
return page.get("items", [])
|
|
287
|
+
|
|
288
|
+
async def iter_all(
|
|
289
|
+
self,
|
|
290
|
+
collection_id: Optional[str] = None,
|
|
291
|
+
limit: int = 50,
|
|
292
|
+
) -> AsyncIterator[Dict[str, Any]]:
|
|
293
|
+
"""
|
|
294
|
+
Async-iterate every memory matching the filter, following cursors.
|
|
295
|
+
|
|
296
|
+
Example:
|
|
297
|
+
async for memory in client.memories.iter_all(collection_id="col_x"):
|
|
298
|
+
print(memory["id"])
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
collection_id: Optional collection filter
|
|
302
|
+
limit: Page size used under the hood (backend max: 100)
|
|
303
|
+
|
|
304
|
+
Yields:
|
|
305
|
+
Memory dicts, one per item, across all pages
|
|
306
|
+
"""
|
|
307
|
+
cursor: Optional[str] = None
|
|
308
|
+
while True:
|
|
309
|
+
page = await self.list_page(
|
|
310
|
+
collection_id=collection_id, cursor=cursor, limit=limit
|
|
311
|
+
)
|
|
312
|
+
for item in page.get("items", []):
|
|
313
|
+
yield item
|
|
314
|
+
if not page.get("has_more") or not page.get("next_cursor"):
|
|
315
|
+
break
|
|
316
|
+
cursor = page["next_cursor"]
|
|
317
|
+
|
|
253
318
|
async def get(self, memory_id: str) -> Dict[str, Any]:
|
|
254
319
|
"""
|
|
255
320
|
Get a specific memory.
|
|
@@ -9,7 +9,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="hebbrix",
|
|
12
|
-
version="2.0.
|
|
12
|
+
version="2.0.3",
|
|
13
13
|
author="Hebbrix Team",
|
|
14
14
|
author_email="support@hebbrix.com",
|
|
15
15
|
description="Advanced Memory API for AI Agents with Reinforcement Learning - 3-line integration",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|