fix: memory scope_id column overflow and API read mismatch
Some checks failed
CI/CD Pipeline / Backend — Lint & Test (push) Has been cancelled
CI/CD Pipeline / Frontend — Lint & Build (push) Has been cancelled
CI/CD Pipeline / Docker — Build Check (push) Has been cancelled

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:
2026-07-01 08:05:23 +08:00
parent e33aa6e0b7
commit f1326bff95
3 changed files with 78 additions and 40 deletions

View File

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

View File

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

View File

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