diff --git a/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt b/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt index f1eac1a..bf8b93b 100644 --- a/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt +++ b/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt @@ -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(), diff --git a/backend/app/api/agent_memory.py b/backend/app/api/agent_memory.py index 6b49617..e9dedc0 100644 --- a/backend/app/api/agent_memory.py +++ b/backend/app/api/agent_memory.py @@ -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"), diff --git a/backend/app/api/auth.py b/backend/app/api/auth.py index aa074b1..27bce42 100644 --- a/backend/app/api/auth.py +++ b/backend/app/api/auth.py @@ -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