This commit is contained in:
Nicolò Boschi 2025-11-04 15:46:23 +01:00
parent 1b7e0bc380
commit 45b3a68332
6 changed files with 350 additions and 325 deletions

View file

@ -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)

View file

@ -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,11 +281,18 @@ 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
}
}
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."""
@ -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)

View file

@ -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';

View file

@ -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();

View file

@ -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>

View file

@ -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