cleanup
This commit is contained in:
parent
1b7e0bc380
commit
45b3a68332
6 changed files with 350 additions and 325 deletions
|
|
@ -28,7 +28,7 @@ class EntryPoint(BaseModel):
|
|||
|
||||
class WeightComponents(BaseModel):
|
||||
"""Breakdown of weight calculation components."""
|
||||
activation: float = Field(description="Activation from spreading", ge=0.0, le=1.0)
|
||||
activation: float = Field(description="Activation from spreading (can exceed 1.0 through accumulation)", ge=0.0)
|
||||
semantic_similarity: float = Field(description="Semantic similarity to query", ge=0.0, le=1.0)
|
||||
recency: float = Field(description="Recency weight", ge=0.0, le=1.0)
|
||||
frequency: float = Field(description="Normalized frequency weight", ge=0.0, le=1.0)
|
||||
|
|
|
|||
|
|
@ -91,13 +91,12 @@ The system uses:
|
|||
# Store memory instance on app for route handlers to access
|
||||
app.state.memory = memory
|
||||
|
||||
# Register all routes
|
||||
_register_routes(app)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
# Create default app instance with default embeddings
|
||||
app = create_app()
|
||||
|
||||
|
||||
class SearchRequest(BaseModel):
|
||||
"""Request model for search endpoint."""
|
||||
query: str
|
||||
|
|
@ -226,11 +225,17 @@ class ThinkRequest(BaseModel):
|
|||
}
|
||||
|
||||
|
||||
class OpinionItem(BaseModel):
|
||||
"""Model for an opinion with confidence score."""
|
||||
text: str
|
||||
confidence: float
|
||||
|
||||
|
||||
class ThinkResponse(BaseModel):
|
||||
"""Response model for think endpoint."""
|
||||
text: str
|
||||
based_on: Dict[str, List[Dict[str, Any]]] # {"world": [...], "agent": [...], "opinion": [...]}
|
||||
new_opinions: List[str] = [] # List of newly formed opinions
|
||||
new_opinions: List[OpinionItem] = [] # List of newly formed opinions with confidence
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
|
|
@ -241,7 +246,9 @@ class ThinkResponse(BaseModel):
|
|||
"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"]
|
||||
"new_opinions": [
|
||||
{"text": "AI has great potential when used responsibly", "confidence": 0.95}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -262,6 +269,8 @@ class GraphDataResponse(BaseModel):
|
|||
"""Response model for graph data endpoint."""
|
||||
nodes: List[Dict[str, Any]]
|
||||
edges: List[Dict[str, Any]]
|
||||
table_rows: List[Dict[str, Any]]
|
||||
total_units: int
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
|
|
@ -272,28 +281,35 @@ class GraphDataResponse(BaseModel):
|
|||
],
|
||||
"edges": [
|
||||
{"from": "1", "to": "2", "type": "semantic", "weight": 0.8}
|
||||
]
|
||||
],
|
||||
"table_rows": [
|
||||
{"id": "abc12345...", "text": "Alice works at Google", "context": "Work info", "date": "2024-01-15 10:30", "entities": "Alice (PERSON), Google (ORGANIZATION)"}
|
||||
],
|
||||
"total_units": 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
async def index():
|
||||
def _register_routes(app: FastAPI):
|
||||
"""Register all API routes on the given app instance."""
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
async def index():
|
||||
"""Serve the visualization page."""
|
||||
return FileResponse(str(Path(__file__).parent / "templates" / "index.html"))
|
||||
|
||||
|
||||
@app.get(
|
||||
@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(
|
||||
)
|
||||
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 app.state.memory.get_graph_data(agent_id, fact_type)
|
||||
|
|
@ -305,14 +321,14 @@ async def api_graph(
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post(
|
||||
@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):
|
||||
)
|
||||
async def api_search(request: SearchRequest):
|
||||
"""Run a search and return results with trace."""
|
||||
try:
|
||||
# Run search with tracing
|
||||
|
|
@ -339,14 +355,14 @@ async def api_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post(
|
||||
@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):
|
||||
)
|
||||
async def api_world_search(request: SearchRequest):
|
||||
"""Search only world facts (general knowledge about the world)."""
|
||||
try:
|
||||
# Run search with fact_type filter for 'world'
|
||||
|
|
@ -374,14 +390,14 @@ async def api_world_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post(
|
||||
@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):
|
||||
)
|
||||
async def api_agent_search(request: SearchRequest):
|
||||
"""Search only agent facts (facts about what the agent did)."""
|
||||
try:
|
||||
# Run search with fact_type filter for 'agent'
|
||||
|
|
@ -409,14 +425,14 @@ async def api_agent_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post(
|
||||
@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):
|
||||
)
|
||||
async def api_opinion_search(request: SearchRequest):
|
||||
"""Search only opinion facts (agent's formed opinions and perspectives)."""
|
||||
try:
|
||||
# Run search with fact_type filter for 'opinion'
|
||||
|
|
@ -444,24 +460,24 @@ async def api_opinion_search(request: SearchRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post(
|
||||
@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.
|
||||
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 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
|
||||
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):
|
||||
)
|
||||
async def api_think(request: ThinkRequest):
|
||||
try:
|
||||
# Use the memory system's think_async method
|
||||
result = await app.state.memory.think_async(
|
||||
|
|
@ -484,14 +500,14 @@ async def api_think(request: ThinkRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get(
|
||||
@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():
|
||||
)
|
||||
async def api_agents():
|
||||
"""Get list of available agents from database."""
|
||||
try:
|
||||
agent_list = await app.state.memory.list_agents()
|
||||
|
|
@ -503,30 +519,30 @@ async def api_agents():
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post(
|
||||
@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.
|
||||
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
|
||||
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
|
||||
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):
|
||||
)
|
||||
async def api_batch_put(request: BatchPutRequest):
|
||||
try:
|
||||
# Validate agent_id - prevent writing to reserved agents
|
||||
RESERVED_AGENT_IDS = {"locomo"}
|
||||
|
|
@ -569,8 +585,8 @@ async def api_batch_put(request: BatchPutRequest):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/locomo")
|
||||
async def api_locomo():
|
||||
@app.get("/api/locomo")
|
||||
async def api_locomo():
|
||||
"""Get Locomo benchmark results."""
|
||||
import json
|
||||
try:
|
||||
|
|
@ -587,6 +603,10 @@ async def api_locomo():
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# Create default app instance
|
||||
app = create_app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
print("\n" + "=" * 80)
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ window.loadDataView = async function(factType) {
|
|||
|
||||
try {
|
||||
// Build URL with agent filter and fact_type filter
|
||||
let url = `/api/graph?agent_id=${encodeURIComponent(currentAgentId)}`;
|
||||
let url = `api/graph?agent_id=${encodeURIComponent(currentAgentId)}`;
|
||||
if (factType !== 'all') {
|
||||
url += `&fact_type=${factType}`;
|
||||
}
|
||||
|
|
@ -361,7 +361,7 @@ async function loadGraphData() {
|
|||
}
|
||||
|
||||
// Build URL with agent filter
|
||||
let url = `/api/graph?agent_id=${encodeURIComponent(currentAgentId)}`;
|
||||
let url = `api/graph?agent_id=${encodeURIComponent(currentAgentId)}`;
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
|
|
@ -577,7 +577,7 @@ async function loadAgents() {
|
|||
if (agentsLoaded) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/agents');
|
||||
const response = await fetch('api/agents');
|
||||
const data = await response.json();
|
||||
|
||||
const select = document.getElementById('search-agent-id');
|
||||
|
|
@ -758,7 +758,7 @@ async function loadAgentsForPane(paneId) {
|
|||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/agents');
|
||||
const response = await fetch('api/agents');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
|
@ -802,11 +802,11 @@ window.runSearchInPane = async function(paneId) {
|
|||
|
||||
try {
|
||||
// Determine endpoint based on search type
|
||||
let endpoint = '/api/search';
|
||||
let endpoint = 'api/search';
|
||||
if (searchType === 'world') {
|
||||
endpoint = '/api/world_search';
|
||||
endpoint = 'api/world_search';
|
||||
} else if (searchType === 'agent') {
|
||||
endpoint = '/api/agent_search';
|
||||
endpoint = 'api/agent_search';
|
||||
}
|
||||
|
||||
statusBar.innerHTML = '<span style="color: #ff9800;">🔄 Searching...</span>';
|
||||
|
|
@ -1488,8 +1488,8 @@ async function loadGlobalAgents() {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log('Fetching /api/agents...'); // Debug
|
||||
const response = await fetch('/api/agents');
|
||||
console.log('Fetching api/agents...'); // Debug
|
||||
const response = await fetch('api/agents');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
|
|
@ -1617,9 +1617,9 @@ window.runThink = async function() {
|
|||
resultDiv.style.display = 'none';
|
||||
loadingDiv.style.display = 'block';
|
||||
|
||||
console.log('Calling /api/think with', { query, agentId, thinkingBudget, topK }); // Debug log
|
||||
console.log('Calling api/think with', { query, agentId, thinkingBudget, topK }); // Debug log
|
||||
|
||||
const response = await fetch('/api/think', {
|
||||
const response = await fetch('api/think', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
|
@ -1701,11 +1701,16 @@ window.runThink = async function() {
|
|||
if (data.new_opinions && data.new_opinions.length > 0) {
|
||||
newOpinionsListDiv.innerHTML = data.new_opinions.map((opinion, idx) => `
|
||||
<div style="margin-bottom: 15px; padding: 15px; background: white; border-radius: 6px; border-left: 4px solid #4caf50; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 8px;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<span style="background: #4caf50; color: white; padding: 4px 8px; border-radius: 12px; font-size: 11px; font-weight: bold; margin-right: 10px;">NEW</span>
|
||||
<span style="color: #666; font-size: 12px;">#${idx + 1}</span>
|
||||
</div>
|
||||
<div style="font-size: 14px; color: #333; line-height: 1.5;">${opinion}</div>
|
||||
<span style="background: #e3f2fd; color: #1976d2; padding: 3px 8px; border-radius: 10px; font-size: 11px; font-weight: 600;">
|
||||
${(opinion.confidence * 100).toFixed(0)}% confidence
|
||||
</span>
|
||||
</div>
|
||||
<div style="font-size: 14px; color: #333; line-height: 1.5;">${opinion.text}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
newOpinionsDiv.style.display = 'block';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ let locomoData = null;
|
|||
|
||||
window.loadLocomoResults = async function() {
|
||||
try {
|
||||
const response = await fetch('/api/locomo');
|
||||
const response = await fetch('api/locomo');
|
||||
locomoData = await response.json();
|
||||
console.log('Loaded locomo data:', locomoData);
|
||||
renderLocomoResults();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<title>Memory Graph - Live Visualization</title>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.28.1/cytoscape.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
<link rel="stylesheet" href="./static/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="breadcrumb-container">
|
||||
|
|
@ -300,7 +300,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/app.js"></script>
|
||||
<script src="/static/js/locomo.js"></script>
|
||||
<script src="./static/js/app.js"></script>
|
||||
<script src="./static/js/locomo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
2
serve.sh
2
serve.sh
|
|
@ -1,3 +1,3 @@
|
|||
#!/bin/bash
|
||||
# Start the FastAPI server with hot reload
|
||||
uv run uvicorn web.server:app --reload --host 0.0.0.0 --port 8080
|
||||
uv run uvicorn memora.web.server:app --reload --host 0.0.0.0 --port 8080
|
||||
|
|
|
|||
Loading…
Reference in a new issue