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

127 lines
4.8 KiB
Python

"""SQLAlchemy models for session lifecycle."""
from datetime import datetime, timezone
from sqlalchemy import Column, DateTime, Float, Integer, String, Text, UUID
from cognee.infrastructure.databases.relational import Base
class SessionRecord(Base):
"""One row per (user, session_id).
Narrow by design: lifecycle + aggregate counters only. The QA
entries and trace steps themselves live in the session cache
(Redis / FS) and are referenced here only indirectly via
``session_id`` (which is a string, not a FK — cache IDs aren't
FK-able).
Status is stored as a string but treated as an enum at the app
layer (see ``SessionStatus``). The ``abandoned`` transition is
inferred at read time from ``last_activity_at`` rather than
written — a running session that's been idle past the threshold
is reported as abandoned without any sweeper touching the row.
"""
__tablename__ = "session_records"
# Session ID is a caller-provided string (e.g. "cc_myproj_ab12cd34ef56"
# from the Claude Code plugin). Scoped per user — same string from
# two users is two different sessions.
session_id = Column(String, primary_key=True)
user_id = Column(UUID, primary_key=True, index=True)
dataset_id = Column(UUID, nullable=True, index=True)
# Stored status. "abandoned" is the only value inferred at read
# time instead of being stored — everything else (running,
# completed, failed) is explicitly set.
status = Column(String, nullable=False, default="running", index=True)
started_at = Column(
DateTime(timezone=True),
nullable=False,
default=lambda: datetime.now(timezone.utc),
)
last_activity_at = Column(
DateTime(timezone=True),
nullable=False,
default=lambda: datetime.now(timezone.utc),
index=True,
)
ended_at = Column(DateTime(timezone=True), nullable=True)
# Aggregate counters accumulated by LLMGateway / SessionManager hooks.
tokens_in = Column(Integer, nullable=False, default=0)
tokens_out = Column(Integer, nullable=False, default=0)
cost_usd = Column(Float, nullable=False, default=0.0)
# Cheap per-session counters useful for the dashboard without
# scanning the cache.
error_count = Column(Integer, nullable=False, default=0)
# Last-seen model string — informational only. Per-model cost
# aggregates live in ``SessionModelUsage`` so mixed-model sessions
# attribute correctly.
last_model = Column(Text, nullable=True)
def to_dict(self) -> dict:
started = getattr(self, "started_at", None)
last_act = getattr(self, "last_activity_at", None)
ended = getattr(self, "ended_at", None)
dataset = getattr(self, "dataset_id", None)
return {
"session_id": self.session_id,
"user_id": str(self.user_id),
"dataset_id": str(dataset) if dataset is not None else None,
"status": self.status,
"started_at": started.isoformat() if started is not None else None,
"last_activity_at": last_act.isoformat() if last_act is not None else None,
"ended_at": ended.isoformat() if ended is not None else None,
"tokens_in": self.tokens_in,
"tokens_out": self.tokens_out,
"cost_usd": self.cost_usd,
"error_count": self.error_count,
"last_model": self.last_model,
}
class SessionModelUsage(Base):
"""Per-(session, user, model) token + cost aggregate.
Populated by ``accumulate_usage`` when an LLM call fires inside a
tracked session scope. Normalizing this out of ``SessionRecord``
lets mixed-model sessions (e.g. embedding calls + completion calls
on different models) attribute cost correctly in
``GET /api/v1/sessions/cost-by-model``.
"""
__tablename__ = "session_model_usage"
session_id = Column(String, primary_key=True)
user_id = Column(UUID, primary_key=True, index=True)
model = Column(Text, primary_key=True)
tokens_in = Column(Integer, nullable=False, default=0)
tokens_out = Column(Integer, nullable=False, default=0)
cost_usd = Column(Float, nullable=False, default=0.0)
updated_at = Column(
DateTime(timezone=True),
nullable=False,
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
)
def to_dict(self) -> dict:
updated = getattr(self, "updated_at", None)
return {
"session_id": self.session_id,
"user_id": str(self.user_id),
"model": self.model,
"tokens_in": self.tokens_in,
"tokens_out": self.tokens_out,
"cost_usd": self.cost_usd,
"updated_at": updated.isoformat() if updated is not None else None,
}