hindsight-api 0.1.13__py3-none-any.whl → 0.1.15__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.
- hindsight_api/api/http.py +149 -23
- hindsight_api/engine/memory_engine.py +7 -0
- hindsight_api/engine/retain/orchestrator.py +54 -1
- {hindsight_api-0.1.13.dist-info → hindsight_api-0.1.15.dist-info}/METADATA +1 -1
- {hindsight_api-0.1.13.dist-info → hindsight_api-0.1.15.dist-info}/RECORD +7 -7
- {hindsight_api-0.1.13.dist-info → hindsight_api-0.1.15.dist-info}/WHEEL +0 -0
- {hindsight_api-0.1.13.dist-info → hindsight_api-0.1.15.dist-info}/entry_points.txt +0 -0
hindsight_api/api/http.py
CHANGED
|
@@ -689,6 +689,26 @@ class DocumentResponse(BaseModel):
|
|
|
689
689
|
memory_unit_count: int
|
|
690
690
|
|
|
691
691
|
|
|
692
|
+
class DeleteDocumentResponse(BaseModel):
|
|
693
|
+
"""Response model for delete document endpoint."""
|
|
694
|
+
|
|
695
|
+
model_config = ConfigDict(
|
|
696
|
+
json_schema_extra={
|
|
697
|
+
"example": {
|
|
698
|
+
"success": True,
|
|
699
|
+
"message": "Document 'session_1' and 5 associated memory units deleted successfully",
|
|
700
|
+
"document_id": "session_1",
|
|
701
|
+
"memory_units_deleted": 5,
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
success: bool
|
|
707
|
+
message: str
|
|
708
|
+
document_id: str
|
|
709
|
+
memory_units_deleted: int
|
|
710
|
+
|
|
711
|
+
|
|
692
712
|
class ChunkResponse(BaseModel):
|
|
693
713
|
"""Response model for get chunk endpoint."""
|
|
694
714
|
|
|
@@ -725,6 +745,108 @@ class DeleteResponse(BaseModel):
|
|
|
725
745
|
deleted_count: int | None = None
|
|
726
746
|
|
|
727
747
|
|
|
748
|
+
class BankStatsResponse(BaseModel):
|
|
749
|
+
"""Response model for bank statistics endpoint."""
|
|
750
|
+
|
|
751
|
+
model_config = ConfigDict(
|
|
752
|
+
json_schema_extra={
|
|
753
|
+
"example": {
|
|
754
|
+
"bank_id": "user123",
|
|
755
|
+
"total_nodes": 150,
|
|
756
|
+
"total_links": 300,
|
|
757
|
+
"total_documents": 10,
|
|
758
|
+
"nodes_by_fact_type": {"fact": 100, "preference": 30, "observation": 20},
|
|
759
|
+
"links_by_link_type": {"temporal": 150, "semantic": 100, "entity": 50},
|
|
760
|
+
"links_by_fact_type": {"fact": 200, "preference": 60, "observation": 40},
|
|
761
|
+
"links_breakdown": {"fact": {"temporal": 100, "semantic": 60, "entity": 40}},
|
|
762
|
+
"pending_operations": 2,
|
|
763
|
+
"failed_operations": 0,
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
bank_id: str
|
|
769
|
+
total_nodes: int
|
|
770
|
+
total_links: int
|
|
771
|
+
total_documents: int
|
|
772
|
+
nodes_by_fact_type: dict[str, int]
|
|
773
|
+
links_by_link_type: dict[str, int]
|
|
774
|
+
links_by_fact_type: dict[str, int]
|
|
775
|
+
links_breakdown: dict[str, dict[str, int]]
|
|
776
|
+
pending_operations: int
|
|
777
|
+
failed_operations: int
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
class OperationResponse(BaseModel):
|
|
781
|
+
"""Response model for a single async operation."""
|
|
782
|
+
|
|
783
|
+
model_config = ConfigDict(
|
|
784
|
+
json_schema_extra={
|
|
785
|
+
"example": {
|
|
786
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
787
|
+
"task_type": "retain",
|
|
788
|
+
"items_count": 5,
|
|
789
|
+
"document_id": "meeting-notes-2024",
|
|
790
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
791
|
+
"status": "pending",
|
|
792
|
+
"error_message": None,
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
id: str
|
|
798
|
+
task_type: str
|
|
799
|
+
items_count: int
|
|
800
|
+
document_id: str | None
|
|
801
|
+
created_at: str
|
|
802
|
+
status: str
|
|
803
|
+
error_message: str | None
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
class OperationsListResponse(BaseModel):
|
|
807
|
+
"""Response model for list operations endpoint."""
|
|
808
|
+
|
|
809
|
+
model_config = ConfigDict(
|
|
810
|
+
json_schema_extra={
|
|
811
|
+
"example": {
|
|
812
|
+
"bank_id": "user123",
|
|
813
|
+
"operations": [
|
|
814
|
+
{
|
|
815
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
816
|
+
"task_type": "retain",
|
|
817
|
+
"items_count": 5,
|
|
818
|
+
"document_id": None,
|
|
819
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
820
|
+
"status": "pending",
|
|
821
|
+
"error_message": None,
|
|
822
|
+
}
|
|
823
|
+
],
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
bank_id: str
|
|
829
|
+
operations: list[OperationResponse]
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
class CancelOperationResponse(BaseModel):
|
|
833
|
+
"""Response model for cancel operation endpoint."""
|
|
834
|
+
|
|
835
|
+
model_config = ConfigDict(
|
|
836
|
+
json_schema_extra={
|
|
837
|
+
"example": {
|
|
838
|
+
"success": True,
|
|
839
|
+
"message": "Operation 550e8400-e29b-41d4-a716-446655440000 cancelled",
|
|
840
|
+
"operation_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
success: bool
|
|
846
|
+
message: str
|
|
847
|
+
operation_id: str
|
|
848
|
+
|
|
849
|
+
|
|
728
850
|
def create_app(
|
|
729
851
|
memory: MemoryEngine,
|
|
730
852
|
initialize_memory: bool = True,
|
|
@@ -1142,6 +1264,7 @@ def _register_routes(app: FastAPI):
|
|
|
1142
1264
|
|
|
1143
1265
|
@app.get(
|
|
1144
1266
|
"/v1/default/banks/{bank_id}/stats",
|
|
1267
|
+
response_model=BankStatsResponse,
|
|
1145
1268
|
summary="Get statistics for memory bank",
|
|
1146
1269
|
description="Get statistics about nodes and links for a specific agent",
|
|
1147
1270
|
operation_id="get_agent_stats",
|
|
@@ -1242,18 +1365,18 @@ def _register_routes(app: FastAPI):
|
|
|
1242
1365
|
total_nodes = sum(nodes_by_type.values())
|
|
1243
1366
|
total_links = sum(links_by_type.values())
|
|
1244
1367
|
|
|
1245
|
-
return
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1368
|
+
return BankStatsResponse(
|
|
1369
|
+
bank_id=bank_id,
|
|
1370
|
+
total_nodes=total_nodes,
|
|
1371
|
+
total_links=total_links,
|
|
1372
|
+
total_documents=total_documents,
|
|
1373
|
+
nodes_by_fact_type=nodes_by_type,
|
|
1374
|
+
links_by_link_type=links_by_type,
|
|
1375
|
+
links_by_fact_type=links_by_fact_type,
|
|
1376
|
+
links_breakdown=links_breakdown,
|
|
1377
|
+
pending_operations=pending_operations,
|
|
1378
|
+
failed_operations=failed_operations,
|
|
1379
|
+
)
|
|
1257
1380
|
|
|
1258
1381
|
except Exception as e:
|
|
1259
1382
|
import traceback
|
|
@@ -1477,6 +1600,7 @@ def _register_routes(app: FastAPI):
|
|
|
1477
1600
|
|
|
1478
1601
|
@app.delete(
|
|
1479
1602
|
"/v1/default/banks/{bank_id}/documents/{document_id}",
|
|
1603
|
+
response_model=DeleteDocumentResponse,
|
|
1480
1604
|
summary="Delete a document",
|
|
1481
1605
|
description="Delete a document and all its associated memory units and links.\n\n"
|
|
1482
1606
|
"This will cascade delete:\n"
|
|
@@ -1503,12 +1627,12 @@ def _register_routes(app: FastAPI):
|
|
|
1503
1627
|
if result["document_deleted"] == 0:
|
|
1504
1628
|
raise HTTPException(status_code=404, detail="Document not found")
|
|
1505
1629
|
|
|
1506
|
-
return
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1630
|
+
return DeleteDocumentResponse(
|
|
1631
|
+
success=True,
|
|
1632
|
+
message=f"Document '{document_id}' and {result['memory_units_deleted']} associated memory units deleted successfully",
|
|
1633
|
+
document_id=document_id,
|
|
1634
|
+
memory_units_deleted=result["memory_units_deleted"],
|
|
1635
|
+
)
|
|
1512
1636
|
except HTTPException:
|
|
1513
1637
|
raise
|
|
1514
1638
|
except Exception as e:
|
|
@@ -1520,6 +1644,7 @@ def _register_routes(app: FastAPI):
|
|
|
1520
1644
|
|
|
1521
1645
|
@app.get(
|
|
1522
1646
|
"/v1/default/banks/{bank_id}/operations",
|
|
1647
|
+
response_model=OperationsListResponse,
|
|
1523
1648
|
summary="List async operations",
|
|
1524
1649
|
description="Get a list of all async operations (pending and failed) for a specific agent, including error messages for failed operations",
|
|
1525
1650
|
operation_id="list_operations",
|
|
@@ -1529,10 +1654,10 @@ def _register_routes(app: FastAPI):
|
|
|
1529
1654
|
"""List all async operations (pending and failed) for a memory bank."""
|
|
1530
1655
|
try:
|
|
1531
1656
|
operations = await app.state.memory.list_operations(bank_id, request_context=request_context)
|
|
1532
|
-
return
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1657
|
+
return OperationsListResponse(
|
|
1658
|
+
bank_id=bank_id,
|
|
1659
|
+
operations=[OperationResponse(**op) for op in operations],
|
|
1660
|
+
)
|
|
1536
1661
|
except Exception as e:
|
|
1537
1662
|
import traceback
|
|
1538
1663
|
|
|
@@ -1542,6 +1667,7 @@ def _register_routes(app: FastAPI):
|
|
|
1542
1667
|
|
|
1543
1668
|
@app.delete(
|
|
1544
1669
|
"/v1/default/banks/{bank_id}/operations/{operation_id}",
|
|
1670
|
+
response_model=CancelOperationResponse,
|
|
1545
1671
|
summary="Cancel a pending async operation",
|
|
1546
1672
|
description="Cancel a pending async operation by removing it from the queue",
|
|
1547
1673
|
operation_id="cancel_operation",
|
|
@@ -1559,7 +1685,7 @@ def _register_routes(app: FastAPI):
|
|
|
1559
1685
|
raise HTTPException(status_code=400, detail=f"Invalid operation_id format: {operation_id}")
|
|
1560
1686
|
|
|
1561
1687
|
result = await app.state.memory.cancel_operation(bank_id, operation_id, request_context=request_context)
|
|
1562
|
-
return result
|
|
1688
|
+
return CancelOperationResponse(**result)
|
|
1563
1689
|
except ValueError as e:
|
|
1564
1690
|
raise HTTPException(status_code=404, detail=str(e))
|
|
1565
1691
|
except Exception as e:
|
|
@@ -695,7 +695,14 @@ class MemoryEngine(MemoryEngineInterface):
|
|
|
695
695
|
|
|
696
696
|
Returns:
|
|
697
697
|
dict with status and optional error message
|
|
698
|
+
|
|
699
|
+
Note:
|
|
700
|
+
Returns unhealthy until initialize() has completed successfully.
|
|
698
701
|
"""
|
|
702
|
+
# Not healthy until fully initialized
|
|
703
|
+
if not self._initialized:
|
|
704
|
+
return {"status": "unhealthy", "reason": "not_initialized"}
|
|
705
|
+
|
|
699
706
|
try:
|
|
700
707
|
pool = await self._get_pool()
|
|
701
708
|
async with pool.acquire() as conn:
|
|
@@ -106,9 +106,62 @@ async def retain_batch(
|
|
|
106
106
|
)
|
|
107
107
|
|
|
108
108
|
if not extracted_facts:
|
|
109
|
+
# Still need to create document if document_id was provided
|
|
110
|
+
async with acquire_with_retry(pool) as conn:
|
|
111
|
+
async with conn.transaction():
|
|
112
|
+
await fact_storage.ensure_bank_exists(conn, bank_id)
|
|
113
|
+
|
|
114
|
+
# Handle document tracking even with no facts
|
|
115
|
+
if document_id:
|
|
116
|
+
combined_content = "\n".join([c.get("content", "") for c in contents_dicts])
|
|
117
|
+
retain_params = {}
|
|
118
|
+
if contents_dicts:
|
|
119
|
+
first_item = contents_dicts[0]
|
|
120
|
+
if first_item.get("context"):
|
|
121
|
+
retain_params["context"] = first_item["context"]
|
|
122
|
+
if first_item.get("event_date"):
|
|
123
|
+
retain_params["event_date"] = (
|
|
124
|
+
first_item["event_date"].isoformat()
|
|
125
|
+
if hasattr(first_item["event_date"], "isoformat")
|
|
126
|
+
else str(first_item["event_date"])
|
|
127
|
+
)
|
|
128
|
+
if first_item.get("metadata"):
|
|
129
|
+
retain_params["metadata"] = first_item["metadata"]
|
|
130
|
+
await fact_storage.handle_document_tracking(
|
|
131
|
+
conn, bank_id, document_id, combined_content, is_first_batch, retain_params
|
|
132
|
+
)
|
|
133
|
+
else:
|
|
134
|
+
# Check for per-item document_ids
|
|
135
|
+
from collections import defaultdict
|
|
136
|
+
|
|
137
|
+
contents_by_doc = defaultdict(list)
|
|
138
|
+
for idx, content_dict in enumerate(contents_dicts):
|
|
139
|
+
doc_id = content_dict.get("document_id")
|
|
140
|
+
if doc_id:
|
|
141
|
+
contents_by_doc[doc_id].append((idx, content_dict))
|
|
142
|
+
|
|
143
|
+
for doc_id, doc_contents in contents_by_doc.items():
|
|
144
|
+
combined_content = "\n".join([c.get("content", "") for _, c in doc_contents])
|
|
145
|
+
retain_params = {}
|
|
146
|
+
if doc_contents:
|
|
147
|
+
first_item = doc_contents[0][1]
|
|
148
|
+
if first_item.get("context"):
|
|
149
|
+
retain_params["context"] = first_item["context"]
|
|
150
|
+
if first_item.get("event_date"):
|
|
151
|
+
retain_params["event_date"] = (
|
|
152
|
+
first_item["event_date"].isoformat()
|
|
153
|
+
if hasattr(first_item["event_date"], "isoformat")
|
|
154
|
+
else str(first_item["event_date"])
|
|
155
|
+
)
|
|
156
|
+
if first_item.get("metadata"):
|
|
157
|
+
retain_params["metadata"] = first_item["metadata"]
|
|
158
|
+
await fact_storage.handle_document_tracking(
|
|
159
|
+
conn, bank_id, doc_id, combined_content, is_first_batch, retain_params
|
|
160
|
+
)
|
|
161
|
+
|
|
109
162
|
total_time = time.time() - start_time
|
|
110
163
|
logger.info(
|
|
111
|
-
f"RETAIN_BATCH COMPLETE: 0 facts extracted from {len(contents)} contents in {total_time:.3f}s (
|
|
164
|
+
f"RETAIN_BATCH COMPLETE: 0 facts extracted from {len(contents)} contents in {total_time:.3f}s (document tracked, no facts)"
|
|
112
165
|
)
|
|
113
166
|
return [[] for _ in contents]
|
|
114
167
|
|
|
@@ -19,7 +19,7 @@ hindsight_api/alembic/versions/d9f6a3b4c5e2_rename_bank_to_interactions.py,sha25
|
|
|
19
19
|
hindsight_api/alembic/versions/e0a1b2c3d4e5_disposition_to_3_traits.py,sha256=IdDP6fgsYj5fCXAF0QT-3t_wcKJsnf7B0mh7qS-cf_w,3806
|
|
20
20
|
hindsight_api/alembic/versions/rename_personality_to_disposition.py,sha256=A29-nDJ2Re4u9jdp2sUw29It808j4h6BpcA4wDHJMJ8,2765
|
|
21
21
|
hindsight_api/api/__init__.py,sha256=zoDWA86ttx-UriC35UIgdPswIrau7GuMWTN63wYsUdM,2916
|
|
22
|
-
hindsight_api/api/http.py,sha256=
|
|
22
|
+
hindsight_api/api/http.py,sha256=M3mJIfqILPe5kC3iO2Ds11XmVPD5n_SuUSF3s8mUmhM,76049
|
|
23
23
|
hindsight_api/api/mcp.py,sha256=Iowo3ourjWx7ZqLiCwF9nvjMAJpRceBprF5cgn5M6fs,7853
|
|
24
24
|
hindsight_api/engine/__init__.py,sha256=-BwaSwG9fTT_BBO0c_2MBkxG6-tGdclSzIqsgHw4cnw,1633
|
|
25
25
|
hindsight_api/engine/cross_encoder.py,sha256=5WmUx9yfJdIwZ0nA218O-mMKQJ7EKaPOtwhMiDbG8KQ,10483
|
|
@@ -28,7 +28,7 @@ hindsight_api/engine/embeddings.py,sha256=IEdP5-p6oTJRRKV2JzUEojByJGShUEmkInCyA9
|
|
|
28
28
|
hindsight_api/engine/entity_resolver.py,sha256=f-fbUDKCrM9a5Sz10J0rW3jV7dib7BmpyGyassspKXg,23510
|
|
29
29
|
hindsight_api/engine/interface.py,sha256=F6BgnjloH7EgL9_D2NpPuabR_zR-h_iEJBQ0ERC2P58,16090
|
|
30
30
|
hindsight_api/engine/llm_wrapper.py,sha256=nLdVAk2xtkbwxLFMQNmEU-JmHucdtQoh3ph0BWX4sDc,29140
|
|
31
|
-
hindsight_api/engine/memory_engine.py,sha256=
|
|
31
|
+
hindsight_api/engine/memory_engine.py,sha256=wQESgZ49srxgbpQTfvAM5BDp4-st1NGhiqktj753qaA,167050
|
|
32
32
|
hindsight_api/engine/query_analyzer.py,sha256=DKFxmyyVVc59zwKbbGx4D22UVp6TxmD7jAa7cg9FGSU,19641
|
|
33
33
|
hindsight_api/engine/response_models.py,sha256=QeESHC7oh84SYPDrR6FqHjiGBZnTAzo61IDB-qwVTSY,8737
|
|
34
34
|
hindsight_api/engine/task_backend.py,sha256=txtcMUzHW1MigDCW7XsVZc5zqvM9FbR_xF_c9BKokBk,8054
|
|
@@ -45,7 +45,7 @@ hindsight_api/engine/retain/fact_storage.py,sha256=zhIiccW1D4wkgnZMFcbxDeMeHy5v4
|
|
|
45
45
|
hindsight_api/engine/retain/link_creation.py,sha256=KP2kGU2VCymJptgw0hjaSdsjvncBgNp3P_A4OB_qx-w,3082
|
|
46
46
|
hindsight_api/engine/retain/link_utils.py,sha256=w8n_pPzs_rd3EMkb7nv4k_qSZttAKDig93hSSjl-Xbc,32854
|
|
47
47
|
hindsight_api/engine/retain/observation_regeneration.py,sha256=qE1-iSyH0lh5Zab1XIwSQSpxEArdOJOAC_yJY5iHLMQ,8143
|
|
48
|
-
hindsight_api/engine/retain/orchestrator.py,sha256=
|
|
48
|
+
hindsight_api/engine/retain/orchestrator.py,sha256=9l27-IMemx4Wrym2HJ59DSUuh6NqMpTs0wqul2dwUzA,20631
|
|
49
49
|
hindsight_api/engine/retain/types.py,sha256=UzCXauLrMD26g5oZK3_oQ-gTaSSsd-Ttjh17le64HH4,6898
|
|
50
50
|
hindsight_api/engine/search/__init__.py,sha256=YPz_4g7IOabx078Xwg3RBfbOpJ649NRwNfe0gTI9P1U,802
|
|
51
51
|
hindsight_api/engine/search/fusion.py,sha256=cY81BH9U5RyWrPXbQnrDBghtelDMckZWCke9aqMyNnQ,4220
|
|
@@ -69,7 +69,7 @@ hindsight_api/extensions/operation_validator.py,sha256=zQPD8pTMJJxQjpByxa4JxvGgD
|
|
|
69
69
|
hindsight_api/extensions/tenant.py,sha256=gvngBMn3cJtUfd4P0P_288faNJq00T8zPQkeldEsD3g,1903
|
|
70
70
|
hindsight_api/extensions/builtin/__init__.py,sha256=hLx2oFYZ1JtZhTWfab6AYcR02SWP2gIdbEqnZezT8ek,526
|
|
71
71
|
hindsight_api/extensions/builtin/tenant.py,sha256=lsS0GDEUXmfPBzqhqk2FpN4Z_k5cA3Y3PFNYyiiuZjU,1444
|
|
72
|
-
hindsight_api-0.1.
|
|
73
|
-
hindsight_api-0.1.
|
|
74
|
-
hindsight_api-0.1.
|
|
75
|
-
hindsight_api-0.1.
|
|
72
|
+
hindsight_api-0.1.15.dist-info/METADATA,sha256=uHr_iivfQRS-yIhjQoVNh8CbvaRUTLjHGFsYdQatopg,5408
|
|
73
|
+
hindsight_api-0.1.15.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
74
|
+
hindsight_api-0.1.15.dist-info/entry_points.txt,sha256=vqZv5WLHbSx8vyec5RtMlUqtE_ul7DTgEVODSmou6Og,109
|
|
75
|
+
hindsight_api-0.1.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|