hebbrix 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.
hebbrix/__init__.py ADDED
@@ -0,0 +1,69 @@
1
+ """
2
+ Hebbrix Python SDK - Advanced Memory API for AI Agents
3
+
4
+ A modern, async-first Python SDK for the Hebbrix Memory API.
5
+ The only memory API with Reinforcement Learning for AI agents.
6
+
7
+ Quick Start:
8
+
9
+ from hebbrix import MemoryClient, MemoryChat
10
+
11
+ # Option 1: Simple 3-line chat integration
12
+ chat = MemoryChat(api_key="hbx_...")
13
+ response = chat.send("Remember I love Python!", "user_123")
14
+ response = chat.send("What language do I like?", "user_123")
15
+
16
+ # Option 2: Full async API client
17
+ async with MemoryClient(api_key="hbx_...") as client:
18
+ # Create collection
19
+ collection = await client.collections.create(name="My Agent")
20
+
21
+ # Store memory
22
+ memory = await client.memories.create(
23
+ collection_id=collection["id"],
24
+ content="Important information"
25
+ )
26
+
27
+ # Search with hybrid vector + BM25 + graph
28
+ results = await client.search(query="What was important?", limit=5)
29
+
30
+ # AI-powered reasoning over memories
31
+ answer = await client.reason(query="Explain what I learned")
32
+
33
+ Features:
34
+ - ✅ Reinforcement Learning for memory optimization
35
+ - ✅ Temporal Knowledge Graphs with bi-temporal model
36
+ - ✅ Procedural Memory (skills and learned behaviors)
37
+ - ✅ Working Memory (short-term context buffer)
38
+ - ✅ Memory Consolidation (automatic compression)
39
+ - ✅ 6-layer Hybrid Search (Vector + BM25 + KG + Decay + AI + RL)
40
+ - ✅ Complete async/await support
41
+ - ✅ Type hints throughout
42
+ """
43
+
44
+ __version__ = "2.0.0"
45
+ __author__ = "Hebbrix Team"
46
+ __license__ = "MIT"
47
+
48
+ from hebbrix.client import MemoryClient
49
+ from hebbrix.chat import MemoryChat
50
+ from hebbrix.exceptions import (
51
+ HebbrixError,
52
+ AuthenticationError,
53
+ ValidationError,
54
+ NotFoundError,
55
+ RateLimitError,
56
+ ServerError,
57
+ )
58
+
59
+
60
+ __all__ = [
61
+ "MemoryClient",
62
+ "MemoryChat",
63
+ "HebbrixError",
64
+ "AuthenticationError",
65
+ "ValidationError",
66
+ "NotFoundError",
67
+ "RateLimitError",
68
+ "ServerError",
69
+ ]
hebbrix/chat.py ADDED
@@ -0,0 +1,349 @@
1
+ """
2
+ Hebbrix Chat Wrapper - 3-Line Integration
3
+
4
+ The easiest way to add memory-powered conversations to your application.
5
+
6
+ Example:
7
+ from hebbrix import MemoryChat
8
+
9
+ chat = MemoryChat(api_key="mem_sk_...")
10
+ response = chat.send("What's my favorite food?", session_id="user_123")
11
+ """
12
+ from typing import Optional, Dict, Any, List
13
+ import requests
14
+
15
+ from hebbrix.client import MemoryClient
16
+
17
+
18
+ class MemoryChat:
19
+ """
20
+ Simple chat interface with automatic memory integration.
21
+
22
+ This is the easiest way to use Hebbrix - just 3 lines of code!
23
+
24
+ Features:
25
+ - Automatic memory retrieval
26
+ - User profile injection
27
+ - Session management
28
+ - RL-powered personalization
29
+ - OpenAI-compatible responses
30
+
31
+ Example:
32
+ ```python
33
+ from hebbrix import MemoryChat
34
+
35
+ # Line 1: Initialize
36
+ chat = MemoryChat(api_key="mem_sk_...")
37
+
38
+ # Line 2: Send message
39
+ response = chat.send("Hi, remember that I love pizza?", "user_123")
40
+
41
+ # Line 3: Get response with memory
42
+ response = chat.send("What's my favorite food?", "user_123")
43
+ print(response) # "Your favorite food is pizza!"
44
+ ```
45
+
46
+ Args:
47
+ api_key: Hebbrix API key
48
+ base_url: API base URL (default: https://api.hebbrix.com)
49
+ model: LLM model to use (default: gpt-4o-mini)
50
+ auto_memory: Enable automatic memory retrieval (default: True)
51
+ auto_profile: Enable user profile injection (default: True)
52
+ personalization: Enable RL-powered personalization (default: True)
53
+ learning: Enable RL trajectory logging (default: True)
54
+ prompt_strategy: Prompt template strategy (default: "default")
55
+ memory_limit: Max memories to retrieve per message (default: 10)
56
+ """
57
+
58
+ def __init__(
59
+ self,
60
+ api_key: str,
61
+ base_url: str = "http://localhost:8000",
62
+ model: str = "gpt-4o-mini",
63
+ auto_memory: bool = True,
64
+ auto_profile: bool = True,
65
+ personalization: bool = True,
66
+ learning: bool = True,
67
+ prompt_strategy: str = "default",
68
+ memory_limit: int = 10,
69
+ ):
70
+ self.api_key = api_key
71
+ self.base_url = base_url.rstrip("/")
72
+ self.model = model
73
+ self.prompt_strategy = prompt_strategy
74
+ self.memory_limit = memory_limit
75
+
76
+ # Feature configuration
77
+ self.features = {
78
+ "memory": auto_memory,
79
+ "user_profile": auto_profile,
80
+ "personalization": personalization,
81
+ "learning": learning,
82
+ }
83
+
84
+ # Also create full client for advanced operations
85
+ self.client = MemoryClient(api_key=api_key, base_url=base_url)
86
+
87
+ def send(
88
+ self,
89
+ message: str,
90
+ session_id: str,
91
+ temperature: float = 0.7,
92
+ max_tokens: Optional[int] = None,
93
+ ) -> str:
94
+ """
95
+ Send a message and get a response with automatic memory integration.
96
+
97
+ This is the main method - just call it and Hebbrix handles everything!
98
+
99
+ Args:
100
+ message: Your message
101
+ session_id: Session identifier (user ID or conversation ID)
102
+ temperature: Response randomness (0-2, default: 0.7)
103
+ max_tokens: Maximum response length (default: None)
104
+
105
+ Returns:
106
+ Assistant's response as a string
107
+
108
+ Example:
109
+ response = chat.send("What did I tell you earlier?", "user_123")
110
+ """
111
+ url = f"{self.base_url}/v1/chat/completions"
112
+
113
+ payload = {
114
+ "model": self.model,
115
+ "messages": [{"role": "user", "content": message}],
116
+ "session_id": session_id,
117
+ "features": self.features,
118
+ "prompt_strategy": self.prompt_strategy,
119
+ "memory_limit": self.memory_limit,
120
+ "temperature": temperature,
121
+ }
122
+
123
+ if max_tokens:
124
+ payload["max_tokens"] = max_tokens
125
+
126
+ headers = {
127
+ "Authorization": f"Bearer {self.api_key}",
128
+ "Content-Type": "application/json",
129
+ }
130
+
131
+ response = requests.post(url, json=payload, headers=headers)
132
+ response.raise_for_status()
133
+
134
+ data = response.json()
135
+ return data["choices"][0]["message"]["content"]
136
+
137
+ def send_with_context(
138
+ self,
139
+ message: str,
140
+ session_id: str,
141
+ temperature: float = 0.7,
142
+ max_tokens: Optional[int] = None,
143
+ ) -> Dict[str, Any]:
144
+ """
145
+ Send a message and get full response with memory context.
146
+
147
+ Use this when you want to see which memories were used.
148
+
149
+ Args:
150
+ message: Your message
151
+ session_id: Session identifier
152
+ temperature: Response randomness (0-2, default: 0.7)
153
+ max_tokens: Maximum response length (default: None)
154
+
155
+ Returns:
156
+ Full response dict with memory context
157
+
158
+ Example:
159
+ result = chat.send_with_context("What's my name?", "user_123")
160
+ print(result["message"]) # Assistant's response
161
+ print(result["memory_context"]) # Which memories were used
162
+ """
163
+ url = f"{self.base_url}/v1/chat/completions"
164
+
165
+ payload = {
166
+ "model": self.model,
167
+ "messages": [{"role": "user", "content": message}],
168
+ "session_id": session_id,
169
+ "features": self.features,
170
+ "prompt_strategy": self.prompt_strategy,
171
+ "memory_limit": self.memory_limit,
172
+ "temperature": temperature,
173
+ }
174
+
175
+ if max_tokens:
176
+ payload["max_tokens"] = max_tokens
177
+
178
+ headers = {
179
+ "Authorization": f"Bearer {self.api_key}",
180
+ "Content-Type": "application/json",
181
+ }
182
+
183
+ response = requests.post(url, json=payload, headers=headers)
184
+ response.raise_for_status()
185
+
186
+ data = response.json()
187
+
188
+ return {
189
+ "message": data["choices"][0]["message"]["content"],
190
+ "memory_context": data["memory_context"],
191
+ "usage": data["usage"],
192
+ "model": data["model"],
193
+ }
194
+
195
+ def configure_features(
196
+ self,
197
+ auto_memory: Optional[bool] = None,
198
+ auto_profile: Optional[bool] = None,
199
+ personalization: Optional[bool] = None,
200
+ learning: Optional[bool] = None,
201
+ ) -> None:
202
+ """
203
+ Configure features on the fly.
204
+
205
+ Args:
206
+ auto_memory: Enable/disable automatic memory retrieval
207
+ auto_profile: Enable/disable user profile injection
208
+ personalization: Enable/disable RL personalization
209
+ learning: Enable/disable RL learning
210
+ """
211
+ if auto_memory is not None:
212
+ self.features["memory"] = auto_memory
213
+ if auto_profile is not None:
214
+ self.features["user_profile"] = auto_profile
215
+ if personalization is not None:
216
+ self.features["personalization"] = personalization
217
+ if learning is not None:
218
+ self.features["learning"] = learning
219
+
220
+ def set_model(self, model: str) -> None:
221
+ """
222
+ Change the LLM model.
223
+
224
+ Args:
225
+ model: Model name (e.g., "gpt-4o-mini", "claude-3-sonnet")
226
+ """
227
+ self.model = model
228
+
229
+ def set_strategy(self, strategy: str) -> None:
230
+ """
231
+ Change the prompt strategy.
232
+
233
+ Args:
234
+ strategy: Strategy name ("default", "concise", "detailed")
235
+ """
236
+ self.prompt_strategy = strategy
237
+
238
+ def list_sessions(self, active_only: bool = True) -> List[Dict[str, Any]]:
239
+ """
240
+ List all chat sessions.
241
+
242
+ Args:
243
+ active_only: Only return active sessions
244
+
245
+ Returns:
246
+ List of session dicts
247
+ """
248
+ url = f"{self.base_url}/v1/chat/sessions"
249
+ params = {"active_only": active_only}
250
+ headers = {"Authorization": f"Bearer {self.api_key}"}
251
+
252
+ response = requests.get(url, params=params, headers=headers)
253
+ response.raise_for_status()
254
+
255
+ return response.json()["sessions"]
256
+
257
+ def get_session_info(self, session_id: str) -> Dict[str, Any]:
258
+ """
259
+ Get information about a specific session.
260
+
261
+ Args:
262
+ session_id: Session identifier
263
+
264
+ Returns:
265
+ Session info and statistics
266
+ """
267
+ url = f"{self.base_url}/v1/chat/sessions/{session_id}"
268
+ headers = {"Authorization": f"Bearer {self.api_key}"}
269
+
270
+ response = requests.get(url, headers=headers)
271
+ response.raise_for_status()
272
+
273
+ return response.json()
274
+
275
+ def delete_session(self, session_id: str) -> None:
276
+ """
277
+ Delete a chat session.
278
+
279
+ Args:
280
+ session_id: Session identifier
281
+ """
282
+ url = f"{self.base_url}/v1/chat/sessions/{session_id}"
283
+ headers = {"Authorization": f"Bearer {self.api_key}"}
284
+
285
+ response = requests.delete(url, headers=headers)
286
+ response.raise_for_status()
287
+
288
+ # Convenience methods for memory operations
289
+ def add_memory(self, content: str, collection_id: str, importance: float = 0.5) -> Dict[str, Any]:
290
+ """
291
+ Add a memory manually.
292
+
293
+ Args:
294
+ content: Memory content
295
+ collection_id: Collection ID
296
+ importance: Memory importance (0-1)
297
+
298
+ Returns:
299
+ Created memory dict
300
+ """
301
+ return self.client.memories.create(
302
+ content=content,
303
+ collection_id=collection_id,
304
+ importance=importance,
305
+ )
306
+
307
+ def search_memories(self, query: str, collection_id: Optional[str] = None, limit: int = 10) -> List[Dict[str, Any]]:
308
+ """
309
+ Search memories manually.
310
+
311
+ Args:
312
+ query: Search query
313
+ collection_id: Optional collection filter
314
+ limit: Number of results
315
+
316
+ Returns:
317
+ List of memory dicts
318
+ """
319
+ return self.client.memories.search(
320
+ query=query,
321
+ collection_id=collection_id,
322
+ limit=limit,
323
+ )["results"]
324
+
325
+
326
+ # Convenience function for even simpler usage
327
+ def quick_chat(api_key: str, message: str, session_id: str) -> str:
328
+ """
329
+ The absolute simplest way to use Hebbrix - single function call.
330
+
331
+ Args:
332
+ api_key: Hebbrix API key
333
+ message: Your message
334
+ session_id: Session identifier
335
+
336
+ Returns:
337
+ Assistant's response
338
+
339
+ Example:
340
+ from hebbrix.chat import quick_chat
341
+
342
+ response = quick_chat(
343
+ api_key="mem_sk_...",
344
+ message="What's my favorite color?",
345
+ session_id="user_123"
346
+ )
347
+ """
348
+ chat = MemoryChat(api_key=api_key)
349
+ return chat.send(message, session_id)
hebbrix/client.py ADDED
@@ -0,0 +1,226 @@
1
+ """
2
+ Hebbrix Client
3
+
4
+ Main client class for interacting with the Hebbrix api.
5
+ """
6
+ from typing import Optional, Dict, Any, List
7
+ import httpx
8
+
9
+ from hebbrix.resources import (
10
+ AuthResource,
11
+ CollectionsResource,
12
+ MemoriesResource,
13
+ SearchResource,
14
+ RLResource,
15
+ ProceduralResource,
16
+ TemporalResource,
17
+ WorkingMemoryResource,
18
+ ConsolidationResource,
19
+ MemoryToolsResource,
20
+ WorldModelResource,
21
+ )
22
+ from hebbrix.exceptions import (
23
+ HebbrixError,
24
+ AuthenticationError,
25
+ ValidationError,
26
+ NotFoundError,
27
+ RateLimitError,
28
+ ServerError,
29
+ )
30
+
31
+
32
+ class MemoryClient:
33
+ """
34
+ Hebbrix api client.
35
+
36
+ Args:
37
+ api_key: API key for authentication
38
+ base_url: Base URL of the API (default: https://api.hebbrix.com)
39
+ timeout: Request timeout in seconds (default: 30)
40
+
41
+ Example:
42
+ >>> client = MemoryClient(api_key="mem_sk_...")
43
+ >>> memory = await client.memories.create(
44
+ ... collection_id="col_123",
45
+ ... content="Important note"
46
+ ... )
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ api_key: Optional[str] = None,
52
+ base_url: str = "https://memory-api.livelystone-78e9a45c.centralus.azurecontainerapps.io",
53
+ timeout: float = 120.0,
54
+ ):
55
+ self.api_key = api_key
56
+ self.base_url = base_url.rstrip("/")
57
+ self.timeout = timeout
58
+
59
+ # HTTP client
60
+ self._client = httpx.AsyncClient(
61
+ base_url=self.base_url,
62
+ timeout=timeout,
63
+ headers=self._get_headers(),
64
+ )
65
+
66
+ # Initialize resources
67
+ self.auth = AuthResource(self)
68
+ self.collections = CollectionsResource(self)
69
+ self.memories = MemoriesResource(self)
70
+ self.search_resource = SearchResource(self)
71
+ self.rl = RLResource(self)
72
+ self.procedural = ProceduralResource(self)
73
+ self.temporal = TemporalResource(self)
74
+ self.working_memory = WorkingMemoryResource(self)
75
+ self.consolidation = ConsolidationResource(self)
76
+ self.memory_tools = MemoryToolsResource(self)
77
+ self.world_model = WorldModelResource(self)
78
+
79
+ def _get_headers(self) -> Dict[str, str]:
80
+ """Get request headers."""
81
+ headers = {
82
+ "Content-Type": "application/json",
83
+ "User-Agent": "hebbrix-python/2.0.0",
84
+ }
85
+
86
+ if self.api_key:
87
+ headers["Authorization"] = f"Bearer {self.api_key}"
88
+
89
+ return headers
90
+
91
+ def _handle_error(self, response: httpx.Response) -> None:
92
+ """Handle error responses."""
93
+ status_code = response.status_code
94
+
95
+ try:
96
+ error_data = response.json()
97
+ message = error_data.get("error", {}).get("message", response.text)
98
+ except Exception:
99
+ message = response.text
100
+
101
+ if status_code == 401:
102
+ raise AuthenticationError(message)
103
+ elif status_code == 404:
104
+ raise NotFoundError(message)
105
+ elif status_code == 422:
106
+ errors = error_data.get("error", {}).get("details", [])
107
+ raise ValidationError(message, errors=errors)
108
+ elif status_code == 429:
109
+ raise RateLimitError(message)
110
+ elif status_code >= 500:
111
+ raise ServerError(message)
112
+ else:
113
+ raise HebbrixError(message, status_code=status_code)
114
+
115
+ async def request(
116
+ self,
117
+ method: str,
118
+ path: str,
119
+ **kwargs,
120
+ ) -> Dict[str, Any]:
121
+ """
122
+ Make an HTTP request.
123
+
124
+ Args:
125
+ method: HTTP method (GET, POST, etc.)
126
+ path: API path
127
+ **kwargs: Additional arguments for httpx
128
+
129
+ Returns:
130
+ Response data as dictionary
131
+
132
+ Raises:
133
+ HebbrixError: On API errors
134
+ """
135
+ url = f"{self.base_url}{path}"
136
+
137
+ response = await self._client.request(method, url, **kwargs)
138
+
139
+ if response.status_code >= 400:
140
+ self._handle_error(response)
141
+
142
+ return response.json() if response.text else {}
143
+
144
+ async def get(self, path: str, **kwargs) -> Dict[str, Any]:
145
+ """Make a GET request."""
146
+ return await self.request("GET", path, **kwargs)
147
+
148
+ async def post(self, path: str, **kwargs) -> Dict[str, Any]:
149
+ """Make a POST request."""
150
+ return await self.request("POST", path, **kwargs)
151
+
152
+ async def patch(self, path: str, **kwargs) -> Dict[str, Any]:
153
+ """Make a PATCH request."""
154
+ return await self.request("PATCH", path, **kwargs)
155
+
156
+ async def delete(self, path: str, **kwargs) -> Dict[str, Any]:
157
+ """Make a DELETE request."""
158
+ return await self.request("DELETE", path, **kwargs)
159
+
160
+ # Convenience methods
161
+ async def search(
162
+ self,
163
+ query: str,
164
+ collection_id: Optional[str] = None,
165
+ limit: int = 10,
166
+ search_type: str = "hybrid",
167
+ filters: Optional[Dict[str, Any]] = None,
168
+ ) -> List[Dict[str, Any]]:
169
+ """
170
+ Search memories.
171
+
172
+ Args:
173
+ query: Search query
174
+ collection_id: Optional collection filter
175
+ limit: Number of results
176
+ search_type: Type of search (hybrid, vector, bm25, graph)
177
+ filters: Additional filters
178
+
179
+ Returns:
180
+ List of search results
181
+ """
182
+ return await self.search_resource.search(
183
+ query=query,
184
+ collection_id=collection_id,
185
+ limit=limit,
186
+ search_type=search_type,
187
+ filters=filters or {},
188
+ )
189
+
190
+ async def reason(
191
+ self,
192
+ query: str,
193
+ collection_id: Optional[str] = None,
194
+ provider: Optional[str] = None,
195
+ include_steps: bool = False,
196
+ ) -> Dict[str, Any]:
197
+ """
198
+ Perform reasoning over memories.
199
+
200
+ Args:
201
+ query: Question or query
202
+ collection_id: Optional collection filter
203
+ provider: LLM provider (gemini, openai, anthropic)
204
+ include_steps: Include reasoning steps
205
+
206
+ Returns:
207
+ Reasoning result with answer and sources
208
+ """
209
+ return await self.search_resource.reason(
210
+ query=query,
211
+ collection_id=collection_id,
212
+ provider=provider,
213
+ include_steps=include_steps,
214
+ )
215
+
216
+ async def close(self):
217
+ """Close the HTTP client."""
218
+ await self._client.aclose()
219
+
220
+ async def __aenter__(self):
221
+ """Async context manager entry."""
222
+ return self
223
+
224
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
225
+ """Async context manager exit."""
226
+ await self.close()
hebbrix/exceptions.py ADDED
@@ -0,0 +1,48 @@
1
+ """
2
+ Hebbrix SDK Exceptions
3
+ """
4
+
5
+
6
+ class HebbrixError(Exception):
7
+ """Base exception for Hebbrix SDK."""
8
+
9
+ def __init__(self, message: str, status_code: int = None):
10
+ self.message = message
11
+ self.status_code = status_code
12
+ super().__init__(self.message)
13
+
14
+
15
+ class AuthenticationError(HebbrixError):
16
+ """Raised when authentication fails."""
17
+
18
+ def __init__(self, message: str = "Authentication failed"):
19
+ super().__init__(message, status_code=401)
20
+
21
+
22
+ class ValidationError(HebbrixError):
23
+ """Raised when request validation fails."""
24
+
25
+ def __init__(self, message: str, errors: list = None):
26
+ self.errors = errors or []
27
+ super().__init__(message, status_code=422)
28
+
29
+
30
+ class NotFoundError(HebbrixError):
31
+ """Raised when a resource is not found."""
32
+
33
+ def __init__(self, message: str = "Resource not found"):
34
+ super().__init__(message, status_code=404)
35
+
36
+
37
+ class RateLimitError(HebbrixError):
38
+ """Raised when rate limit is exceeded."""
39
+
40
+ def __init__(self, message: str = "Rate limit exceeded"):
41
+ super().__init__(message, status_code=429)
42
+
43
+
44
+ class ServerError(HebbrixError):
45
+ """Raised when server returns 5xx error."""
46
+
47
+ def __init__(self, message: str = "Internal server error"):
48
+ super().__init__(message, status_code=500)