This commit is contained in:
+10
-5
@@ -124,6 +124,11 @@ class MemoryCreate(BaseModel):
|
|||||||
prompt: Optional[str] = Field(None, description="Custom prompt to use for fact extraction.")
|
prompt: Optional[str] = Field(None, description="Custom prompt to use for fact extraction.")
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryUpdate(BaseModel):
|
||||||
|
text: str = Field(..., description="New content to update the memory with.")
|
||||||
|
metadata: Optional[Dict[str, Any]] = Field(None, description="Metadata to update.")
|
||||||
|
|
||||||
|
|
||||||
class SearchRequest(BaseModel):
|
class SearchRequest(BaseModel):
|
||||||
query: str = Field(..., description="Search query.")
|
query: str = Field(..., description="Search query.")
|
||||||
user_id: Optional[str] = None
|
user_id: Optional[str] = None
|
||||||
@@ -199,18 +204,18 @@ def search_memories(search_req: SearchRequest, _api_key: Optional[str] = Depends
|
|||||||
|
|
||||||
|
|
||||||
@app.put("/memories/{memory_id}", summary="Update a memory")
|
@app.put("/memories/{memory_id}", summary="Update a memory")
|
||||||
def update_memory(memory_id: str, updated_memory: Dict[str, Any], _api_key: Optional[str] = Depends(verify_api_key)):
|
def update_memory(memory_id: str, updated_memory: MemoryUpdate, _api_key: Optional[str] = Depends(verify_api_key)):
|
||||||
"""Update an existing memory with new content.
|
"""Update an existing memory with new content.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
memory_id (str): ID of the memory to update
|
memory_id (str): ID of the memory to update
|
||||||
updated_memory (str): New content to update the memory with
|
updated_memory (MemoryUpdate): New content and optional metadata to update the memory with
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: Success message indicating the memory was updated
|
dict: Success message indicating the memory was updated
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return MEMORY_INSTANCE.update(memory_id=memory_id, data=updated_memory)
|
return MEMORY_INSTANCE.update(memory_id=memory_id, data=updated_memory.text, metadata=updated_memory.metadata)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Error in update_memory:")
|
logging.exception("Error in update_memory:")
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class TestAuthDisabled:
|
|||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
|
|
||||||
def test_update_memory_without_key(self):
|
def test_update_memory_without_key(self):
|
||||||
resp = self.client.put("/memories/mem-1", json={"data": "updated"})
|
resp = self.client.put("/memories/mem-1", json={"text": "updated"})
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
|
|
||||||
def test_history_without_key(self):
|
def test_history_without_key(self):
|
||||||
@@ -276,7 +276,7 @@ class TestAuthEnabled:
|
|||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
|
|
||||||
def test_update_memory_with_key(self):
|
def test_update_memory_with_key(self):
|
||||||
resp = self._authed("PUT", "/memories/mem-1", json={"data": "updated"})
|
resp = self._authed("PUT", "/memories/mem-1", json={"text": "updated"})
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
|
|
||||||
def test_history_with_key(self):
|
def test_history_with_key(self):
|
||||||
@@ -346,7 +346,7 @@ class TestAuthenticatedCRUDFlow:
|
|||||||
self.mock.search.assert_called_once()
|
self.mock.search.assert_called_once()
|
||||||
|
|
||||||
# 5. Update
|
# 5. Update
|
||||||
resp = self._authed("PUT", "/memories/mem-1", json={"data": "updated content"})
|
resp = self._authed("PUT", "/memories/mem-1", json={"text": "updated content"})
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
self.mock.update.assert_called_once()
|
self.mock.update.assert_called_once()
|
||||||
|
|
||||||
|
|||||||
@@ -505,3 +505,59 @@ class TestCallSignatureMatch:
|
|||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
_, kwargs = mock_memory.search.call_args
|
_, kwargs = mock_memory.search.call_args
|
||||||
assert kwargs["query"] == "food"
|
assert kwargs["query"] == "food"
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# MemoryUpdate: text and metadata forwarding (fix for #3933)
|
||||||
|
# ===========================================================================
|
||||||
|
|
||||||
|
class TestUpdateMemory:
|
||||||
|
"""Verify that PUT /memories/{id} extracts text and metadata from the
|
||||||
|
request body and forwards them correctly to Memory.update()."""
|
||||||
|
|
||||||
|
def test_text_forwarded_as_data(self, client, mock_memory):
|
||||||
|
resp = client.put("/memories/mem-1", json={"text": "Likes tennis"})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
_, kwargs = mock_memory.update.call_args
|
||||||
|
assert kwargs["data"] == "Likes tennis"
|
||||||
|
|
||||||
|
def test_metadata_forwarded(self, client, mock_memory):
|
||||||
|
resp = client.put("/memories/mem-1", json={
|
||||||
|
"text": "Likes tennis",
|
||||||
|
"metadata": {"category": "sports"},
|
||||||
|
})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
_, kwargs = mock_memory.update.call_args
|
||||||
|
assert kwargs["metadata"] == {"category": "sports"}
|
||||||
|
|
||||||
|
def test_metadata_omitted_passes_none(self, client, mock_memory):
|
||||||
|
resp = client.put("/memories/mem-1", json={"text": "Likes tennis"})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
_, kwargs = mock_memory.update.call_args
|
||||||
|
assert kwargs["metadata"] is None
|
||||||
|
|
||||||
|
def test_missing_text_returns_422(self, client):
|
||||||
|
"""text is required — omitting it should fail validation."""
|
||||||
|
resp = client.put("/memories/mem-1", json={"metadata": {"k": "v"}})
|
||||||
|
assert resp.status_code == 422
|
||||||
|
|
||||||
|
def test_dict_not_passed_as_data(self, client, mock_memory):
|
||||||
|
"""Regression test for #3933: the entire dict must NOT be passed as data."""
|
||||||
|
resp = client.put("/memories/mem-1", json={"text": "updated content"})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
_, kwargs = mock_memory.update.call_args
|
||||||
|
assert isinstance(kwargs["data"], str)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUpdateOpenAPISchema:
|
||||||
|
"""Verify the MemoryUpdate schema appears in the OpenAPI docs."""
|
||||||
|
|
||||||
|
def test_update_schema_includes_text(self, client):
|
||||||
|
schema = client.get("/openapi.json").json()
|
||||||
|
update_props = schema["components"]["schemas"]["MemoryUpdate"]["properties"]
|
||||||
|
assert "text" in update_props
|
||||||
|
|
||||||
|
def test_update_schema_includes_metadata(self, client):
|
||||||
|
schema = client.get("/openapi.json").json()
|
||||||
|
update_props = schema["components"]["schemas"]["MemoryUpdate"]["properties"]
|
||||||
|
assert "metadata" in update_props
|
||||||
|
|||||||
Reference in New Issue
Block a user