feat: 添加 points_audit_ledger 及 JSON 字段 Pydantic Schema 约束

This commit is contained in:
qzl
2026-04-10 12:28:18 +08:00
parent 46513829cd
commit 0ac8b81a66
34 changed files with 2595 additions and 1757 deletions
@@ -9,6 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from models.agent_chat_message import AgentChatMessage
from models.agent_chat_session import AgentChatSession
from schemas.domain.chat_session import SessionStateSnapshot
from schemas.enums import AgentChatMessageRole, AgentChatSessionStatus
@@ -80,13 +81,13 @@ class SessionRepository:
*,
chat_session: AgentChatSession,
status: AgentChatSessionStatus,
state_snapshot: dict[str, object],
state_snapshot: SessionStateSnapshot,
message_delta: int,
token_delta: int = 0,
cost_delta: Decimal = Decimal("0"),
) -> None:
chat_session.status = status
chat_session.state_snapshot = state_snapshot
chat_session.state_snapshot = state_snapshot.model_dump(mode="json")
chat_session.last_activity_at = datetime.now(timezone.utc)
chat_session.message_count += message_delta
chat_session.total_tokens += token_delta
+3 -1
View File
@@ -16,6 +16,7 @@ from schemas.agent.system_agent import AgentType
from schemas.agent.runtime_models import AgentOutput, FollowUpOutput, ToolAgentOutput
from schemas.agent.visibility import SystemVisibilityBit, bit_mask
from schemas.domain.chat_message import AgentChatMessageMetadata
from schemas.domain.chat_session import SessionStateSnapshot
class EventStore(Protocol):
@@ -382,11 +383,12 @@ class SqlAlchemyEventStore:
token_delta: int = 0,
cost_delta: Decimal = Decimal("0"),
) -> None:
snapshot = (
snapshot_raw = (
chat_session.state_snapshot
if isinstance(chat_session.state_snapshot, dict)
else {}
)
snapshot = SessionStateSnapshot.model_validate(snapshot_raw)
await session_repo.update_runtime_state(
chat_session=chat_session,
status=status,
+30 -24
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
import asyncio
import base64
import json
from typing import Any, cast
@@ -53,7 +54,7 @@ _RUNTIME_AGENT_OUTPUT_ADAPTER = TypeAdapter(RuntimeAgentOutput)
def _serialize_tool_agent_output(
*,
metadata: AgentChatMessageMetadata | dict[str, object] | None,
metadata: AgentChatMessageMetadata | None,
) -> str | None:
if metadata is None:
return None
@@ -80,30 +81,13 @@ def _serialize_tool_agent_output(
def _serialize_assistant_context_from_metadata(
*,
metadata: AgentChatMessageMetadata | dict[str, object] | None,
metadata: AgentChatMessageMetadata | None,
fallback_content: str,
) -> str:
if metadata is None:
return fallback_content
try:
resolved_metadata = (
metadata
if isinstance(metadata, AgentChatMessageMetadata)
else AgentChatMessageMetadata.model_validate(metadata)
)
except Exception:
return fallback_content
agent_output = resolved_metadata.agent_output
if agent_output is None and isinstance(metadata, dict):
raw = metadata.get("agent_output")
if raw is not None:
try:
agent_output = _RUNTIME_AGENT_OUTPUT_ADAPTER.validate_python(raw)
except Exception:
return fallback_content
agent_output = metadata.agent_output
if agent_output is None:
return fallback_content
@@ -226,11 +210,11 @@ async def _build_recent_context_messages(
content_raw = msg.get("content", "")
content: str = content_raw if isinstance(content_raw, str) else ""
metadata_raw = msg.get("metadata")
metadata: AgentChatMessageMetadata | dict[str, object] | None
metadata: AgentChatMessageMetadata | None
if isinstance(metadata_raw, AgentChatMessageMetadata):
metadata = metadata_raw
elif isinstance(metadata_raw, dict):
metadata = metadata_raw
metadata = AgentChatMessageMetadata.model_validate(metadata_raw)
else:
metadata = None
@@ -391,6 +375,7 @@ async def run_agentscope_task(command: dict[str, Any]) -> dict[str, object]:
context_config=runtime_config.context,
)
points_service = PointsService(repository=PointsRepository(session))
try:
await runtime.run(
run_input=run_input,
@@ -399,15 +384,36 @@ async def run_agentscope_task(command: dict[str, Any]) -> dict[str, object]:
runtime_config=runtime_config,
cancel_checker=_cancel_checker,
)
points_service = PointsService(repository=PointsRepository(session))
await points_service.consume_successful_run_points(
user_id=owner_id,
session_id=UUID(thread_id),
run_id=run_id,
operator_id=owner_id,
user_email=owner_email,
)
await session.commit()
except asyncio.CancelledError:
await points_service.record_failed_run_platform_cost(
user_id=owner_id,
session_id=UUID(thread_id),
run_id=run_id,
operator_id=owner_id,
user_email=owner_email,
failure_kind="canceled",
)
await session.commit()
raise
except Exception:
await points_service.record_failed_run_platform_cost(
user_id=owner_id,
session_id=UUID(thread_id),
run_id=run_id,
operator_id=owner_id,
user_email=owner_email,
failure_kind="failed",
)
await session.commit()
raise
finally:
delete_fn = getattr(redis_client, "delete", None)
if callable(delete_fn):