memora
This commit is contained in:
parent
42260c29f7
commit
7d8d07d1aa
37 changed files with 1264 additions and 140 deletions
14
README.md
14
README.md
|
|
@ -341,7 +341,7 @@ poetry add ../memory-poc --editable
|
|||
### 2. Import the memory system:
|
||||
|
||||
```python
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
|
||||
# Initialize memory
|
||||
memory = TemporalSemanticMemory()
|
||||
|
|
@ -363,7 +363,7 @@ results, trace = await memory.search_async(
|
|||
### 3. Import the FastAPI app:
|
||||
|
||||
```python
|
||||
from web import app, memory
|
||||
from memora.web import app, memory
|
||||
|
||||
# Use the FastAPI app in your own project
|
||||
# You can mount it as a sub-application or run it directly
|
||||
|
|
@ -377,7 +377,7 @@ if __name__ == "__main__":
|
|||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
from web import app as memory_app, memory
|
||||
from memora.web import app as memory_app, memory
|
||||
|
||||
# Create your own app
|
||||
my_app = FastAPI()
|
||||
|
|
@ -407,7 +407,7 @@ if __name__ == "__main__":
|
|||
- `SearchTrace`, `SearchTracer` - Search tracing utilities
|
||||
- `QueryInfo`, `EntryPoint`, `NodeVisit`, etc. - Trace data structures
|
||||
|
||||
**From `web` package:**
|
||||
**From `memory.web` package:**
|
||||
- `app` - FastAPI application instance
|
||||
- `memory` - Shared TemporalSemanticMemory instance
|
||||
|
||||
|
|
@ -417,10 +417,10 @@ To run the web interface:
|
|||
|
||||
```bash
|
||||
# Development mode with auto-reload
|
||||
uvicorn web.server:app --reload --port 8000
|
||||
uvicorn memora.web.server:app --reload --port 8000
|
||||
|
||||
# Production mode
|
||||
uvicorn web.server:app --host 0.0.0.0 --port 8000 --workers 4
|
||||
uvicorn memora.web.server:app --host 0.0.0.0 --port 8000 --workers 4
|
||||
```
|
||||
|
||||
Then open http://localhost:8000 in your browser to access the visualization interface.
|
||||
|
|
@ -476,7 +476,7 @@ The memory system uses a **mixin pattern** for code organization:
|
|||
### Store Memories
|
||||
|
||||
```python
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
|
||||
memory = TemporalSemanticMemory()
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from rich.table import Table
|
|||
from rich import box
|
||||
import pydantic
|
||||
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
console = Console()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
|||
|
||||
import asyncio
|
||||
import argparse
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
from locomo_benchmark import LoComoDataset, LoComoAnswerGenerator, LoComoAnswerEvaluator
|
||||
from common.benchmark_runner import BenchmarkRunner
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import asyncio
|
|||
import argparse
|
||||
import subprocess
|
||||
from rich.console import Console
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
from longmemeval_benchmark import LongMemEvalDataset, LongMemEvalAnswerGenerator, LongMemEvalAnswerEvaluator
|
||||
from common.benchmark_runner import BenchmarkRunner
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ This demonstrates how to:
|
|||
3. Mount the memory app as a sub-application
|
||||
"""
|
||||
import asyncio
|
||||
from web import app, memory
|
||||
from memora.web import app, memory
|
||||
|
||||
|
||||
async def example_memory_usage():
|
||||
|
|
|
|||
43
generate_openapi.py
Normal file
43
generate_openapi.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate OpenAPI specification from FastAPI app.
|
||||
|
||||
This script imports the FastAPI app and exports its OpenAPI schema to a JSON file.
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path to import memory module
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from memory.web.server import app
|
||||
|
||||
def generate_openapi_spec(output_path: str = "openapi.json"):
|
||||
"""Generate OpenAPI spec and save to file."""
|
||||
# Get the OpenAPI schema from the app
|
||||
openapi_schema = app.openapi()
|
||||
|
||||
# Write to file
|
||||
output_file = Path(output_path)
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(openapi_schema, f, indent=2)
|
||||
|
||||
print(f"✓ OpenAPI specification generated: {output_file.absolute()}")
|
||||
print(f" - Title: {openapi_schema['info']['title']}")
|
||||
print(f" - Version: {openapi_schema['info']['version']}")
|
||||
print(f" - Endpoints: {len(openapi_schema['paths'])}")
|
||||
|
||||
# List endpoints
|
||||
print("\n Endpoints:")
|
||||
for path, methods in openapi_schema['paths'].items():
|
||||
for method in methods.keys():
|
||||
if method.upper() in ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']:
|
||||
endpoint_info = methods[method]
|
||||
summary = endpoint_info.get('summary', 'No summary')
|
||||
tags = ', '.join(endpoint_info.get('tags', ['untagged']))
|
||||
print(f" {method.upper():6} {path:30} [{tags}] - {summary}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
output = sys.argv[1] if len(sys.argv) > 1 else "openapi.json"
|
||||
generate_openapi_spec(output)
|
||||
|
|
@ -338,9 +338,19 @@ class LinkOperationsMixin:
|
|||
# Try direct conversion (works for numpy arrays, pgvector objects, etc.)
|
||||
emb = np.array(raw_emb, dtype=np.float32)
|
||||
|
||||
# Ensure it's 1D
|
||||
if emb.ndim != 1:
|
||||
raise ValueError(f"Expected 1D embedding, got shape {emb.shape}")
|
||||
embedding_arrays.append(emb)
|
||||
|
||||
existing_embeddings = np.vstack(embedding_arrays) if embedding_arrays else np.array([])
|
||||
if not embedding_arrays:
|
||||
existing_embeddings = np.array([])
|
||||
elif len(embedding_arrays) == 1:
|
||||
# Single embedding: reshape to (1, dim)
|
||||
existing_embeddings = embedding_arrays[0].reshape(1, -1)
|
||||
else:
|
||||
# Multiple embeddings: vstack
|
||||
existing_embeddings = np.vstack(embedding_arrays)
|
||||
|
||||
# For each new unit, compute similarities with ALL existing units
|
||||
for unit_id, new_embedding in zip(unit_ids, embeddings):
|
||||
|
|
@ -290,7 +290,29 @@ class TemporalSemanticMemory(
|
|||
is_duplicate = []
|
||||
|
||||
# Convert existing embeddings to numpy for faster computation
|
||||
existing_embeddings = np.array([np.array(row['embedding']) for row in existing_facts])
|
||||
embedding_arrays = []
|
||||
for row in existing_facts:
|
||||
raw_emb = row['embedding']
|
||||
# Handle different pgvector formats
|
||||
if isinstance(raw_emb, str):
|
||||
# Parse string format: "[1.0, 2.0, ...]"
|
||||
import json
|
||||
emb = np.array(json.loads(raw_emb), dtype=np.float32)
|
||||
elif isinstance(raw_emb, (list, tuple)):
|
||||
emb = np.array(raw_emb, dtype=np.float32)
|
||||
else:
|
||||
# Try direct conversion
|
||||
emb = np.array(raw_emb, dtype=np.float32)
|
||||
embedding_arrays.append(emb)
|
||||
|
||||
if not embedding_arrays:
|
||||
existing_embeddings = np.array([])
|
||||
elif len(embedding_arrays) == 1:
|
||||
# Single embedding: reshape to (1, dim)
|
||||
existing_embeddings = embedding_arrays[0].reshape(1, -1)
|
||||
else:
|
||||
# Multiple embeddings: vstack
|
||||
existing_embeddings = np.vstack(embedding_arrays)
|
||||
|
||||
comp_start = time_mod.time()
|
||||
for embedding in embeddings:
|
||||
|
|
@ -16,19 +16,48 @@ from pathlib import Path
|
|||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
from memory import TemporalSemanticMemory
|
||||
# Import from parent memora package
|
||||
from memora import TemporalSemanticMemory
|
||||
|
||||
import logging
|
||||
|
||||
load_dotenv()
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
app = FastAPI(title="Memory Graph API", version="1.0.0")
|
||||
app = FastAPI(
|
||||
title="Agent Memory API",
|
||||
version="1.0.0",
|
||||
description="""
|
||||
A temporal-semantic memory system for AI agents that stores, retrieves, and reasons over memories.
|
||||
|
||||
## Features
|
||||
|
||||
* **Batch Memory Storage**: Store multiple memories efficiently with automatic fact extraction
|
||||
* **Semantic Search**: Find relevant memories using natural language queries
|
||||
* **Fact Type Filtering**: Search across world facts, agent actions, and opinions separately
|
||||
* **Think Endpoint**: Generate contextual answers based on agent identity and memories
|
||||
* **Graph Visualization**: Interactive memory graph visualization
|
||||
* **Document Tracking**: Track and manage memory documents with upsert support
|
||||
|
||||
## Architecture
|
||||
|
||||
The system uses:
|
||||
- **Temporal Links**: Connect memories that are close in time
|
||||
- **Semantic Links**: Connect semantically similar memories
|
||||
- **Entity Links**: Connect memories that mention the same entities
|
||||
- **Spreading Activation**: Intelligent traversal for memory retrieval
|
||||
""",
|
||||
contact={
|
||||
"name": "Memory System",
|
||||
},
|
||||
license_info={
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
|
||||
}
|
||||
)
|
||||
|
||||
# Mount static files
|
||||
app.mount("/static", StaticFiles(directory="web/static"), name="static")
|
||||
app.mount("/static", StaticFiles(directory=str(Path(__file__).parent / "static")), name="static")
|
||||
|
||||
|
||||
class SearchRequest(BaseModel):
|
||||
|
|
@ -40,6 +69,42 @@ class SearchRequest(BaseModel):
|
|||
mmr_lambda: float = 0.5
|
||||
trace: bool = False
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"query": "What did Alice say about machine learning?",
|
||||
"agent_id": "user123",
|
||||
"thinking_budget": 100,
|
||||
"top_k": 10,
|
||||
"mmr_lambda": 0.5,
|
||||
"trace": True
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SearchResponse(BaseModel):
|
||||
"""Response model for search endpoints."""
|
||||
results: List[Dict[str, Any]]
|
||||
trace: Optional[Dict[str, Any]] = None
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"results": [
|
||||
{
|
||||
"text": "Alice works at Google on the AI team",
|
||||
"score": 0.95,
|
||||
"id": "123e4567-e89b-12d3-a456-426614174000"
|
||||
}
|
||||
],
|
||||
"trace": {
|
||||
"query": "What did Alice say about machine learning?",
|
||||
"num_results": 1,
|
||||
"time_seconds": 0.123
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MemoryItem(BaseModel):
|
||||
"""Single memory item for batch put."""
|
||||
|
|
@ -47,6 +112,15 @@ class MemoryItem(BaseModel):
|
|||
event_date: Optional[datetime] = None
|
||||
context: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"content": "Alice mentioned she's working on a new ML model",
|
||||
"event_date": "2024-01-15T10:30:00Z",
|
||||
"context": "team meeting"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BatchPutRequest(BaseModel):
|
||||
"""Request model for batch put endpoint."""
|
||||
|
|
@ -56,6 +130,45 @@ class BatchPutRequest(BaseModel):
|
|||
document_metadata: Optional[Dict[str, Any]] = None
|
||||
upsert: bool = False
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"agent_id": "user123",
|
||||
"items": [
|
||||
{
|
||||
"content": "Alice works at Google",
|
||||
"context": "work"
|
||||
},
|
||||
{
|
||||
"content": "Bob went hiking yesterday",
|
||||
"event_date": "2024-01-15T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"document_id": "conversation_123",
|
||||
"upsert": False
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BatchPutResponse(BaseModel):
|
||||
"""Response model for batch put endpoint."""
|
||||
success: bool
|
||||
message: str
|
||||
agent_id: str
|
||||
document_id: Optional[str] = None
|
||||
items_count: int
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"success": True,
|
||||
"message": "Successfully stored 2 memory items",
|
||||
"agent_id": "user123",
|
||||
"document_id": "conversation_123",
|
||||
"items_count": 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ThinkRequest(BaseModel):
|
||||
"""Request model for think endpoint."""
|
||||
|
|
@ -64,6 +177,16 @@ class ThinkRequest(BaseModel):
|
|||
thinking_budget: int = 50
|
||||
top_k: int = 10
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"query": "What do you think about artificial intelligence?",
|
||||
"agent_id": "user123",
|
||||
"thinking_budget": 50,
|
||||
"top_k": 10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ThinkResponse(BaseModel):
|
||||
"""Response model for think endpoint."""
|
||||
|
|
@ -71,6 +194,50 @@ class ThinkResponse(BaseModel):
|
|||
based_on: Dict[str, List[Dict[str, Any]]] # {"world": [...], "agent": [...], "opinion": [...]}
|
||||
new_opinions: List[str] = [] # List of newly formed opinions
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"text": "Based on my understanding, AI is a transformative technology...",
|
||||
"based_on": {
|
||||
"world": [{"text": "AI is used in healthcare", "score": 0.9}],
|
||||
"agent": [{"text": "I discussed AI applications last week", "score": 0.85}],
|
||||
"opinion": [{"text": "I believe AI should be used ethically", "score": 0.8}]
|
||||
},
|
||||
"new_opinions": ["AI has great potential when used responsibly"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AgentsResponse(BaseModel):
|
||||
"""Response model for agents list endpoint."""
|
||||
agents: List[str]
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"agents": ["user123", "agent_alice", "agent_bob"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GraphDataResponse(BaseModel):
|
||||
"""Response model for graph data endpoint."""
|
||||
nodes: List[Dict[str, Any]]
|
||||
edges: List[Dict[str, Any]]
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"nodes": [
|
||||
{"id": "1", "label": "Alice works at Google", "type": "world"},
|
||||
{"id": "2", "label": "Bob went hiking", "type": "world"}
|
||||
],
|
||||
"edges": [
|
||||
{"from": "1", "to": "2", "type": "semantic", "weight": 0.8}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
memory = TemporalSemanticMemory()
|
||||
|
||||
|
|
@ -86,14 +253,23 @@ async def shutdown_event():
|
|||
await memory.close()
|
||||
logging.info("Memory system closed")
|
||||
|
||||
@app.get("/")
|
||||
@app.get("/", include_in_schema=False)
|
||||
async def index():
|
||||
"""Serve the visualization page."""
|
||||
return FileResponse("web/templates/index.html")
|
||||
return FileResponse(str(Path(__file__).parent / "templates" / "index.html"))
|
||||
|
||||
|
||||
@app.get("/api/graph")
|
||||
async def api_graph(agent_id: Optional[str] = None, fact_type: Optional[str] = None):
|
||||
@app.get(
|
||||
"/api/graph",
|
||||
response_model=GraphDataResponse,
|
||||
tags=["Visualization"],
|
||||
summary="Get memory graph data",
|
||||
description="Retrieve graph data for visualization, optionally filtered by agent_id and fact_type (world/agent/opinion)"
|
||||
)
|
||||
async def api_graph(
|
||||
agent_id: Optional[str] = None,
|
||||
fact_type: Optional[str] = None
|
||||
):
|
||||
"""Get graph data from database, optionally filtered by agent_id and fact_type."""
|
||||
try:
|
||||
data = await memory.get_graph_data(agent_id, fact_type)
|
||||
|
|
@ -105,11 +281,16 @@ async def api_graph(agent_id: Optional[str] = None, fact_type: Optional[str] = N
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/search")
|
||||
@app.post(
|
||||
"/api/search",
|
||||
response_model=SearchResponse,
|
||||
tags=["Search"],
|
||||
summary="Search all memory types",
|
||||
description="Search across all memory types (world, agent, opinion) using semantic similarity and spreading activation"
|
||||
)
|
||||
async def api_search(request: SearchRequest):
|
||||
"""Run a search and return results with trace."""
|
||||
try:
|
||||
# Initialize memory system
|
||||
# Run search with tracing
|
||||
results, trace = await memory.search_async(
|
||||
agent_id=request.agent_id,
|
||||
|
|
@ -123,10 +304,10 @@ async def api_search(request: SearchRequest):
|
|||
# Convert trace to dict
|
||||
trace_dict = trace.to_dict() if trace else None
|
||||
|
||||
return {
|
||||
'results': results,
|
||||
'trace': trace_dict
|
||||
}
|
||||
return SearchResponse(
|
||||
results=results,
|
||||
trace=trace_dict
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
||||
|
|
@ -134,7 +315,13 @@ async def api_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/world_search")
|
||||
@app.post(
|
||||
"/api/world_search",
|
||||
response_model=SearchResponse,
|
||||
tags=["Search"],
|
||||
summary="Search world facts",
|
||||
description="Search only world facts - general knowledge about people, places, events, and things that happen"
|
||||
)
|
||||
async def api_world_search(request: SearchRequest):
|
||||
"""Search only world facts (general knowledge about the world)."""
|
||||
try:
|
||||
|
|
@ -152,10 +339,10 @@ async def api_world_search(request: SearchRequest):
|
|||
# Convert trace to dict
|
||||
trace_dict = trace.to_dict() if trace else None
|
||||
|
||||
return {
|
||||
'results': results,
|
||||
'trace': trace_dict
|
||||
}
|
||||
return SearchResponse(
|
||||
results=results,
|
||||
trace=trace_dict
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
||||
|
|
@ -163,7 +350,13 @@ async def api_world_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/agent_search")
|
||||
@app.post(
|
||||
"/api/agent_search",
|
||||
response_model=SearchResponse,
|
||||
tags=["Search"],
|
||||
summary="Search agent action facts",
|
||||
description="Search only agent facts - memories about what the AI agent did, actions taken, and tasks performed"
|
||||
)
|
||||
async def api_agent_search(request: SearchRequest):
|
||||
"""Search only agent facts (facts about what the agent did)."""
|
||||
try:
|
||||
|
|
@ -181,10 +374,10 @@ async def api_agent_search(request: SearchRequest):
|
|||
# Convert trace to dict
|
||||
trace_dict = trace.to_dict() if trace else None
|
||||
|
||||
return {
|
||||
'results': results,
|
||||
'trace': trace_dict
|
||||
}
|
||||
return SearchResponse(
|
||||
results=results,
|
||||
trace=trace_dict
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
||||
|
|
@ -192,7 +385,13 @@ async def api_agent_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/opinion_search")
|
||||
@app.post(
|
||||
"/api/opinion_search",
|
||||
response_model=SearchResponse,
|
||||
tags=["Search"],
|
||||
summary="Search agent opinions",
|
||||
description="Search only opinion facts - the agent's formed beliefs, perspectives, and viewpoints"
|
||||
)
|
||||
async def api_opinion_search(request: SearchRequest):
|
||||
"""Search only opinion facts (agent's formed opinions and perspectives)."""
|
||||
try:
|
||||
|
|
@ -210,10 +409,10 @@ async def api_opinion_search(request: SearchRequest):
|
|||
# Convert trace to dict
|
||||
trace_dict = trace.to_dict() if trace else None
|
||||
|
||||
return {
|
||||
'results': results,
|
||||
'trace': trace_dict
|
||||
}
|
||||
return SearchResponse(
|
||||
results=results,
|
||||
trace=trace_dict
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
||||
|
|
@ -221,19 +420,24 @@ async def api_opinion_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/think")
|
||||
async def api_think(request: ThinkRequest):
|
||||
"""
|
||||
Think and formulate an answer using agent identity, world facts, and opinions.
|
||||
@app.post(
|
||||
"/api/think",
|
||||
response_model=ThinkResponse,
|
||||
tags=["Reasoning"],
|
||||
summary="Think and generate answer",
|
||||
description="""
|
||||
Think and formulate an answer using agent identity, world facts, and opinions.
|
||||
|
||||
This endpoint:
|
||||
1. Retrieves agent facts (agent's identity)
|
||||
2. Retrieves world facts relevant to the query
|
||||
3. Retrieves existing opinions (agent's perspectives)
|
||||
4. Uses Groq LLM to formulate an answer
|
||||
5. Extracts and stores any new opinions formed
|
||||
6. Returns plain text answer, the facts used, and new opinions
|
||||
This endpoint:
|
||||
1. Retrieves agent facts (agent's identity)
|
||||
2. Retrieves world facts relevant to the query
|
||||
3. Retrieves existing opinions (agent's perspectives)
|
||||
4. Uses LLM to formulate a contextual answer
|
||||
5. Extracts and stores any new opinions formed
|
||||
6. Returns plain text answer, the facts used, and new opinions
|
||||
"""
|
||||
)
|
||||
async def api_think(request: ThinkRequest):
|
||||
try:
|
||||
# Use the memory system's think_async method
|
||||
result = await memory.think_async(
|
||||
|
|
@ -256,12 +460,18 @@ async def api_think(request: ThinkRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/agents")
|
||||
@app.get(
|
||||
"/api/agents",
|
||||
response_model=AgentsResponse,
|
||||
tags=["Management"],
|
||||
summary="List all agents",
|
||||
description="Get a list of all agent IDs that have stored memories in the system"
|
||||
)
|
||||
async def api_agents():
|
||||
"""Get list of available agents from database."""
|
||||
try:
|
||||
agent_list = await memory.list_agents()
|
||||
return {"agents": agent_list}
|
||||
return AgentsResponse(agents=agent_list)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
||||
|
|
@ -269,25 +479,30 @@ async def api_agents():
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/memories/batch")
|
||||
@app.post(
|
||||
"/api/memories/batch",
|
||||
response_model=BatchPutResponse,
|
||||
tags=["Memory Storage"],
|
||||
summary="Store multiple memories",
|
||||
description="""
|
||||
Store multiple memory items in batch with automatic fact extraction.
|
||||
|
||||
Features:
|
||||
- Efficient batch processing
|
||||
- Automatic fact extraction from natural language
|
||||
- Entity recognition and linking
|
||||
- Document tracking with optional upsert
|
||||
- Temporal and semantic linking
|
||||
|
||||
The system automatically:
|
||||
1. Extracts semantic facts from the content
|
||||
2. Generates embeddings
|
||||
3. Deduplicates similar facts
|
||||
4. Creates temporal, semantic, and entity links
|
||||
5. Tracks document metadata
|
||||
"""
|
||||
)
|
||||
async def api_batch_put(request: BatchPutRequest):
|
||||
"""
|
||||
Store multiple memories in batch.
|
||||
|
||||
This endpoint calls put_batch_async to efficiently store multiple memory items.
|
||||
Supports document tracking and upsert operations.
|
||||
|
||||
Example request:
|
||||
{
|
||||
"agent_id": "user123",
|
||||
"items": [
|
||||
{"content": "Alice works at Google", "context": "work"},
|
||||
{"content": "Bob went hiking yesterday", "event_date": "2024-01-15T10:00:00Z"}
|
||||
],
|
||||
"document_id": "conversation_123",
|
||||
"upsert": false
|
||||
}
|
||||
"""
|
||||
try:
|
||||
# Validate agent_id - prevent writing to reserved agents
|
||||
RESERVED_AGENT_IDS = {"locomo"}
|
||||
|
|
@ -297,9 +512,6 @@ async def api_batch_put(request: BatchPutRequest):
|
|||
detail=f"Cannot write to reserved agent_id '{request.agent_id}'. Reserved agents: {', '.join(RESERVED_AGENT_IDS)}"
|
||||
)
|
||||
|
||||
# Initialize memory system
|
||||
|
||||
|
||||
# Prepare contents for put_batch_async
|
||||
contents = []
|
||||
for item in request.items:
|
||||
|
|
@ -319,13 +531,13 @@ async def api_batch_put(request: BatchPutRequest):
|
|||
upsert=request.upsert
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Successfully stored {len(contents)} memory items",
|
||||
"agent_id": request.agent_id,
|
||||
"document_id": request.document_id,
|
||||
"items_count": len(contents)
|
||||
}
|
||||
return BatchPutResponse(
|
||||
success=True,
|
||||
message=f"Successfully stored {len(contents)} memory items",
|
||||
agent_id=request.agent_id,
|
||||
document_id=request.document_id,
|
||||
items_count=len(contents)
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
||||
|
|
@ -365,4 +577,4 @@ if __name__ == "__main__":
|
|||
print(" GET /api/agents - List available agents")
|
||||
print("\n" + "=" * 80 + "\n")
|
||||
|
||||
uvicorn.run("server:app", host="0.0.0.0", port=8080, reload=True)
|
||||
uvicorn.run("memora.web.server:app", host="0.0.0.0", port=8080, reload=True)
|
||||
837
openapi.json
Normal file
837
openapi.json
Normal file
|
|
@ -0,0 +1,837 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "Agent Memory API",
|
||||
"description": "\nA temporal-semantic memory system for AI agents that stores, retrieves, and reasons over memories.\n\n## Features\n\n* **Batch Memory Storage**: Store multiple memories efficiently with automatic fact extraction\n* **Semantic Search**: Find relevant memories using natural language queries\n* **Fact Type Filtering**: Search across world facts, agent actions, and opinions separately\n* **Think Endpoint**: Generate contextual answers based on agent identity and memories\n* **Graph Visualization**: Interactive memory graph visualization\n* **Document Tracking**: Track and manage memory documents with upsert support\n\n## Architecture\n\nThe system uses:\n- **Temporal Links**: Connect memories that are close in time\n- **Semantic Links**: Connect semantically similar memories\n- **Entity Links**: Connect memories that mention the same entities\n- **Spreading Activation**: Intelligent traversal for memory retrieval\n ",
|
||||
"contact": {
|
||||
"name": "Memory System"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/api/graph": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Visualization"
|
||||
],
|
||||
"summary": "Get memory graph data",
|
||||
"description": "Retrieve graph data for visualization, optionally filtered by agent_id and fact_type (world/agent/opinion)",
|
||||
"operationId": "api_graph_api_graph_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "agent_id",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Agent Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fact_type",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Fact Type"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GraphDataResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/search": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Search"
|
||||
],
|
||||
"summary": "Search all memory types",
|
||||
"description": "Search across all memory types (world, agent, opinion) using semantic similarity and spreading activation",
|
||||
"operationId": "api_search_api_search_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/world_search": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Search"
|
||||
],
|
||||
"summary": "Search world facts",
|
||||
"description": "Search only world facts - general knowledge about people, places, events, and things that happen",
|
||||
"operationId": "api_world_search_api_world_search_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/agent_search": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Search"
|
||||
],
|
||||
"summary": "Search agent action facts",
|
||||
"description": "Search only agent facts - memories about what the AI agent did, actions taken, and tasks performed",
|
||||
"operationId": "api_agent_search_api_agent_search_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/opinion_search": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Search"
|
||||
],
|
||||
"summary": "Search agent opinions",
|
||||
"description": "Search only opinion facts - the agent's formed beliefs, perspectives, and viewpoints",
|
||||
"operationId": "api_opinion_search_api_opinion_search_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SearchResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/think": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Reasoning"
|
||||
],
|
||||
"summary": "Think and generate answer",
|
||||
"description": "Think and formulate an answer using agent identity, world facts, and opinions.\n\nThis endpoint:\n1. Retrieves agent facts (agent's identity)\n2. Retrieves world facts relevant to the query\n3. Retrieves existing opinions (agent's perspectives)\n4. Uses LLM to formulate a contextual answer\n5. Extracts and stores any new opinions formed\n6. Returns plain text answer, the facts used, and new opinions",
|
||||
"operationId": "api_think_api_think_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ThinkRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ThinkResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/agents": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Management"
|
||||
],
|
||||
"summary": "List all agents",
|
||||
"description": "Get a list of all agent IDs that have stored memories in the system",
|
||||
"operationId": "api_agents_api_agents_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AgentsResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/memories/batch": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Memory Storage"
|
||||
],
|
||||
"summary": "Store multiple memories",
|
||||
"description": "Store multiple memory items in batch with automatic fact extraction.\n\nFeatures:\n- Efficient batch processing\n- Automatic fact extraction from natural language\n- Entity recognition and linking\n- Document tracking with optional upsert\n- Temporal and semantic linking\n\nThe system automatically:\n1. Extracts semantic facts from the content\n2. Generates embeddings\n3. Deduplicates similar facts\n4. Creates temporal, semantic, and entity links\n5. Tracks document metadata",
|
||||
"operationId": "api_batch_put_api_memories_batch_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BatchPutRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BatchPutResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/locomo": {
|
||||
"get": {
|
||||
"summary": "Api Locomo",
|
||||
"description": "Get Locomo benchmark results.",
|
||||
"operationId": "api_locomo_api_locomo_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"AgentsResponse": {
|
||||
"properties": {
|
||||
"agents": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Agents"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"agents"
|
||||
],
|
||||
"title": "AgentsResponse",
|
||||
"description": "Response model for agents list endpoint.",
|
||||
"example": {
|
||||
"agents": [
|
||||
"user123",
|
||||
"agent_alice",
|
||||
"agent_bob"
|
||||
]
|
||||
}
|
||||
},
|
||||
"BatchPutRequest": {
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"title": "Agent Id"
|
||||
},
|
||||
"items": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MemoryItem"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Items"
|
||||
},
|
||||
"document_id": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Document Id"
|
||||
},
|
||||
"document_metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Document Metadata"
|
||||
},
|
||||
"upsert": {
|
||||
"type": "boolean",
|
||||
"title": "Upsert",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"agent_id",
|
||||
"items"
|
||||
],
|
||||
"title": "BatchPutRequest",
|
||||
"description": "Request model for batch put endpoint.",
|
||||
"example": {
|
||||
"agent_id": "user123",
|
||||
"document_id": "conversation_123",
|
||||
"items": [
|
||||
{
|
||||
"content": "Alice works at Google",
|
||||
"context": "work"
|
||||
},
|
||||
{
|
||||
"content": "Bob went hiking yesterday",
|
||||
"event_date": "2024-01-15T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"upsert": false
|
||||
}
|
||||
},
|
||||
"BatchPutResponse": {
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"title": "Success"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"title": "Message"
|
||||
},
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"title": "Agent Id"
|
||||
},
|
||||
"document_id": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Document Id"
|
||||
},
|
||||
"items_count": {
|
||||
"type": "integer",
|
||||
"title": "Items Count"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"success",
|
||||
"message",
|
||||
"agent_id",
|
||||
"items_count"
|
||||
],
|
||||
"title": "BatchPutResponse",
|
||||
"description": "Response model for batch put endpoint.",
|
||||
"example": {
|
||||
"agent_id": "user123",
|
||||
"document_id": "conversation_123",
|
||||
"items_count": 2,
|
||||
"message": "Successfully stored 2 memory items",
|
||||
"success": true
|
||||
}
|
||||
},
|
||||
"GraphDataResponse": {
|
||||
"properties": {
|
||||
"nodes": {
|
||||
"items": {
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Nodes"
|
||||
},
|
||||
"edges": {
|
||||
"items": {
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Edges"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"nodes",
|
||||
"edges"
|
||||
],
|
||||
"title": "GraphDataResponse",
|
||||
"description": "Response model for graph data endpoint.",
|
||||
"example": {
|
||||
"edges": [
|
||||
{
|
||||
"from": "1",
|
||||
"to": "2",
|
||||
"type": "semantic",
|
||||
"weight": 0.8
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "1",
|
||||
"label": "Alice works at Google",
|
||||
"type": "world"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"label": "Bob went hiking",
|
||||
"type": "world"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValidationError"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Detail"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError"
|
||||
},
|
||||
"MemoryItem": {
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"title": "Content"
|
||||
},
|
||||
"event_date": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Event Date"
|
||||
},
|
||||
"context": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Context"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content"
|
||||
],
|
||||
"title": "MemoryItem",
|
||||
"description": "Single memory item for batch put.",
|
||||
"example": {
|
||||
"content": "Alice mentioned she's working on a new ML model",
|
||||
"context": "team meeting",
|
||||
"event_date": "2024-01-15T10:30:00Z"
|
||||
}
|
||||
},
|
||||
"SearchRequest": {
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"title": "Query"
|
||||
},
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"title": "Agent Id",
|
||||
"default": "default"
|
||||
},
|
||||
"thinking_budget": {
|
||||
"type": "integer",
|
||||
"title": "Thinking Budget",
|
||||
"default": 100
|
||||
},
|
||||
"top_k": {
|
||||
"type": "integer",
|
||||
"title": "Top K",
|
||||
"default": 10
|
||||
},
|
||||
"mmr_lambda": {
|
||||
"type": "number",
|
||||
"title": "Mmr Lambda",
|
||||
"default": 0.5
|
||||
},
|
||||
"trace": {
|
||||
"type": "boolean",
|
||||
"title": "Trace",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"query"
|
||||
],
|
||||
"title": "SearchRequest",
|
||||
"description": "Request model for search endpoint.",
|
||||
"example": {
|
||||
"agent_id": "user123",
|
||||
"mmr_lambda": 0.5,
|
||||
"query": "What did Alice say about machine learning?",
|
||||
"thinking_budget": 100,
|
||||
"top_k": 10,
|
||||
"trace": true
|
||||
}
|
||||
},
|
||||
"SearchResponse": {
|
||||
"properties": {
|
||||
"results": {
|
||||
"items": {
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Results"
|
||||
},
|
||||
"trace": {
|
||||
"anyOf": [
|
||||
{
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Trace"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"results"
|
||||
],
|
||||
"title": "SearchResponse",
|
||||
"description": "Response model for search endpoints.",
|
||||
"example": {
|
||||
"results": [
|
||||
{
|
||||
"id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"score": 0.95,
|
||||
"text": "Alice works at Google on the AI team"
|
||||
}
|
||||
],
|
||||
"trace": {
|
||||
"num_results": 1,
|
||||
"query": "What did Alice say about machine learning?",
|
||||
"time_seconds": 0.123
|
||||
}
|
||||
}
|
||||
},
|
||||
"ThinkRequest": {
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"title": "Query"
|
||||
},
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"title": "Agent Id",
|
||||
"default": "default"
|
||||
},
|
||||
"thinking_budget": {
|
||||
"type": "integer",
|
||||
"title": "Thinking Budget",
|
||||
"default": 50
|
||||
},
|
||||
"top_k": {
|
||||
"type": "integer",
|
||||
"title": "Top K",
|
||||
"default": 10
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"query"
|
||||
],
|
||||
"title": "ThinkRequest",
|
||||
"description": "Request model for think endpoint.",
|
||||
"example": {
|
||||
"agent_id": "user123",
|
||||
"query": "What do you think about artificial intelligence?",
|
||||
"thinking_budget": 50,
|
||||
"top_k": 10
|
||||
}
|
||||
},
|
||||
"ThinkResponse": {
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"title": "Text"
|
||||
},
|
||||
"based_on": {
|
||||
"additionalProperties": {
|
||||
"items": {
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"type": "object",
|
||||
"title": "Based On"
|
||||
},
|
||||
"new_opinions": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "New Opinions",
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"text",
|
||||
"based_on"
|
||||
],
|
||||
"title": "ThinkResponse",
|
||||
"description": "Response model for think endpoint.",
|
||||
"example": {
|
||||
"based_on": {
|
||||
"agent": [
|
||||
{
|
||||
"score": 0.85,
|
||||
"text": "I discussed AI applications last week"
|
||||
}
|
||||
],
|
||||
"opinion": [
|
||||
{
|
||||
"score": 0.8,
|
||||
"text": "I believe AI should be used ethically"
|
||||
}
|
||||
],
|
||||
"world": [
|
||||
{
|
||||
"score": 0.9,
|
||||
"text": "AI is used in healthcare"
|
||||
}
|
||||
]
|
||||
},
|
||||
"new_opinions": [
|
||||
"AI has great potential when used responsibly"
|
||||
],
|
||||
"text": "Based on my understanding, AI is a transformative technology..."
|
||||
}
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location"
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"title": "Message"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "Error Type"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"loc",
|
||||
"msg",
|
||||
"type"
|
||||
],
|
||||
"title": "ValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ requires = ["hatchling"]
|
|||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "agent_memory"
|
||||
name = "memora"
|
||||
version = "0.1.0"
|
||||
description = "Temporal + Semantic + Entity Memory System for AI agents using PostgreSQL"
|
||||
readme = "README.md"
|
||||
|
|
@ -29,7 +29,7 @@ dependencies = [
|
|||
]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["memory", "web"]
|
||||
packages = ["memora"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
log_cli = true
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest_asyncio
|
|||
import os
|
||||
import asyncio
|
||||
from dotenv import load_dotenv
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
import asyncpg
|
||||
|
||||
load_dotenv()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Test chunking functionality for large documents.
|
||||
"""
|
||||
import pytest
|
||||
from memory.llm_client import chunk_text
|
||||
from memora.llm_client import chunk_text
|
||||
|
||||
|
||||
def test_chunk_text_small():
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import logging
|
|||
import os
|
||||
import pytest
|
||||
from datetime import datetime, timezone
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import json
|
|||
import pytest
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
|
||||
|
||||
# Configure logging to show performance metrics
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ Test search tracing functionality.
|
|||
import pytest
|
||||
import asyncio
|
||||
import os
|
||||
from memory.temporal_semantic_memory import TemporalSemanticMemory
|
||||
from memory.search_trace import SearchTrace
|
||||
from memora.temporal_semantic_memory import TemporalSemanticMemory
|
||||
from memora.search_trace import SearchTrace
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ Test temporal extraction and per-fact dating.
|
|||
"""
|
||||
import pytest
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from memory.llm_client import extract_facts_from_text
|
||||
from memora.llm_client import extract_facts_from_text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Test think function for opinion generation and consistency.
|
|||
import pytest
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
from memory import TemporalSemanticMemory
|
||||
from memora import TemporalSemanticMemory
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
90
uv.lock
90
uv.lock
|
|
@ -6,51 +6,6 @@ resolution-markers = [
|
|||
"python_full_version < '3.12'",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agent-memory"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "alembic" },
|
||||
{ name = "asyncpg" },
|
||||
{ name = "fastapi", extra = ["standard"] },
|
||||
{ name = "greenlet" },
|
||||
{ name = "langchain-text-splitters" },
|
||||
{ name = "openai" },
|
||||
{ name = "pgvector" },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-timeout" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "rich" },
|
||||
{ name = "sentence-transformers" },
|
||||
{ name = "sqlalchemy" },
|
||||
{ name = "uvicorn" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "alembic", specifier = ">=1.17.1" },
|
||||
{ name = "asyncpg", specifier = ">=0.29.0" },
|
||||
{ name = "fastapi", extras = ["standard"], specifier = ">=0.120.3" },
|
||||
{ name = "greenlet", specifier = ">=3.2.4" },
|
||||
{ name = "langchain-text-splitters", specifier = ">=0.3.0" },
|
||||
{ name = "openai", specifier = ">=1.0.0" },
|
||||
{ name = "pgvector", specifier = ">=0.4.1" },
|
||||
{ name = "psycopg2-binary", specifier = ">=2.9.11" },
|
||||
{ name = "pydantic", specifier = ">=2.0.0" },
|
||||
{ name = "pytest", specifier = ">=7.0.0" },
|
||||
{ name = "pytest-asyncio", specifier = ">=0.21.0" },
|
||||
{ name = "pytest-timeout", specifier = ">=2.4.0" },
|
||||
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||
{ name = "rich", specifier = ">=13.0.0" },
|
||||
{ name = "sentence-transformers", specifier = ">=2.2.0" },
|
||||
{ name = "sqlalchemy", specifier = ">=2.0.44" },
|
||||
{ name = "uvicorn", specifier = ">=0.38.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.17.1"
|
||||
|
|
@ -807,6 +762,51 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memora"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "alembic" },
|
||||
{ name = "asyncpg" },
|
||||
{ name = "fastapi", extra = ["standard"] },
|
||||
{ name = "greenlet" },
|
||||
{ name = "langchain-text-splitters" },
|
||||
{ name = "openai" },
|
||||
{ name = "pgvector" },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "pytest-timeout" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "rich" },
|
||||
{ name = "sentence-transformers" },
|
||||
{ name = "sqlalchemy" },
|
||||
{ name = "uvicorn" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "alembic", specifier = ">=1.17.1" },
|
||||
{ name = "asyncpg", specifier = ">=0.29.0" },
|
||||
{ name = "fastapi", extras = ["standard"], specifier = ">=0.120.3" },
|
||||
{ name = "greenlet", specifier = ">=3.2.4" },
|
||||
{ name = "langchain-text-splitters", specifier = ">=0.3.0" },
|
||||
{ name = "openai", specifier = ">=1.0.0" },
|
||||
{ name = "pgvector", specifier = ">=0.4.1" },
|
||||
{ name = "psycopg2-binary", specifier = ">=2.9.11" },
|
||||
{ name = "pydantic", specifier = ">=2.0.0" },
|
||||
{ name = "pytest", specifier = ">=7.0.0" },
|
||||
{ name = "pytest-asyncio", specifier = ">=0.21.0" },
|
||||
{ name = "pytest-timeout", specifier = ">=2.4.0" },
|
||||
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||
{ name = "rich", specifier = ">=13.0.0" },
|
||||
{ name = "sentence-transformers", specifier = ">=2.2.0" },
|
||||
{ name = "sqlalchemy", specifier = ">=2.0.44" },
|
||||
{ name = "uvicorn", specifier = ">=0.38.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mpmath"
|
||||
version = "1.3.0"
|
||||
|
|
|
|||
Loading…
Reference in a new issue