diff --git a/agentcore_memory_browser/backend.py b/agentcore_memory_browser/backend.py index 185c938..07d9ee8 100644 --- a/agentcore_memory_browser/backend.py +++ b/agentcore_memory_browser/backend.py @@ -144,6 +144,16 @@ class RetrieveRequest(BaseModel): nextToken: Optional[str] = None +class CreateRecordRequest(BaseModel): + """Request for creating a memory record via event""" + + content: str + contentType: str = "text" # "text" or "json" + actorId: str = "default" + sessionId: str = "default" + role: str = "USER" # USER, ASSISTANT, TOOL, OTHER + + # --- Helper Functions --- @@ -383,6 +393,56 @@ async def delete_memory_record( raise HTTPException(status_code=500, detail=str(e)) +@app.post("/api/memories/{memory_id}/records") +async def create_memory_record( + memory_id: str, request: CreateRecordRequest +) -> Dict[str, Any]: + """Create a new memory record via event""" + import json as json_module + + try: + # Validate JSON if contentType is json + content_text = request.content + payload=[ + { + "conversational": { + "content": {"text": content_text}, + "role": request.role, + } + } + ] + if request.contentType == "json": + try: + json_module.loads(content_text) + payload=[ + { + "blob": content_text + } + ] + except json_module.JSONDecodeError as e: + raise HTTPException(status_code=400, detail=f"Invalid JSON: {e}") + + response = bedrock_data.create_event( + memoryId=memory_id, + actorId=request.actorId or "default", + sessionId=request.sessionId or "default", + eventTimestamp=datetime.now(), + payload=payload, + ) + + return { + "success": True, + "message": "Event created successfully", + "eventId": response.get("event", {}).get("eventId"), + } + + except HTTPException: + raise + except Exception as e: + logger.error(f"Error creating event: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + if __name__ == "__main__": import uvicorn diff --git a/agentcore_memory_browser/static/js/app.js b/agentcore_memory_browser/static/js/app.js index 4b5ec2d..9125c5c 100644 --- a/agentcore_memory_browser/static/js/app.js +++ b/agentcore_memory_browser/static/js/app.js @@ -821,6 +821,56 @@ async function refreshData() { let currentSessionId = null; let currentActorId = null; +// Execute Add Event (global, at memory level) +async function executeAddEvent() { + if (!currentMemory) { + console.error('No memory selected'); + return; + } + + const resultsDiv = document.getElementById('add-event-results'); + const contentInput = document.getElementById('add-event-content'); + const typeSelect = document.getElementById('add-event-type'); + const roleSelect = document.getElementById('add-event-role'); + const actorInput = document.getElementById('add-event-actor'); + const sessionInput = document.getElementById('add-event-session'); + + await executeApiCall({ + resultsDiv, + validation: () => { + if (!contentInput.value.trim()) { + showWarning(resultsDiv, 'Please enter content.'); + return false; + } + return true; + }, + apiCall: async () => { + const response = await fetch(`/api/memories/${currentMemory.id}/records`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + content: contentInput.value.trim(), + contentType: typeSelect.value, + role: roleSelect.value, + actorId: actorInput.value.trim() || 'default', + sessionId: sessionInput.value.trim() || 'default' + }) + }); + if (!response.ok) { + const err = await response.json(); + throw new Error(err.detail || response.statusText); + } + return await response.json(); + }, + displayFunction: (container, result) => { + container.innerHTML = `
${result.message} (Event ID: ${result.eventId})
`; + contentInput.value = ''; + }, + loadingMessage: "Creating event...", + errorPrefix: "Error creating event" + }); +} + // Execute Global List Events (from main section, not strategy-specific) async function executeGlobalListEvents() { if (!currentMemory) { @@ -1040,12 +1090,14 @@ function initializeStrategyNamespaces(strategy, index) { const memoryRecordsNamespaceInput = document.getElementById(`strategy-${index}-namespace`); const retrieveNamespaceInput = document.getElementById(`strategy-${index}-retrieve-namespace`); + const ns = getSimplifiedNamespace(strategy); + if (memoryRecordsNamespaceInput && !memoryRecordsNamespaceInput.value) { - memoryRecordsNamespaceInput.value = getSimplifiedNamespace(strategy); + memoryRecordsNamespaceInput.value = ns; } if (retrieveNamespaceInput && !retrieveNamespaceInput.value) { - retrieveNamespaceInput.value = getSimplifiedNamespace(strategy); + retrieveNamespaceInput.value = ns; } }, 100); } diff --git a/agentcore_memory_browser/templates/index.html b/agentcore_memory_browser/templates/index.html index 2f719be..83e132a 100644 --- a/agentcore_memory_browser/templates/index.html +++ b/agentcore_memory_browser/templates/index.html @@ -184,6 +184,56 @@
Updated
+ +
+
+
+ + Add Event +
+
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
+
+
+
@@ -197,11 +247,11 @@
- +
- +
diff --git a/pyproject.toml b/pyproject.toml index 0ec2fb1..9e6c297 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,8 @@ description = "A web interface for browsing and exploring Amazon Bedrock AgentCo readme = "README.md" requires-python = ">=3.13" dependencies = [ - "boto3>=1.40.33", + "boto3>=1.42.0", + "botocore>=1.42.0", "fastapi>=0.116.2", "jinja2>=3.1.6", "python-multipart>=0.0.20", diff --git a/uv.lock b/uv.lock index 3a3a60b..fff7c30 100644 --- a/uv.lock +++ b/uv.lock @@ -8,6 +8,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "boto3" }, + { name = "botocore" }, { name = "fastapi" }, { name = "jinja2" }, { name = "python-multipart" }, @@ -22,7 +23,8 @@ dev = [ [package.metadata] requires-dist = [ - { name = "boto3", specifier = ">=1.40.33" }, + { name = "boto3", specifier = ">=1.42.0" }, + { name = "botocore", specifier = ">=1.42.0" }, { name = "fastapi", specifier = ">=0.116.2" }, { name = "jinja2", specifier = ">=3.1.6" }, { name = "python-multipart", specifier = ">=0.0.20" }, @@ -59,30 +61,30 @@ wheels = [ [[package]] name = "boto3" -version = "1.40.33" +version = "1.42.22" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/c9/8319d3b15c6758537d0ce705e78b5447b71305a86c609e20800b05b1918a/boto3-1.40.33.tar.gz", hash = "sha256:b9548f4cfb44dc9fb7f09741c7dce34350d4a9f904ff0f7f97d699d0586f6b9a", size = 111565, upload-time = "2025-09-17T19:36:56.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/91/87a0cedb0335f2c0653fe7353fc47d785b092353dab5b2d7141efd5d74b5/boto3-1.42.22.tar.gz", hash = "sha256:8550d91432dec1e587ab6d97f7e031bb334ca4fbb7824b8b63bca6e69c7e84b5", size = 112808, upload-time = "2026-01-05T20:29:27.399Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/63/aa401c06b37a271e8d8ac7bc24ed7b73a74842502d8afe6ca02748972b2b/boto3-1.40.33-py3-none-any.whl", hash = "sha256:dc8a37b25f43d458d830a5988283f0a5ac38a47dd2c46cccc5bc40e47fda97c9", size = 139345, upload-time = "2025-09-17T19:36:54.077Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0f/2cc0e0806b1c945185eb8af385ef7a3ff2545565db17ec72b2531ef8fcf9/boto3-1.42.22-py3-none-any.whl", hash = "sha256:c8df2c356366f6193a85d2582ba27b170a93dd37784b8f195e901b169ae74d29", size = 140574, upload-time = "2026-01-05T20:29:25.391Z" }, ] [[package]] name = "botocore" -version = "1.40.33" +version = "1.42.22" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/82/bbb393f794ad21112a8cf105ccdf00f5fb4e587f3511fa7c3a738da33bab/botocore-1.40.33.tar.gz", hash = "sha256:307e0cb55f4b7307bfd2090fd1c9ca14e1febece5b928144ccc0f78adfd2d864", size = 14344043, upload-time = "2025-09-17T19:36:46.165Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/86/b6f00de81a3f0e7e83328354b38376fbb9f0be1c8b66626ac9a274cdca4e/botocore-1.42.22.tar.gz", hash = "sha256:635c9213a448885a1cf735f1a950b83adaced0860b8159fc26d1242abc042443", size = 14879014, upload-time = "2026-01-05T20:29:16.419Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/3b/6ea450eb97dab358aa09b51f22d3050f5d6fef00af99fa01d87fad8897a5/botocore-1.40.33-py3-none-any.whl", hash = "sha256:b9a33758913410ca0d81f30dfd9f00c4ff22c72c38fdf679864011afc73d99fd", size = 14016447, upload-time = "2025-09-17T19:36:43.065Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d4/eb3ac8b2689b6b83655874281fa1fd5a570e9fc6578ebdbde0bd87055910/botocore-1.42.22-py3-none-any.whl", hash = "sha256:a1dfebcf9dec52a74ad7f28bc6c895e7c43216cac63748eb1216054fb0c3a7fe", size = 14551116, upload-time = "2026-01-05T20:29:12.816Z" }, ] [[package]] @@ -279,14 +281,14 @@ wheels = [ [[package]] name = "s3transfer" -version = "0.14.0" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/74/8d69dcb7a9efe8baa2046891735e5dfe433ad558ae23d9e3c14c633d1d58/s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125", size = 151547, upload-time = "2025-09-09T19:23:31.089Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712, upload-time = "2025-09-09T19:23:30.041Z" }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, ] [[package]]