agenticmem 0.1.2.4__tar.gz → 0.1.3.8__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.

Potentially problematic release.


This version of agenticmem might be problematic. Click here for more details.

@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.3
2
+ Name: agenticmem
3
+ Version: 0.1.3.8
4
+ Summary: A Python client for the AgenticMem API
5
+ License: MIT
6
+ Author: AgenticMem Team
7
+ Requires-Python: >=3.10,<4.0
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Dist: agenticmem-commons (==0.1.3.8)
15
+ Requires-Dist: aiohttp (>=3.12.9,<4.0.0)
16
+ Requires-Dist: griffe (==0.48.0)
17
+ Requires-Dist: mkdocs-with-pdf (>=0.9.3,<0.10.0)
18
+ Requires-Dist: mkdocstrings[python] (>=0.18.0)
19
+ Requires-Dist: pydantic (>=2.0.0,<3.0.0)
20
+ Requires-Dist: python-dateutil (>=2.8.0,<3.0.0)
21
+ Requires-Dist: requests (>=2.25.0,<3.0.0)
22
+ Description-Content-Type: text/markdown
23
+
24
+ # /user_profiler/agenticmem/agenticmem_client
25
+ Description: Python SDK for remote async access to AgenticMem API
26
+
27
+ ## Main Entry Points
28
+
29
+ - **Client**: `agenticmem/client.py` - `AgenticMemClient`
30
+ - **Utils**: `agenticmem/client_utils.py` - Helper utilities
31
+
32
+ ## Purpose
33
+
34
+ 1. **Remote API access** - Async SDK for applications to call AgenticMem backend
35
+ 2. **Authentication** - Handle login and Bearer token management
36
+ 3. **Type-safe interface** - Auto-parsing responses into Pydantic models
37
+
38
+ ## API Methods
39
+
40
+ **Authentication:**
41
+ - `login(email, password)` - Get auth token
42
+
43
+ **Publishing:**
44
+ - `publish_interaction(request_id, user_id, interactions, source, agent_version)` - Publish interactions (triggers profile/feedback/evaluation)
45
+
46
+ **Profiles:**
47
+ - `search_profiles(request)` - Semantic search
48
+ - `get_profiles(request)` - Get all for user
49
+ - `delete_profile(user_id, profile_id, search_query)` - Delete profiles
50
+ - `get_profile_change_log()` - Get history
51
+
52
+ **Interactions:**
53
+ - `search_interactions(request)` - Semantic search
54
+ - `get_interactions(request)` - Get all for user
55
+ - `delete_interaction(user_id, interaction_id)` - Delete interaction
56
+
57
+ **Feedback:**
58
+ - `get_raw_feedbacks(request)` - Raw feedback from interactions
59
+ - `get_feedbacks(request)` - Aggregated feedback with status
60
+
61
+ **Configuration:**
62
+ - `set_config(config)` - Update org config (extractors, evaluators, storage)
63
+ - `get_config()` - Get current config
64
+
65
+ ## Architecture Pattern
66
+
67
+ - **All async** - Uses `aiohttp` for HTTP requests
68
+ - **Type-safe** - Pydantic models from `agenticmem_commons`
69
+ - **Auto-parsing** - Responses → Pydantic models
70
+ - **Flexible input** - Accepts Pydantic models or dicts
71
+ - **Bearer auth** - Automatic token handling
72
+
@@ -0,0 +1,48 @@
1
+ # /user_profiler/agenticmem/agenticmem_client
2
+ Description: Python SDK for remote async access to AgenticMem API
3
+
4
+ ## Main Entry Points
5
+
6
+ - **Client**: `agenticmem/client.py` - `AgenticMemClient`
7
+ - **Utils**: `agenticmem/client_utils.py` - Helper utilities
8
+
9
+ ## Purpose
10
+
11
+ 1. **Remote API access** - Async SDK for applications to call AgenticMem backend
12
+ 2. **Authentication** - Handle login and Bearer token management
13
+ 3. **Type-safe interface** - Auto-parsing responses into Pydantic models
14
+
15
+ ## API Methods
16
+
17
+ **Authentication:**
18
+ - `login(email, password)` - Get auth token
19
+
20
+ **Publishing:**
21
+ - `publish_interaction(request_id, user_id, interactions, source, agent_version)` - Publish interactions (triggers profile/feedback/evaluation)
22
+
23
+ **Profiles:**
24
+ - `search_profiles(request)` - Semantic search
25
+ - `get_profiles(request)` - Get all for user
26
+ - `delete_profile(user_id, profile_id, search_query)` - Delete profiles
27
+ - `get_profile_change_log()` - Get history
28
+
29
+ **Interactions:**
30
+ - `search_interactions(request)` - Semantic search
31
+ - `get_interactions(request)` - Get all for user
32
+ - `delete_interaction(user_id, interaction_id)` - Delete interaction
33
+
34
+ **Feedback:**
35
+ - `get_raw_feedbacks(request)` - Raw feedback from interactions
36
+ - `get_feedbacks(request)` - Aggregated feedback with status
37
+
38
+ **Configuration:**
39
+ - `set_config(config)` - Update org config (extractors, evaluators, storage)
40
+ - `get_config()` - Get current config
41
+
42
+ ## Architecture Pattern
43
+
44
+ - **All async** - Uses `aiohttp` for HTTP requests
45
+ - **Type-safe** - Pydantic models from `agenticmem_commons`
46
+ - **Auto-parsing** - Responses → Pydantic models
47
+ - **Flexible input** - Accepts Pydantic models or dicts
48
+ - **Bearer auth** - Automatic token handling
@@ -2,7 +2,7 @@ __app_name__ = "agenticmem"
2
2
  __version__ = "0.1.1"
3
3
 
4
4
 
