Files
wehub-resource-sync c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:02:24 +08:00

300 lines
9.8 KiB
Python

from __future__ import annotations
from datetime import datetime, timedelta, timezone
from typing import Any, Literal, Optional
from uuid import UUID as UUIDType
from sqlalchemy import select
from cognee.infrastructure.databases.relational import get_relational_engine
from cognee.modules.agents.models import (
AgentConnection,
AgentDatasetRef,
AgentDetailResponse,
AgentsListResponse,
MemorySourceConnection,
RegisterAgentRequest,
)
from cognee.modules.agents.registry import (
classify_memory_source_type,
list_persisted_agent_connections,
list_registered_agent_connections,
register_agent_connection,
)
from cognee.modules.users.exceptions import PermissionDeniedError
from cognee.modules.users.models import User
from cognee.modules.users.permissions.methods.get_specific_user_permission_datasets import (
get_specific_user_permission_datasets,
)
from cognee.shared.logging_utils import get_logger
logger = get_logger("agents")
RangeLiteral = Literal["24h", "7d", "30d", "all"]
def _range_since(range_key: RangeLiteral) -> Optional[datetime]:
now = datetime.now(timezone.utc)
if range_key == "24h":
return now - timedelta(hours=24)
if range_key == "7d":
return now - timedelta(days=7)
if range_key == "30d":
return now - timedelta(days=30)
return None
def _entry_to_dict(entry: Any) -> dict[str, Any]:
if isinstance(entry, dict):
return entry
if hasattr(entry, "model_dump"):
return entry.model_dump(mode="json")
if hasattr(entry, "dict"):
return entry.dict()
return {"value": str(entry)}
async def _readable_datasets_for(user: User) -> list[Any]:
try:
return await get_specific_user_permission_datasets(user.id, "read", None)
except PermissionDeniedError:
return []
except Exception as error:
logger.warning("Failed to resolve readable datasets for agents API: %s", error)
return []
async def _child_agent_user_ids(user_id: UUIDType) -> list[UUIDType]:
try:
engine = get_relational_engine()
async with engine.get_async_session() as session:
from cognee.modules.users.models import User as UserModel
rows = (
await session.execute(
select(UserModel.id).where(UserModel.parent_user_id == user_id)
)
).all()
return [row.id for row in rows]
except Exception as error:
logger.debug("Failed to resolve child agent users for agents API: %s", error)
return []
async def _visible_user_ids(user: User) -> list[UUIDType]:
ids = [user.id]
ids.extend(await _child_agent_user_ids(user.id))
return ids
def _memory_sources_from_datasets(datasets: list[Any]) -> list[MemorySourceConnection]:
sources = []
for dataset in datasets:
dataset_id = getattr(dataset, "id", None)
dataset_name = getattr(dataset, "name", None)
if dataset_id is None or dataset_name is None:
continue
sources.append(
MemorySourceConnection(
id=str(dataset_id),
name=str(dataset_name),
type=classify_memory_source_type(str(dataset_name)),
owner_id=getattr(dataset, "owner_id", None),
tenant_id=getattr(dataset, "tenant_id", None),
status="active",
)
)
return sources
def _is_visible_registered_agent(
agent: AgentConnection,
*,
visible_user_ids: set[UUIDType],
permitted_dataset_ids: set[str],
) -> bool:
if agent.user_id and agent.user_id in visible_user_ids:
return True
if any(dataset.id in permitted_dataset_ids for dataset in agent.datasets if dataset.id):
return True
return agent.user_id is None and not agent.datasets
def _merge_agents(agents: list[AgentConnection]) -> list[AgentConnection]:
merged: dict[str, AgentConnection] = {}
for agent in agents:
existing = merged.get(agent.id)
if existing is None:
merged[agent.id] = agent
continue
existing_ts = existing.last_active_at or datetime.min.replace(tzinfo=timezone.utc)
agent_ts = agent.last_active_at or datetime.min.replace(tzinfo=timezone.utc)
if agent_ts >= existing_ts:
metadata = {**existing.metadata, **agent.metadata}
merged[agent.id] = agent.model_copy(update={"metadata": metadata})
else:
metadata = {**agent.metadata, **existing.metadata}
merged[agent.id] = existing.model_copy(update={"metadata": metadata})
return sorted(
merged.values(),
key=lambda item: item.last_active_at or datetime.min.replace(tzinfo=timezone.utc),
reverse=True,
)
def _attach_connected_agent_ids(
memory_sources: list[MemorySourceConnection],
agents: list[AgentConnection],
) -> list[MemorySourceConnection]:
agents_by_dataset: dict[str, list[str]] = {}
for agent in agents:
for dataset in agent.datasets:
if dataset.id:
agents_by_dataset.setdefault(dataset.id, []).append(agent.id)
return [
source.model_copy(
update={"connected_agent_ids": sorted(set(agents_by_dataset.get(source.id, [])))}
)
for source in memory_sources
]
async def list_agent_connections(
*,
user: User,
agent_id: Optional[UUIDType] = None,
range_key: RangeLiteral = "30d",
status_filter: Optional[str] = None,
include_sources: bool = True,
active_only: bool = True,
limit: int = 50,
offset: int = 0,
) -> AgentsListResponse:
readable_datasets = await _readable_datasets_for(user)
memory_sources = _memory_sources_from_datasets(readable_datasets)
visible_user_ids = await _visible_user_ids(user)
visible_user_id_set = set(visible_user_ids)
permitted_dataset_id_strings = {source.id for source in memory_sources}
registered_agents = [
agent
for agent in list_registered_agent_connections()
if _is_visible_registered_agent(
agent,
visible_user_ids=visible_user_id_set,
permitted_dataset_ids=permitted_dataset_id_strings,
)
]
persisted_agents = await list_persisted_agent_connections(
visible_user_ids, active_only=active_only
)
agents = _merge_agents([*registered_agents, *persisted_agents])
if agent_id:
agent_id_str = str(agent_id)
agents = [
agent
for agent in agents
if agent.user_id is not None and str(agent.user_id) == agent_id_str
]
if status_filter:
agents = [agent for agent in agents if agent.status == status_filter]
memory_sources = _attach_connected_agent_ids(memory_sources, agents) if include_sources else []
total = len(agents)
sliced_agents = agents[offset : offset + limit]
return AgentsListResponse(
agents=sliced_agents,
memory_sources=memory_sources,
total=total,
limit=limit,
offset=offset,
has_more=offset + len(sliced_agents) < total,
)
async def get_agent_connection_detail(
*,
user: User,
agent_id: UUIDType,
agent_session_name: Optional[str] = None,
) -> Optional[AgentDetailResponse]:
response = await list_agent_connections(
user=user,
agent_id=agent_id,
include_sources=True,
limit=10000,
offset=0,
)
if agent_session_name:
matching = [
item for item in response.agents if item.agent_session_name == agent_session_name
]
else:
matching = list(response.agents)
agent = matching[0] if matching else None
if agent is None:
return None
recent_sessions = []
recent_traces = []
recent_qas = []
if agent.session_id and agent.user_id:
try:
from cognee.infrastructure.session.get_session_manager import get_session_manager
session_manager = get_session_manager()
qas = await session_manager.get_session(
user_id=agent.user_id,
session_id=agent.session_id,
formatted=False,
)
traces = await session_manager.get_agent_trace_session(
user_id=agent.user_id,
session_id=agent.session_id,
last_n=20,
)
recent_qas = (
[_entry_to_dict(entry) for entry in qas[-20:]] if isinstance(qas, list) else []
)
recent_traces = [_entry_to_dict(entry) for entry in traces[-20:]]
recent_sessions = [{"session_id": agent.session_id, "user_id": agent.user_id}]
except Exception as error:
logger.debug("Failed to hydrate agent detail from session cache: %s", error)
return AgentDetailResponse(
agent=agent,
memory_sources=response.memory_sources,
recent_sessions=recent_sessions,
recent_traces=recent_traces,
recent_qas=recent_qas,
)
async def register_agent_from_request(user: User, request: RegisterAgentRequest) -> AgentConnection:
datasets = [
AgentDatasetRef(id=dataset_id, role="read_write", type="dataset")
for dataset_id in request.dataset_ids
]
datasets.extend(
AgentDatasetRef(
name=dataset_name,
role="read_write",
type=classify_memory_source_type(dataset_name),
)
for dataset_name in request.dataset_names
)
return await register_agent_connection(
agent_session_name=request.agent_session_name,
connection_type=request.type,
memory_mode=request.memory_mode,
source=request.source,
origin_function=request.origin_function,
user_id=user.id,
tenant_id=getattr(user, "tenant_id", None),
session_id=request.session_id,
datasets=datasets,
metadata=request.metadata,
)