fix: memory scope_id column overflow and API read mismatch
AgentRuntime writes scope_id as {user_id}:{agent_id} (73 chars) but:
- DB columns were CHAR(36)/VARCHAR(36), causing Data too long errors
- Memory API queried with agent_id only, returning empty results
- Add _mem_scope() helper to build correct scope_id format
- Update all CRUD queries in agent_memory.py to use _mem_scope()
- Enhance login failure logging in auth.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,39 @@ fun LoginScreen(
|
||||
|
||||
// Server URL
|
||||
if (isEditingUrl) {
|
||||
// Preset quick-select buttons
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
val isLocalSelected = editedUrl == "http://192.168.31.135:8037"
|
||||
val isCloudSelected = editedUrl == "http://101.43.95.130:8037"
|
||||
OutlinedButton(
|
||||
onClick = { editedUrl = "http://192.168.31.135:8037" },
|
||||
colors = if (isLocalSelected) {
|
||||
ButtonDefaults.outlinedButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
} else {
|
||||
ButtonDefaults.outlinedButtonColors()
|
||||
}
|
||||
) {
|
||||
Text("本地")
|
||||
}
|
||||
OutlinedButton(
|
||||
onClick = { editedUrl = "http://101.43.95.130:8037" },
|
||||
colors = if (isCloudSelected) {
|
||||
ButtonDefaults.outlinedButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
} else {
|
||||
ButtonDefaults.outlinedButtonColors()
|
||||
}
|
||||
) {
|
||||
Text("云服务")
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = editedUrl,
|
||||
onValueChange = { editedUrl = it },
|
||||
@@ -84,7 +117,7 @@ fun LoginScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
enabled = !uiState.isLoading,
|
||||
supportingText = { Text("输入后点击保存,设置即时生效") }
|
||||
supportingText = { Text("点击上方快捷选择,或手动输入后保存") }
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
||||
@@ -11,7 +11,6 @@ from typing import List, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import or_
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.api.auth import get_current_user
|
||||
@@ -30,6 +29,11 @@ router = APIRouter(
|
||||
SCOPE_AGENT = "agent"
|
||||
|
||||
|
||||
def _mem_scope(user: User, agent_id: str) -> str:
|
||||
"""构建记忆 scope_id,与 AgentRuntime 写入的格式保持一致({user_id}:{agent_id})。"""
|
||||
return f"{user.id}:{agent_id}"
|
||||
|
||||
|
||||
def _check_agent(db: Session, agent_id: str, user: User):
|
||||
agent = db.query(Agent).filter(Agent.id == agent_id).first()
|
||||
if not agent:
|
||||
@@ -218,9 +222,10 @@ def list_global_knowledge(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
q = db.query(GlobalKnowledge).filter(
|
||||
GlobalKnowledge.scope_kind == SCOPE_AGENT,
|
||||
GlobalKnowledge.scope_id == agent_id,
|
||||
GlobalKnowledge.scope_id == scope_id,
|
||||
)
|
||||
if search:
|
||||
q = q.filter(GlobalKnowledge.content.contains(search))
|
||||
@@ -237,13 +242,14 @@ def create_global_knowledge(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
gk = GlobalKnowledge(
|
||||
id=str(uuid.uuid4()),
|
||||
content=body.content,
|
||||
tags=body.tags,
|
||||
confidence=body.confidence,
|
||||
scope_kind=SCOPE_AGENT,
|
||||
scope_id=agent_id,
|
||||
scope_id=scope_id,
|
||||
source_user_id=current_user.id,
|
||||
)
|
||||
db.add(gk)
|
||||
@@ -261,10 +267,11 @@ def update_global_knowledge(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
gk = db.query(GlobalKnowledge).filter(
|
||||
GlobalKnowledge.id == knowledge_id,
|
||||
GlobalKnowledge.scope_kind == SCOPE_AGENT,
|
||||
GlobalKnowledge.scope_id == agent_id,
|
||||
GlobalKnowledge.scope_id == scope_id,
|
||||
).first()
|
||||
if not gk:
|
||||
raise HTTPException(status_code=404, detail="知识条目不存在")
|
||||
@@ -287,10 +294,11 @@ def delete_global_knowledge(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
gk = db.query(GlobalKnowledge).filter(
|
||||
GlobalKnowledge.id == knowledge_id,
|
||||
GlobalKnowledge.scope_kind == SCOPE_AGENT,
|
||||
GlobalKnowledge.scope_id == agent_id,
|
||||
GlobalKnowledge.scope_id == scope_id,
|
||||
).first()
|
||||
if not gk:
|
||||
raise HTTPException(status_code=404, detail="知识条目不存在")
|
||||
@@ -312,9 +320,10 @@ def list_knowledge_entities(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
q = db.query(KnowledgeEntity).filter(
|
||||
KnowledgeEntity.scope_kind == SCOPE_AGENT,
|
||||
KnowledgeEntity.scope_id == agent_id,
|
||||
KnowledgeEntity.scope_id == scope_id,
|
||||
)
|
||||
if search:
|
||||
q = q.filter(
|
||||
@@ -334,6 +343,7 @@ def create_knowledge_entity(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
ke = KnowledgeEntity(
|
||||
id=str(uuid.uuid4()),
|
||||
name=body.name,
|
||||
@@ -343,7 +353,7 @@ def create_knowledge_entity(
|
||||
confidence=body.confidence,
|
||||
metadata_=body.tags,
|
||||
scope_kind=SCOPE_AGENT,
|
||||
scope_id=agent_id,
|
||||
scope_id=scope_id,
|
||||
user_id=current_user.id,
|
||||
)
|
||||
db.add(ke)
|
||||
@@ -361,10 +371,11 @@ def update_knowledge_entity(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
ke = db.query(KnowledgeEntity).filter(
|
||||
KnowledgeEntity.id == entity_id,
|
||||
KnowledgeEntity.scope_kind == SCOPE_AGENT,
|
||||
KnowledgeEntity.scope_id == agent_id,
|
||||
KnowledgeEntity.scope_id == scope_id,
|
||||
).first()
|
||||
if not ke:
|
||||
raise HTTPException(status_code=404, detail="实体不存在")
|
||||
@@ -391,10 +402,11 @@ def delete_knowledge_entity(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
ke = db.query(KnowledgeEntity).filter(
|
||||
KnowledgeEntity.id == entity_id,
|
||||
KnowledgeEntity.scope_kind == SCOPE_AGENT,
|
||||
KnowledgeEntity.scope_id == agent_id,
|
||||
KnowledgeEntity.scope_id == scope_id,
|
||||
).first()
|
||||
if not ke:
|
||||
raise HTTPException(status_code=404, detail="实体不存在")
|
||||
@@ -420,9 +432,10 @@ def list_learning_patterns(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
q = db.query(AgentLearningPattern).filter(
|
||||
AgentLearningPattern.scope_kind == SCOPE_AGENT,
|
||||
AgentLearningPattern.scope_id == agent_id,
|
||||
AgentLearningPattern.scope_id == scope_id,
|
||||
)
|
||||
total = q.count()
|
||||
items = q.order_by(AgentLearningPattern.updated_at.desc()).offset(skip).limit(limit).all()
|
||||
@@ -457,10 +470,11 @@ def delete_learning_pattern(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
lp = db.query(AgentLearningPattern).filter(
|
||||
AgentLearningPattern.id == pattern_id,
|
||||
AgentLearningPattern.scope_kind == SCOPE_AGENT,
|
||||
AgentLearningPattern.scope_id == agent_id,
|
||||
AgentLearningPattern.scope_id == scope_id,
|
||||
).first()
|
||||
if not lp:
|
||||
raise HTTPException(status_code=404, detail="学习模式不存在")
|
||||
@@ -482,15 +496,10 @@ def list_vector_memories(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
uid = current_user.id
|
||||
# scope_id may be: agent_id, or {user_id}:{agent_id}, or {user_id}:{agent_id}:{session_id}
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
q = db.query(AgentVectorMemory).filter(
|
||||
AgentVectorMemory.scope_kind == SCOPE_AGENT,
|
||||
or_(
|
||||
AgentVectorMemory.scope_id == agent_id,
|
||||
AgentVectorMemory.scope_id.like(f"%:{agent_id}"),
|
||||
AgentVectorMemory.scope_id.like(f"{uid}:%"),
|
||||
),
|
||||
AgentVectorMemory.scope_id == scope_id,
|
||||
)
|
||||
if search:
|
||||
q = q.filter(AgentVectorMemory.content_text.contains(search))
|
||||
@@ -507,17 +516,13 @@ def delete_vector_memory(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
uid = current_user.id
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
vm = (
|
||||
db.query(AgentVectorMemory)
|
||||
.filter(
|
||||
AgentVectorMemory.id == memory_id,
|
||||
AgentVectorMemory.scope_kind == SCOPE_AGENT,
|
||||
or_(
|
||||
AgentVectorMemory.scope_id == agent_id,
|
||||
AgentVectorMemory.scope_id.like(f"%:{agent_id}"),
|
||||
AgentVectorMemory.scope_id.like(f"{uid}:%"),
|
||||
),
|
||||
AgentVectorMemory.scope_id == scope_id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
@@ -549,35 +554,32 @@ def export_memory(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
gk_list = (
|
||||
db.query(GlobalKnowledge)
|
||||
.filter(GlobalKnowledge.scope_kind == SCOPE_AGENT, GlobalKnowledge.scope_id == agent_id)
|
||||
.filter(GlobalKnowledge.scope_kind == SCOPE_AGENT, GlobalKnowledge.scope_id == scope_id)
|
||||
.all()
|
||||
)
|
||||
ke_list = (
|
||||
db.query(KnowledgeEntity)
|
||||
.filter(KnowledgeEntity.scope_kind == SCOPE_AGENT, KnowledgeEntity.scope_id == agent_id)
|
||||
.filter(KnowledgeEntity.scope_kind == SCOPE_AGENT, KnowledgeEntity.scope_id == scope_id)
|
||||
.all()
|
||||
)
|
||||
kr_list = (
|
||||
db.query(KnowledgeRelation)
|
||||
.filter(KnowledgeRelation.scope_kind == SCOPE_AGENT, KnowledgeRelation.scope_id == agent_id)
|
||||
.filter(KnowledgeRelation.scope_kind == SCOPE_AGENT, KnowledgeRelation.scope_id == scope_id)
|
||||
.all()
|
||||
)
|
||||
lp_list = (
|
||||
db.query(AgentLearningPattern)
|
||||
.filter(AgentLearningPattern.scope_kind == SCOPE_AGENT, AgentLearningPattern.scope_id == agent_id)
|
||||
.filter(AgentLearningPattern.scope_kind == SCOPE_AGENT, AgentLearningPattern.scope_id == scope_id)
|
||||
.all()
|
||||
)
|
||||
vm_list = (
|
||||
db.query(AgentVectorMemory)
|
||||
.filter(
|
||||
AgentVectorMemory.scope_kind == SCOPE_AGENT,
|
||||
or_(
|
||||
AgentVectorMemory.scope_id == agent_id,
|
||||
AgentVectorMemory.scope_id.like(f"%:{agent_id}"),
|
||||
AgentVectorMemory.scope_id.like(f"{current_user.id}:%"),
|
||||
),
|
||||
AgentVectorMemory.scope_id == scope_id,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
@@ -600,6 +602,7 @@ def import_memory(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
_check_agent(db, agent_id, current_user)
|
||||
scope_id = _mem_scope(current_user, agent_id)
|
||||
imported = {"global_knowledge": 0, "knowledge_entities": 0, "knowledge_relations": 0, "learning_patterns": 0}
|
||||
|
||||
for item in body.global_knowledge:
|
||||
@@ -609,7 +612,7 @@ def import_memory(
|
||||
tags=item.get("tags"),
|
||||
confidence=item.get("confidence", "medium"),
|
||||
scope_kind=SCOPE_AGENT,
|
||||
scope_id=agent_id,
|
||||
scope_id=scope_id,
|
||||
source_agent_id=item.get("source_agent_id"),
|
||||
source_user_id=current_user.id,
|
||||
)
|
||||
@@ -626,7 +629,7 @@ def import_memory(
|
||||
confidence=item.get("confidence", "medium"),
|
||||
metadata_=item.get("metadata") or item.get("tags"),
|
||||
scope_kind=SCOPE_AGENT,
|
||||
scope_id=agent_id,
|
||||
scope_id=scope_id,
|
||||
user_id=current_user.id,
|
||||
)
|
||||
db.add(ke)
|
||||
@@ -641,7 +644,7 @@ def import_memory(
|
||||
description=item.get("description"),
|
||||
weight=item.get("weight", "1.0"),
|
||||
scope_kind=SCOPE_AGENT,
|
||||
scope_id=agent_id,
|
||||
scope_id=scope_id,
|
||||
)
|
||||
db.add(kr)
|
||||
imported["knowledge_relations"] += 1
|
||||
@@ -650,7 +653,7 @@ def import_memory(
|
||||
lp = AgentLearningPattern(
|
||||
id=str(uuid.uuid4()),
|
||||
scope_kind=SCOPE_AGENT,
|
||||
scope_id=agent_id,
|
||||
scope_id=scope_id,
|
||||
task_category=item.get("task_category", "general"),
|
||||
task_keywords=item.get("task_keywords", ""),
|
||||
suggested_tools=item.get("suggested_tools", "[]"),
|
||||
@@ -668,7 +671,7 @@ def import_memory(
|
||||
vm = AgentVectorMemory(
|
||||
id=str(uuid.uuid4()),
|
||||
scope_kind=SCOPE_AGENT,
|
||||
scope_id=agent_id,
|
||||
scope_id=scope_id,
|
||||
session_key=item.get("session_key", ""),
|
||||
content_text=item.get("content_text", ""),
|
||||
metadata_=item.get("metadata"),
|
||||
|
||||
@@ -140,7 +140,9 @@ async def login(
|
||||
user = db.query(User).filter(User.username == form_data.username).first()
|
||||
|
||||
if not user or not verify_password(form_data.password, user.password_hash):
|
||||
logger.warning(f"登录失败: 用户名 {form_data.username}")
|
||||
logger.warning(f"登录失败: 用户名 {form_data.username}, user_found={user is not None}, pwd_len={len(form_data.password)}")
|
||||
if user:
|
||||
logger.warning(f" DB hash prefix: {user.password_hash[:30]}...")
|
||||
raise UnauthorizedError("用户名或密码错误")
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
Reference in New Issue
Block a user