5
- from agenticmem.client import AgenticMemClient
5
+ from .client import AgenticMemClient
6
6
  from agenticmem_commons.api_schema.service_schemas import (
7
7
  UserActionType,
8
8
  ProfileTimeToLive,
@@ -31,6 +31,8 @@ from agenticmem_commons.config_schema import (
31
31
  ProfileExtractorConfig,
32
32
  FeedbackAggregatorConfig,
33
33
  AgentFeedbackConfig,
34
+ AgentSuccessConfig,
35
+ ToolUseConfig,
34
36
  Config,
35
37
  )
36
38
 
@@ -63,5 +65,7 @@ __all__ = [
63
65
  "ProfileExtractorConfig",
64
66
  "FeedbackAggregatorConfig",
65
67
  "AgentFeedbackConfig",
68
+ "AgentSuccessConfig",
69
+ "ToolUseConfig",
66
70
  "Config",
67
71
  ]
@@ -0,0 +1,531 @@
1
+ import os
2
+ import aiohttp
3
+ import asyncio
4
+ from urllib.parse import urljoin
5
+ import requests
6
+ from concurrent.futures import ThreadPoolExecutor
7
+ from typing import Optional, Union
8
+ from agenticmem_commons.api_schema.retriever_schema import (
9
+ SearchInteractionRequest,
10
+ SearchInteractionResponse,
11
+ SearchUserProfileRequest,
12
+ SearchUserProfileResponse,
13
+ GetInteractionsRequest,
14
+ GetInteractionsResponse,
15
+ GetUserProfilesRequest,
16
+ GetUserProfilesResponse,
17
+ GetRawFeedbacksRequest,
18
+ GetRawFeedbacksResponse,
19
+ GetFeedbacksRequest,
20
+ GetFeedbacksResponse,
21
+ )
22
+
23
+ from dotenv import load_dotenv
24
+
25
+ # Load environment variables from .env file
26
+ load_dotenv()
27
+
28
+ IS_TEST_ENV = os.environ.get("IS_TEST_ENV", "false").strip() == "true"
29
+
30
+ if IS_TEST_ENV:
31
+ BACKEND_URL = "http://127.0.0.1:8000" # Local server for testing
32
+ else:
33
+ BACKEND_URL = "http://agenticmem-test.us-west-2.elasticbeanstalk.com:8081" # Elastic Beanstalk server url
34
+
35
+ from agenticmem_commons.api_schema.service_schemas import (
36
+ InteractionRequest,
37
+ ProfileChangeLogResponse,
38
+ PublishUserInteractionRequest,
39
+ PublishUserInteractionResponse,
40
+ DeleteUserProfileRequest,
41
+ DeleteUserProfileResponse,
42
+ DeleteUserInteractionRequest,
43
+ DeleteUserInteractionResponse,
44
+ )
45
+ from agenticmem_commons.api_schema.login_schema import Token
46
+ from agenticmem_commons.config_schema import Config
47
+
48
+
49
+ class AgenticMemClient:
50
+ """Client for interacting with the AgenticMem API."""
51
+
52
+ # Shared thread pool for all instances to maximize efficiency
53
+ _thread_pool = ThreadPoolExecutor(max_workers=4, thread_name_prefix="agenticmem")
54
+
55
+ def __init__(self, api_key: str = "", url_endpoint: str = ""):
56
+ """Initialize the AgenticMem client.
57
+
58
+ Args:
59
+ api_key (str): Your API key for authentication
60
+ """
61
+ self.api_key = api_key
62
+ self.base_url = url_endpoint if url_endpoint else BACKEND_URL
63
+ self.session = requests.Session()
64
+
65
+ def _get_auth_headers(self) -> dict:
66
+ """Get authentication headers for API requests.
67
+
68
+ Returns:
69
+ dict: Headers with authorization and content-type
70
+ """
71
+ if self.api_key:
72
+ return {
73
+ "Authorization": f"Bearer {self.api_key}",
74
+ "Content-Type": "application/json",
75
+ }
76
+ return {}
77
+
78
+ def _convert_to_model(self, data: Union[dict, object], model_class):
79
+ """Convert dict to model instance if needed.
80
+
81
+ Args:
82
+ data: Either a dict or already an instance of model_class
83
+ model_class: The target class to convert to
84
+
85
+ Returns:
86
+ An instance of model_class
87
+ """
88
+ if isinstance(data, dict):
89
+ return model_class(**data)
90
+ return data
91
+
92
+ def _fire_and_forget(self, sync_func, async_func, *args, **kwargs):
93
+ """Execute a request in fire-and-forget mode.
94
+
95
+ This method optimizes execution based on context:
96
+ - In async contexts (e.g., FastAPI): Uses existing event loop (most efficient)
97
+ - In sync contexts: Uses shared thread pool (avoids thread creation overhead)
98
+
99
+ Args:
100
+ sync_func: Synchronous function to call
101
+ async_func: Asynchronous function to call
102
+ *args: Positional arguments to pass to the function
103
+ **kwargs: Keyword arguments to pass to the function
104
+ """
105
+ try:
106
+ loop = asyncio.get_running_loop()
107
+
108
+ # We're in an async context - use the event loop
109
+ async def fire_and_forget():
110
+ try:
111
+ await async_func(*args, **kwargs)
112
+ except Exception:
113
+ # Silently ignore errors in fire-and-forget mode
114
+ pass
115
+
116
+ loop.create_task(fire_and_forget())
117
+ except RuntimeError:
118
+ # No running loop - we're in sync context, use thread pool
119
+ def send_request():
120
+ try:
121
+ sync_func(*args, **kwargs)
122
+ except Exception:
123
+ # Silently ignore errors in fire-and-forget mode
124
+ pass
125
+
126
+ self._thread_pool.submit(send_request)
127
+
128
+ async def _make_async_request(
129
+ self, method: str, endpoint: str, headers: Optional[dict] = None, **kwargs
130
+ ):
131
+ """Make an async HTTP request to the API."""
132
+ url = urljoin(self.base_url, endpoint)
133
+
134
+ # Merge auth headers with any provided headers
135
+ request_headers = self._get_auth_headers()
136
+ if headers:
137
+ request_headers.update(headers)
138
+
139
+ async with aiohttp.ClientSession() as async_session:
140
+ response = await async_session.request(
141
+ method, url, headers=request_headers, **kwargs
142
+ )
143
+ response.raise_for_status()
144
+ return await response.json()
145
+
146
+ def _make_request(
147
+ self, method: str, endpoint: str, headers: Optional[dict] = None, **kwargs
148
+ ):
149
+ """Make an HTTP request to the API.
150
+
151
+ Args:
152
+ method (str): HTTP method (GET, POST, DELETE)
153
+ endpoint (str): API endpoint
154
+ headers (dict, optional): Additional headers to include in the request
155
+ **kwargs: Additional arguments to pass to requests
156
+
157
+ Returns:
158
+ dict: API response
159
+ """
160
+ url = urljoin(self.base_url, endpoint)
161
+
162
+ # Merge auth headers with any provided headers
163
+ request_headers = self._get_auth_headers()
164
+ if headers:
165
+ request_headers.update(headers)
166
+
167
+ self.session.headers.update(request_headers)
168
+ response = self.session.request(method, url, **kwargs)
169
+ response.raise_for_status()
170
+ return response.json()
171
+
172
+ def login(self, email: str, password: str) -> Token:
173
+ """Login to the AgenticMem API."""
174
+ response = self._make_request(
175
+ "POST",
176
+ "/token",
177
+ data={"username": email, "password": password},
178
+ headers={
179
+ "Content-Type": "application/x-www-form-urlencoded",
180
+ "accept": "application/json",
181
+ },
182
+ )
183
+ return Token(**response)
184
+
185
+ def _publish_interaction_sync(
186
+ self, request: PublishUserInteractionRequest
187
+ ) -> PublishUserInteractionResponse:
188
+ """Internal sync method to publish interaction."""
189
+ response = self._make_request(
190
+ "POST",
191
+ "/api/publish_interaction",
192
+ json=request.model_dump(),
193
+ )
194
+ return PublishUserInteractionResponse(**response)
195
+
196
+ async def _publish_interaction_async(
197
+ self, request: PublishUserInteractionRequest
198
+ ) -> PublishUserInteractionResponse:
199
+ """Internal async method to publish interaction."""
200
+ response = await self._make_async_request(
201
+ "POST",
202
+ "/api/publish_interaction",
203
+ json=request.model_dump(),
204
+ )
205
+ return PublishUserInteractionResponse(**response)
206
+
207
+ def publish_interaction(
208
+ self,
209
+ request_id: str,
210
+ user_id: str,
211
+ interaction_requests: list[Union[InteractionRequest, dict]],
212
+ source: str = "",
213
+ agent_version: str = "",
214
+ wait_for_response: bool = False,
215
+ ) -> Optional[PublishUserInteractionResponse]:
216
+ """Publish user interactions.
217
+
218
+ This method is optimized for resource efficiency:
219
+ - In async contexts (e.g., FastAPI): Uses existing event loop (most efficient)
220
+ - In sync contexts: Uses shared thread pool (avoids thread creation overhead)
221
+
222
+ Args:
223
+ request_id (str): The request ID
224
+ user_id (str): The user ID
225
+ interaction_requests (List[InteractionRequest]): List of interaction requests
226
+ source (str, optional): The source of the interaction
227
+ agent_version (str, optional): The agent version
228
+ wait_for_response (bool, optional): If True, wait for response. If False, send request without waiting. Defaults to False.
229
+ Returns:
230
+ Optional[PublishUserInteractionResponse]: Response containing success status and message if wait_for_response=True, None otherwise
231
+ """
232
+ interaction_requests = [
233
+ (
234
+ InteractionRequest(**interaction_request)
235
+ if isinstance(interaction_request, dict)
236
+ else interaction_request
237
+ )
238
+ for interaction_request in interaction_requests
239
+ ]
240
+ request = PublishUserInteractionRequest(
241
+ request_id=request_id,
242
+ user_id=user_id,
243
+ interaction_requests=interaction_requests,
244
+ source=source,
245
+ agent_version=agent_version,
246
+ )
247
+
248
+ if wait_for_response:
249
+ # Synchronous blocking call
250
+ return self._publish_interaction_sync(request)
251
+ else:
252
+ # Non-blocking fire-and-forget
253
+ self._fire_and_forget(
254
+ self._publish_interaction_sync, self._publish_interaction_async, request
255
+ )
256
+ return None
257
+
258
+ def search_interactions(
259
+ self,
260
+ request: Union[SearchInteractionRequest, dict],
261
+ ) -> SearchInteractionResponse:
262
+ """Search for user interactions.
263
+
264
+ Args:
265
+ request (SearchInteractionRequest): The search request
266
+
267
+ Returns:
268
+ SearchInteractionResponse: Response containing matching interactions
269
+ """
270
+ request = self._convert_to_model(request, SearchInteractionRequest)
271
+ response = self._make_request(
272
+ "POST",
273
+ "/api/search_interactions",
274
+ json=request.model_dump(),
275
+ )
276
+ return SearchInteractionResponse(**response)
277
+
278
+ def search_profiles(
279
+ self,
280
+ request: Union[SearchUserProfileRequest, dict],
281
+ ) -> SearchUserProfileResponse:
282
+ """Search for user profiles.
283
+
284
+ Args:
285
+ request (SearchUserProfileRequest): The search request
286
+
287
+ Returns:
288
+ SearchUserProfileResponse: Response containing matching profiles
289
+ """
290
+ request = self._convert_to_model(request, SearchUserProfileRequest)
291
+ response = self._make_request(
292
+ "POST", "/api/search_profiles", json=request.model_dump()
293
+ )
294
+ return SearchUserProfileResponse(**response)
295
+
296
+ def _delete_profile_sync(
297
+ self, request: DeleteUserProfileRequest
298
+ ) -> DeleteUserProfileResponse:
299
+ """Internal sync method to delete profile."""
300
+ response = self._make_request(
301
+ "DELETE",
302
+ "/api/delete_profile",
303
+ json=request.model_dump(),
304
+ )
305
+ return DeleteUserProfileResponse(**response)
306
+
307
+ async def _delete_profile_async(
308
+ self, request: DeleteUserProfileRequest
309
+ ) -> DeleteUserProfileResponse:
310
+ """Internal async method to delete profile."""
311
+ response = await self._make_async_request(
312
+ "DELETE",
313
+ "/api/delete_profile",
314
+ json=request.model_dump(),
315
+ )
316
+ return DeleteUserProfileResponse(**response)
317
+
318
+ def delete_profile(
319
+ self,
320
+ user_id: str,
321
+ profile_id: str = "",
322
+ search_query: str = "",
323
+ wait_for_response: bool = False,
324
+ ) -> Optional[DeleteUserProfileResponse]:
325
+ """Delete user profiles.
326
+
327
+ This method is optimized for resource efficiency:
328
+ - In async contexts (e.g., FastAPI): Uses existing event loop (most efficient)
329
+ - In sync contexts: Uses shared thread pool (avoids thread creation overhead)
330
+
331
+ Args:
332
+ user_id (str): The user ID
333
+ profile_id (str, optional): Specific profile ID to delete
334
+ search_query (str, optional): Query to match profiles for deletion
335
+ wait_for_response (bool, optional): If True, wait for response. If False, send request without waiting. Defaults to False.
336
+
337
+ Returns:
338
+ Optional[DeleteUserProfileResponse]: Response containing success status and message if wait_for_response=True, None otherwise
339
+ """
340
+ request = DeleteUserProfileRequest(
341
+ user_id=user_id,
342
+ profile_id=profile_id,
343
+ search_query=search_query,
344
+ )
345
+
346
+ if wait_for_response:
347
+ # Synchronous blocking call
348
+ return self._delete_profile_sync(request)
349
+ else:
350
+ # Non-blocking fire-and-forget
351
+ self._fire_and_forget(
352
+ self._delete_profile_sync, self._delete_profile_async, request
353
+ )
354
+ return None
355
+
356
+ def _delete_interaction_sync(
357
+ self, request: DeleteUserInteractionRequest
358
+ ) -> DeleteUserInteractionResponse:
359
+ """Internal sync method to delete interaction."""
360
+ response = self._make_request(
361
+ "DELETE",
362
+ "/api/delete_interaction",
363
+ json=request.model_dump(),
364
+ )
365
+ return DeleteUserInteractionResponse(**response)
366
+
367
+ async def _delete_interaction_async(
368
+ self, request: DeleteUserInteractionRequest
369
+ ) -> DeleteUserInteractionResponse:
370
+ """Internal async method to delete interaction."""
371
+ response = await self._make_async_request(
372
+ "DELETE",
373
+ "/api/delete_interaction",
374
+ json=request.model_dump(),
375
+ )
376
+ return DeleteUserInteractionResponse(**response)
377
+
378
+ def delete_interaction(
379
+ self, user_id: str, interaction_id: str, wait_for_response: bool = False
380
+ ) -> Optional[DeleteUserInteractionResponse]:
381
+ """Delete a user interaction.
382
+
383
+ This method is optimized for resource efficiency:
384
+ - In async contexts (e.g., FastAPI): Uses existing event loop (most efficient)
385
+ - In sync contexts: Uses shared thread pool (avoids thread creation overhead)
386
+
387
+ Args:
388
+ user_id (str): The user ID
389
+ interaction_id (str): The interaction ID to delete
390
+ wait_for_response (bool, optional): If True, wait for response. If False, send request without waiting. Defaults to False.
391
+
392
+ Returns:
393
+ Optional[DeleteUserInteractionResponse]: Response containing success status and message if wait_for_response=True, None otherwise
394
+ """
395
+ request = DeleteUserInteractionRequest(
396
+ user_id=user_id, interaction_id=interaction_id
397
+ )
398
+
399
+ if wait_for_response:
400
+ # Synchronous blocking call
401
+ return self._delete_interaction_sync(request)
402
+ else:
403
+ # Non-blocking fire-and-forget
404
+ self._fire_and_forget(
405
+ self._delete_interaction_sync, self._delete_interaction_async, request
406
+ )
407
+ return None
408
+
409
+ def get_profile_change_log(self) -> ProfileChangeLogResponse:
410
+ """Get profile change log.
411
+
412
+ Returns:
413
+ ProfileChangeLogResponse: Response containing profile change log
414
+ """
415
+ response = self._make_request("GET", "/api/profile_change_log")
416
+ return ProfileChangeLogResponse(**response)
417
+
418
+ def get_interactions(
419
+ self,
420
+ request: Union[GetInteractionsRequest, dict],
421
+ ) -> GetInteractionsResponse:
422
+ """Get user interactions.
423
+
424
+ Args:
425
+ request (GetInteractionsRequest): The list request
426
+
427
+ Returns:
428
+ GetInteractionsResponse: Response containing list of interactions
429
+ """
430
+ request = self._convert_to_model(request, GetInteractionsRequest)
431
+ response = self._make_request(
432
+ "POST",
433
+ "/api/get_interactions",
434
+ json=request.model_dump(),
435
+ )
436
+ return GetInteractionsResponse(**response)
437
+
438
+ def get_profiles(
439
+ self,
440
+ request: Union[GetUserProfilesRequest, dict],
441
+ ) -> GetUserProfilesResponse:
442
+ """Get user profiles.
443
+
444
+ Args:
445
+ request (GetUserProfilesRequest): The list request
446
+
447
+ Returns:
448
+ GetUserProfilesResponse: Response containing list of profiles
449
+ """
450
+ request = self._convert_to_model(request, GetUserProfilesRequest)
451
+ response = self._make_request(
452
+ "POST",
453
+ "/api/get_profiles",
454
+ json=request.model_dump(),
455
+ )
456
+ return GetUserProfilesResponse(**response)
457
+
458
+ def set_config(self, config: Union[Config, dict]) -> dict:
459
+ """Set configuration for the organization.
460
+
461
+ Args:
462
+ config (Union[Config, dict]): The configuration to set
463
+
464
+ Returns:
465
+ dict: Response containing success status and message
466
+ """
467
+ config = self._convert_to_model(config, Config)
468
+ response = self._make_request(
469
+ "POST",
470
+ "/api/set_config",
471
+ json=config.model_dump(),
472
+ )
473
+ return response
474
+
475
+ def get_config(self) -> Config:
476
+ """Get configuration for the organization.
477
+
478
+ Returns:
479
+ Config: The current configuration
480
+ """
481
+ response = self._make_request(
482
+ "GET",
483
+ "/api/get_config",
484
+ )
485
+ return Config(**response)
486
+
487
+ def get_raw_feedbacks(
488
+ self,
489
+ request: Optional[Union[GetRawFeedbacksRequest, dict]] = None,
490
+ ) -> GetRawFeedbacksResponse:
491
+ """Get raw feedbacks.
492
+
493
+ Args:
494
+ request (Optional[Union[GetRawFeedbacksRequest, dict]]): The get request, defaults to empty request if None
495
+
496
+ Returns:
497
+ GetRawFeedbacksResponse: Response containing raw feedbacks
498
+ """
499
+ if request is None:
500
+ request = GetRawFeedbacksRequest()
501
+ else:
502
+ request = self._convert_to_model(request, GetRawFeedbacksRequest)
503
+ response = self._make_request(
504
+ "POST",
505
+ "/api/get_raw_feedbacks",
506
+ json=request.model_dump(),
507
+ )
508
+ return GetRawFeedbacksResponse(**response)
509
+
510
+ def get_feedbacks(
511
+ self,
512
+ request: Optional[Union[GetFeedbacksRequest, dict]] = None,
513
+ ) -> GetFeedbacksResponse:
514
+ """Get feedbacks.
515
+
516
+ Args:
517
+ request (Optional[Union[GetFeedbacksRequest, dict]]): The get request, defaults to empty request if None
518
+
519
+ Returns:
520
+ GetFeedbacksResponse: Response containing feedbacks
521
+ """
522
+ if request is None:
523
+ request = GetFeedbacksRequest()
524
+ else:
525
+ request = self._convert_to_model(request, GetFeedbacksRequest)
526
+ response = self._make_request(
527
+ "POST",
528
+ "/api/get_feedbacks",
529
+ json=request.model_dump(),
530
+ )
531
+ return GetFeedbacksResponse(**response)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "agenticmem"
3
- version = "0.1.2.4"
3
+ version = "0.1.3.8"
4
4
  description = "A Python client for the AgenticMem API"
5
5
  authors = ["AgenticMem Team"]
6
6
  readme = "README.md"
@@ -12,10 +12,11 @@ python = "^3.10"
12
12
  requests = "^2.25.0"
13
13
  pydantic = "^2.0.0"
14
14
  python-dateutil = "^2.8.0"
15
- agenticmem-commons = "0.1.3.6"
15
+ agenticmem-commons = "0.1.3.8"
16
16
  mkdocstrings = {version = ">=0.18.0", extras = ["python"]}
17
17
  griffe = "0.48.0"
18
18
  aiohttp = "^3.12.9"
19
+ mkdocs-with-pdf = "^0.9.3"
19
20
 
20
21
  [build-system]
21
22
  requires = ["poetry-core>=1.0.0"]
@@ -1,145 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: agenticmem
3
- Version: 0.1.2.4
4
- Summary: A Python client for the AgenticMem API
5
- License: MIT
6
- Author: AgenticMem Team
7
- Requires-Python: >=3.10,<4.0
8
- Classifier: License :: OSI Approved :: MIT License
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: 3.12
13
- Classifier: Programming Language :: Python :: 3.13
14
- Requires-Dist: agenticmem-commons (==0.1.3.6)
15
- Requires-Dist: aiohttp (>=3.12.9,<4.0.0)
16
- Requires-Dist: griffe (==0.48.0)
17
- Requires-Dist: mkdocstrings[python] (>=0.18.0)
18
- Requires-Dist: pydantic (>=2.0.0,<3.0.0)
19
- Requires-Dist: python-dateutil (>=2.8.0,<3.0.0)
20
- Requires-Dist: requests (>=2.25.0,<3.0.0)
21
- Description-Content-Type: text/markdown
22
-
23
- # AgenticMem Python Client
24
-
25
- A Python client library for interacting with the AgenticMem API. This client provides easy-to-use interfaces for managing user interactions and profiles.
26
-
27
- ## Installation
28
-
29
- ```bash
30
- pip install agenticmem
31
- ```
32
-
33
- ## Quick Start
34
-
35
- ```python
36
- from agenticmem import AgenticMemClient
37
- from agenticmem_commons.api_schema.service_schemas import InteractionRequest
38
- from agenticmem_commons.api_schema.retriever_schema import (
39
- SearchInteractionRequest,
40
- SearchUserProfileRequest,
41
- GetInteractionsRequest,
42
- GetUserProfilesRequest
43
- )
44
- from datetime import datetime
45
-
46
- # Initialize the client
47
- client = AgenticMemClient(api_key="your_api_key")
48
-
49
- # Optional: Login with email/password
50
- token = client.login(email="user@example.com", password="password123")
51
-
52
- # Publish a user interaction
53
- interaction = InteractionRequest(
54
- created_at=int(datetime.utcnow().timestamp()),
55
- content="User clicked on product X",
56
- user_action="click",
57
- user_action_description="Clicked on product details button"
58
- )
59
-
60
- response = client.publish_interaction(
61
- user_id="user123",
62
- request_id="req456",
63
- interaction_requests=[interaction]
64
- )
65
- print(f"Published interaction: {response.success} - {response.message}")
66
-
67
- # Search user profiles
68
- profiles_request = SearchUserProfileRequest(
69
- user_id="user123",
70
- search_query="recent interactions",
71
- top_k=5
72
- )
73
- profiles = client.search_profiles(profiles_request)
74
- for profile in profiles.profiles:
75
- print(f"Profile {profile.profile_id}: {profile.profile_content}")
76
-
77
- # Get user profiles directly
78
- profiles_request = GetUserProfilesRequest(
79
- user_id="user123"
80
- )
81
- profiles = client.get_profiles(profiles_request)
82
- for profile in profiles.profiles:
83
- print(f"Profile: {profile}")
84
-
85
- # Search interactions
86
- interactions_request = SearchInteractionRequest(
87
- user_id="user123",
88
- start_time=int(datetime(2024, 1, 1).timestamp()),
89
- end_time=int(datetime.utcnow().timestamp())
90
- )
91
- interactions = client.search_interactions(interactions_request)
92
- for interaction in interactions.interactions:
93
- print(f"Interaction {interaction.interaction_id}: {interaction.content}")
94
-
95
- # Get interactions directly
96
- interactions_request = GetInteractionsRequest(
97
- user_id="user123"
98
- )
99
- interactions = client.get_interactions(interactions_request)
100
- for interaction in interactions.interactions:
101
- print(f"Interaction: {interaction}")
102
-
103
- # Get profile change log
104
- change_log = client.get_profile_change_log()
105
- print(f"Profile changes: {change_log}")
106
- ```
107
-
108
- ## Features
109
-
110
- - Authentication
111
- - API key authentication
112
- - Email/password login
113
- - User interaction management
114
- - Publish user interactions
115
- - Delete specific interactions
116
- - Search interactions with time range and filters
117
- - Get direct list of interactions
118
- - User profile management
119
- - Search user profiles with customizable queries
120
- - Get direct list of user profiles
121
- - Delete specific profiles or profiles matching a search query
122
- - View profile change log history
123
-
124
- ## API Response Types
125
-
126
- All API methods return strongly-typed responses:
127
-
128
- - `login()` returns `Token`
129
- - `publish_interaction()` returns `PublishUserInteractionResponse`
130
- - `search_interactions()` returns `SearchInteractionResponse`
131
- - `get_interactions()` returns `GetInteractionsResponse`
132
- - `search_profiles()` returns `SearchUserProfileResponse`
133
- - `get_profiles()` returns `GetUserProfilesResponse`
134
- - `delete_profile()` returns `DeleteUserProfileResponse`
135
- - `delete_interaction()` returns `DeleteUserInteractionResponse`
136
- - `get_profile_change_log()` returns `ProfileChangeLogResponse`
137
-
138
- ## Documentation
139
-
140
- For detailed documentation, please visit [docs link].
141
-
142
- ## License
143
-
144
- MIT License
145
-
@@ -1,122 +0,0 @@
1
- # AgenticMem Python Client
2
-
3
- A Python client library for interacting with the AgenticMem API. This client provides easy-to-use interfaces for managing user interactions and profiles.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pip install agenticmem
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```python
14
- from agenticmem import AgenticMemClient
15
- from agenticmem_commons.api_schema.service_schemas import InteractionRequest
16
- from agenticmem_commons.api_schema.retriever_schema import (
17
- SearchInteractionRequest,
18
- SearchUserProfileRequest,
19
- GetInteractionsRequest,
20
- GetUserProfilesRequest
21
- )
22
- from datetime import datetime
23
-
24
- # Initialize the client
25
- client = AgenticMemClient(api_key="your_api_key")
26
-
27
- # Optional: Login with email/password
28
- token = client.login(email="user@example.com", password="password123")
29
-
30
- # Publish a user interaction
31
- interaction = InteractionRequest(
32
- created_at=int(datetime.utcnow().timestamp()),
33
- content="User clicked on product X",
34
- user_action="click",
35
- user_action_description="Clicked on product details button"
36
- )
37
-
38
- response = client.publish_interaction(
39
- user_id="user123",
40
- request_id="req456",
41
- interaction_requests=[interaction]
42
- )
43
- print(f"Published interaction: {response.success} - {response.message}")
44
-
45
- # Search user profiles
46
- profiles_request = SearchUserProfileRequest(
47
- user_id="user123",
48
- search_query="recent interactions",
49
- top_k=5
50
- )
51
- profiles = client.search_profiles(profiles_request)
52
- for profile in profiles.profiles:
53
- print(f"Profile {profile.profile_id}: {profile.profile_content}")
54
-
55
- # Get user profiles directly
56
- profiles_request = GetUserProfilesRequest(
57
- user_id="user123"
58
- )
59
- profiles = client.get_profiles(profiles_request)
60
- for profile in profiles.profiles:
61
- print(f"Profile: {profile}")
62
-
63
- # Search interactions
64
- interactions_request = SearchInteractionRequest(
65
- user_id="user123",
66
- start_time=int(datetime(2024, 1, 1).timestamp()),
67
- end_time=int(datetime.utcnow().timestamp())
68
- )
69
- interactions = client.search_interactions(interactions_request)
70
- for interaction in interactions.interactions:
71
- print(f"Interaction {interaction.interaction_id}: {interaction.content}")
72
-
73
- # Get interactions directly
74
- interactions_request = GetInteractionsRequest(
75
- user_id="user123"
76
- )
77
- interactions = client.get_interactions(interactions_request)
78
- for interaction in interactions.interactions:
79
- print(f"Interaction: {interaction}")
80
-
81
- # Get profile change log
82
- change_log = client.get_profile_change_log()
83
- print(f"Profile changes: {change_log}")
84
- ```
85
-
86
- ## Features
87
-
88
- - Authentication
89
- - API key authentication
90
- - Email/password login
91
- - User interaction management
92
- - Publish user interactions
93
- - Delete specific interactions
94
- - Search interactions with time range and filters
95
- - Get direct list of interactions
96
- - User profile management
97
- - Search user profiles with customizable queries
98
- - Get direct list of user profiles
99
- - Delete specific profiles or profiles matching a search query
100
- - View profile change log history
101
-
102
- ## API Response Types
103
-
104
- All API methods return strongly-typed responses:
105
-
106
- - `login()` returns `Token`
107
- - `publish_interaction()` returns `PublishUserInteractionResponse`
108
- - `search_interactions()` returns `SearchInteractionResponse`
109
- - `get_interactions()` returns `GetInteractionsResponse`
110
- - `search_profiles()` returns `SearchUserProfileResponse`
111
- - `get_profiles()` returns `GetUserProfilesResponse`
112
- - `delete_profile()` returns `DeleteUserProfileResponse`
113
- - `delete_interaction()` returns `DeleteUserInteractionResponse`
114
- - `get_profile_change_log()` returns `ProfileChangeLogResponse`
115
-
116
- ## Documentation
117
-
118
- For detailed documentation, please visit [docs link].
119
-
120
- ## License
121
-
122
- MIT License
@@ -1,359 +0,0 @@
1
- from agenticmem_commons import IS_TEST_ENV
2
- import aiohttp
3
- from urllib.parse import urljoin
4
- import requests
5
- from typing import Optional, Union
6
- from agenticmem_commons.api_schema.retriever_schema import (
7
- SearchInteractionRequest,
8
- SearchInteractionResponse,
9
- SearchUserProfileRequest,
10
- SearchUserProfileResponse,
11
- GetInteractionsRequest,
12
- GetInteractionsResponse,
13
- GetUserProfilesRequest,
14
- GetUserProfilesResponse,
15
- GetRawFeedbacksRequest,
16
- GetRawFeedbacksResponse,
17
- GetFeedbacksRequest,
18
- GetFeedbacksResponse,
19
- )
20
-
21
- if IS_TEST_ENV:
22
- BACKEND_URL = "http://127.0.0.1:8000" # Local server for testing
23
- else:
24
- BACKEND_URL = "http://agenticmem-test.us-west-2.elasticbeanstalk.com:8081" # Elastic Beanstalk server url
25
-
26
- from agenticmem_commons.api_schema.service_schemas import (
27
- InteractionRequest,
28
- ProfileChangeLogResponse,
29
- PublishUserInteractionRequest,
30
- PublishUserInteractionResponse,
31
- DeleteUserProfileRequest,
32
- DeleteUserProfileResponse,
33
- DeleteUserInteractionRequest,
34
- DeleteUserInteractionResponse,
35
- )
36
- from agenticmem_commons.api_schema.login_schema import Token
37
- from agenticmem_commons.config_schema import Config
38
-
39
-
40
- class AgenticMemClient:
41
- """Client for interacting with the AgenticMem API."""
42
-
43
- def __init__(self, api_key: str = "", url_endpoint: str = ""):
44
- """Initialize the AgenticMem client.
45
-
46
- Args:
47
- api_key (str): Your API key for authentication
48
- """
49
- self.api_key = api_key
50
- self.base_url = url_endpoint if url_endpoint else BACKEND_URL
51
- self.session = requests.Session()
52
-
53
- async def _make_async_request(
54
- self, method: str, endpoint: str, headers: Optional[dict] = None, **kwargs
55
- ):
56
- """Make an async HTTP request to the API."""
57
- url = urljoin(self.base_url, endpoint)
58
-
59
- headers = headers or {}
60
- if self.api_key:
61
- headers.update(
62
- {
63
- "Authorization": f"Bearer {self.api_key}",
64
- "Content-Type": "application/json",
65
- }
66
- )
67
-
68
- async with aiohttp.ClientSession() as async_session:
69
- response = await async_session.request(
70
- method, url, headers=headers, **kwargs
71
- )
72
- response.raise_for_status()
73
- return await response.json()
74
-
75
- def _make_request(
76
- self, method: str, endpoint: str, headers: Optional[dict] = None, **kwargs
77
- ):
78
- """Make an HTTP request to the API.
79
-
80
- Args:
81
- method (str): HTTP method (GET, POST, DELETE)
82
- endpoint (str): API endpoint
83
- headers (dict, optional): Additional headers to include in the request
84
- **kwargs: Additional arguments to pass to requests
85
-
86
- Returns:
87
- dict: API response
88
- """
89
- url = urljoin(self.base_url, endpoint)
90
- if self.api_key:
91
- self.session.headers.update(
92
- {
93
- "Authorization": f"Bearer {self.api_key}",
94
- "Content-Type": "application/json",
95
- }
96
- )
97
- if headers:
98
- self.session.headers.update(headers)
99
- response = self.session.request(method, url, **kwargs)
100
- response.raise_for_status()
101
- return response.json()
102
-
103
- async def login(self, email: str, password: str) -> Token:
104
- """Async login to the AgenticMem API."""
105
- response = await self._make_async_request(
106
- "POST",
107
- "/token",
108
- data={"username": email, "password": password},
109
- headers={
110
- "Content-Type": "application/x-www-form-urlencoded",
111
- "accept": "application/json",
112
- },
113
- )
114
- return Token(**response)
115
-
116
- async def publish_interaction(
117
- self,
118
- request_id: str,
119
- user_id: str,
120
- interaction_requests: list[Union[InteractionRequest, dict]],
121
- source: str = "",
122
- agent_version: str = "",
123
- ) -> PublishUserInteractionResponse:
124
- """Publish user interactions.
125
-
126
- Args:
127
- request_id (str): The request ID
128
- user_id (str): The user ID
129
- interaction_requests (List[InteractionRequest]): List of interaction requests
130
- source (str, optional): The source of the interaction
131
- Returns:
132
- PublishUserInteractionResponse: Response containing success status and message
133
- """
134
- interaction_requests = [
135
- (
136
- InteractionRequest(**interaction_request)
137
- if isinstance(interaction_request, dict)
138
- else interaction_request
139
- )
140
- for interaction_request in interaction_requests
141
- ]
142
- request = PublishUserInteractionRequest(
143
- request_id=request_id,
144
- user_id=user_id,
145
- interaction_requests=interaction_requests,
146
- source=source,
147
- agent_version=agent_version,
148
- )
149
- response = await self._make_async_request(
150
- "POST",
151
- "/api/publish_interaction",
152
- json=request.model_dump(),
153
- )
154
- return PublishUserInteractionResponse(**response)
155
-
156
- async def search_interactions(
157
- self,
158
- request: Union[SearchInteractionRequest, dict],
159
- ) -> SearchInteractionResponse:
160
- """Search for user interactions.
161
-
162
- Args:
163
- request (SearchInteractionRequest): The search request
164
-
165
- Returns:
166
- SearchInteractionResponse: Response containing matching interactions
167
- """
168
- if isinstance(request, dict):
169
- request = SearchInteractionRequest(**request)
170
- response = await self._make_async_request(
171
- "POST",
172
- "/api/search_interactions",
173
- json=request.model_dump(),
174
- )
175
- return SearchInteractionResponse(**response)
176
-
177
- async def search_profiles(
178
- self,
179
- request: Union[SearchUserProfileRequest, dict],
180
- ) -> SearchUserProfileResponse:
181
- """Search for user profiles.
182
-
183
- Args:
184
- request (SearchUserProfileRequest): The search request
185
-
186
- Returns:
187
- SearchUserProfileResponse: Response containing matching profiles
188
- """
189
- if isinstance(request, dict):
190
- request = SearchUserProfileRequest(**request)
191
- response = await self._make_async_request(
192
- "POST", "/api/search_profiles", json=request.model_dump()
193
- )
194
- return SearchUserProfileResponse(**response)
195
-
196
- async def delete_profile(
197
- self, user_id: str, profile_id: str = "", search_query: str = ""
198
- ) -> DeleteUserProfileResponse:
199
- """Delete user profiles.
200
-
201
- Args:
202
- user_id (str): The user ID
203
- profile_id (str, optional): Specific profile ID to delete
204
- search_query (str, optional): Query to match profiles for deletion
205
-
206
- Returns:
207
- DeleteUserProfileResponse: Response containing success status and message
208
- """
209
- request = DeleteUserProfileRequest(
210
- user_id=user_id,
211
- profile_id=profile_id,
212
- search_query=search_query,
213
- )
214
- response = await self._make_async_request(
215
- "DELETE", "/api/delete_profile", json=request.model_dump()
216
- )
217
- return DeleteUserProfileResponse(**response)
218
-
219
- async def delete_interaction(
220
- self, user_id: str, interaction_id: str
221
- ) -> DeleteUserInteractionResponse:
222
- """Delete a user interaction.
223
-
224
- Args:
225
- user_id (str): The user ID
226
- interaction_id (str): The interaction ID to delete
227
-
228
- Returns:
229
- DeleteUserInteractionResponse: Response containing success status and message
230
- """
231
- request = DeleteUserInteractionRequest(
232
- user_id=user_id, interaction_id=interaction_id
233
- )
234
- response = await self._make_async_request(
235
- "DELETE", "/api/delete_interaction", json=request.model_dump()
236
- )
237
- return DeleteUserInteractionResponse(**response)
238
-
239
- async def get_profile_change_log(self) -> ProfileChangeLogResponse:
240
- response = await self._make_async_request("GET", "/api/profile_change_log")
241
- return ProfileChangeLogResponse(**response)
242
-
243
- async def get_interactions(
244
- self,
245
- request: Union[GetInteractionsRequest, dict],
246
- ) -> GetInteractionsResponse:
247
- """Get user interactions.
248
-
249
- Args:
250
- request (GetInteractionsRequest): The list request
251
-
252
- Returns:
253
- GetInteractionsResponse: Response containing list of interactions
254
- """
255
- if isinstance(request, dict):
256
- request = GetInteractionsRequest(**request)
257
- response = await self._make_async_request(
258
- "POST",
259
- "/api/get_interactions",
260
- json=request.model_dump(),
261
- )
262
- return GetInteractionsResponse(**response)
263
-
264
- async def get_profiles(
265
- self,
266
- request: Union[GetUserProfilesRequest, dict],
267
- ) -> GetUserProfilesResponse:
268
- """Get user profiles.
269
-
270
- Args:
271
- request (GetUserProfilesRequest): The list request
272
-
273
- Returns:
274
- GetUserProfilesResponse: Response containing list of profiles
275
- """
276
- if isinstance(request, dict):
277
- request = GetUserProfilesRequest(**request)
278
- response = await self._make_async_request(
279
- "POST",
280
- "/api/get_profiles",
281
- json=request.model_dump(),
282
- )
283
- return GetUserProfilesResponse(**response)
284
-
285
- async def set_config(self, config: Union[Config, dict]) -> dict:
286
- """Set configuration for the organization.
287
-
288
- Args:
289
- config (Union[Config, dict]): The configuration to set
290
-
291
- Returns:
292
- dict: Response containing success status and message
293
- """
294
- if isinstance(config, dict):
295
- config = Config(**config)
296
- response = await self._make_async_request(
297
- "POST",
298
- "/api/set_config",
299
- json=config.model_dump(),
300
- )
301
- return response
302
-
303
- async def get_config(self) -> Config:
304
- """Get configuration for the organization.
305
-
306
- Returns:
307
- Config: The current configuration
308
- """
309
- response = await self._make_async_request(
310
- "GET",
311
- "/api/get_config",
312
- )
313
- return Config(**response)
314
-
315
- async def get_raw_feedbacks(
316
- self,
317
- request: Optional[Union[GetRawFeedbacksRequest, dict]] = None,
318
- ) -> GetRawFeedbacksResponse:
319
- """Get raw feedbacks.
320
-
321
- Args:
322
- request (Optional[Union[GetRawFeedbacksRequest, dict]]): The get request, defaults to empty request if None
323
-
324
- Returns:
325
- GetRawFeedbacksResponse: Response containing raw feedbacks
326
- """
327
- if request is None:
328
- request = GetRawFeedbacksRequest()
329
- elif isinstance(request, dict):
330
- request = GetRawFeedbacksRequest(**request)
331
- response = await self._make_async_request(
332
- "POST",
333
- "/api/get_raw_feedbacks",
334
- json=request.model_dump(),
335
- )
336
- return GetRawFeedbacksResponse(**response)
337
-
338
- async def get_feedbacks(
339
- self,
340
- request: Optional[Union[GetFeedbacksRequest, dict]] = None,
341
- ) -> GetFeedbacksResponse:
342
- """Get feedbacks.
343
-
344
- Args:
345
- request (Optional[Union[GetFeedbacksRequest, dict]]): The get request, defaults to empty request if None
346
-
347
- Returns:
348
- GetFeedbacksResponse: Response containing feedbacks
349
- """
350
- if request is None:
351
- request = GetFeedbacksRequest()
352
- elif isinstance(request, dict):
353
- request = GetFeedbacksRequest(**request)
354
- response = await self._make_async_request(
355
- "POST",
356
- "/api/get_feedbacks",
357
- json=request.model_dump(),
358
- )
359
- return GetFeedbacksResponse(**response)