mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
refactor(copilot): replace dryRun Boolean column with metadata Json column
Replace the single-purpose `dryRun Boolean` column on `AgentChatSession`
with an extensible `metadata Json` column backed by a typed Pydantic model
(`ChatSessionMetadata`). This avoids DB migrations for future session-level
flags — new fields can be added to the model with defaults.
- Schema: `dryRun Boolean @default(false)` -> `metadata Json @default("{}")`
- Model: `ChatSessionMetadata(dry_run=False)` with convenience property
- API: `SessionDetailResponse.dry_run` -> `.metadata`
- Migration: data-preserving (migrates existing dryRun=true rows)
- All existing `session.dry_run` reads still work via the property
This commit is contained in:
@@ -20,6 +20,7 @@ from backend.copilot.executor.utils import enqueue_cancel_task, enqueue_copilot_
|
||||
from backend.copilot.model import (
|
||||
ChatMessage,
|
||||
ChatSession,
|
||||
ChatSessionMetadata,
|
||||
append_and_save_message,
|
||||
create_chat_session,
|
||||
delete_chat_session,
|
||||
@@ -124,7 +125,7 @@ class CreateSessionResponse(BaseModel):
|
||||
id: str
|
||||
created_at: str
|
||||
user_id: str | None
|
||||
dry_run: bool = False
|
||||
metadata: ChatSessionMetadata = ChatSessionMetadata()
|
||||
|
||||
|
||||
class ActiveStreamInfo(BaseModel):
|
||||
@@ -145,7 +146,7 @@ class SessionDetailResponse(BaseModel):
|
||||
active_stream: ActiveStreamInfo | None = None # Present if stream is still active
|
||||
total_prompt_tokens: int = 0
|
||||
total_completion_tokens: int = 0
|
||||
dry_run: bool = False
|
||||
metadata: ChatSessionMetadata = ChatSessionMetadata()
|
||||
|
||||
|
||||
class SessionSummaryResponse(BaseModel):
|
||||
@@ -286,7 +287,7 @@ async def create_session(
|
||||
id=session.session_id,
|
||||
created_at=session.started_at.isoformat(),
|
||||
user_id=session.user_id,
|
||||
dry_run=session.dry_run,
|
||||
metadata=session.metadata,
|
||||
)
|
||||
|
||||
|
||||
@@ -435,7 +436,7 @@ async def get_session(
|
||||
active_stream=active_stream_info,
|
||||
total_prompt_tokens=total_prompt,
|
||||
total_completion_tokens=total_completion,
|
||||
dry_run=session.dry_run,
|
||||
metadata=session.metadata,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from prisma.types import (
|
||||
from backend.data import db
|
||||
from backend.util.json import SafeJson, sanitize_string
|
||||
|
||||
from .model import ChatMessage, ChatSession, ChatSessionInfo
|
||||
from .model import ChatMessage, ChatSession, ChatSessionInfo, ChatSessionMetadata
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -35,7 +35,7 @@ async def get_chat_session(session_id: str) -> ChatSession | None:
|
||||
async def create_chat_session(
|
||||
session_id: str,
|
||||
user_id: str,
|
||||
dry_run: bool = False,
|
||||
metadata: ChatSessionMetadata | None = None,
|
||||
) -> ChatSessionInfo:
|
||||
"""Create a new chat session in the database."""
|
||||
data = ChatSessionCreateInput(
|
||||
@@ -44,7 +44,7 @@ async def create_chat_session(
|
||||
credentials=SafeJson({}),
|
||||
successfulAgentRuns=SafeJson({}),
|
||||
successfulAgentSchedules=SafeJson({}),
|
||||
dryRun=dry_run,
|
||||
metadata=SafeJson((metadata or ChatSessionMetadata()).model_dump()),
|
||||
)
|
||||
prisma_session = await PrismaChatSession.prisma().create(data=data)
|
||||
return ChatSessionInfo.from_db(prisma_session)
|
||||
|
||||
@@ -46,6 +46,16 @@ def _get_session_cache_key(session_id: str) -> str:
|
||||
# ===================== Chat data models ===================== #
|
||||
|
||||
|
||||
class ChatSessionMetadata(BaseModel):
|
||||
"""Typed metadata stored in the ``metadata`` JSON column of ChatSession.
|
||||
|
||||
Add new session-level flags here instead of adding DB columns —
|
||||
no migration required for new fields as long as a default is provided.
|
||||
"""
|
||||
|
||||
dry_run: bool = False
|
||||
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
role: str
|
||||
content: str | None = None
|
||||
@@ -88,7 +98,12 @@ class ChatSessionInfo(BaseModel):
|
||||
updated_at: datetime
|
||||
successful_agent_runs: dict[str, int] = {}
|
||||
successful_agent_schedules: dict[str, int] = {}
|
||||
dry_run: bool = False
|
||||
metadata: ChatSessionMetadata = ChatSessionMetadata()
|
||||
|
||||
@property
|
||||
def dry_run(self) -> bool:
|
||||
"""Convenience accessor for ``metadata.dry_run``."""
|
||||
return self.metadata.dry_run
|
||||
|
||||
@classmethod
|
||||
def from_db(cls, prisma_session: PrismaChatSession) -> Self:
|
||||
@@ -102,6 +117,10 @@ class ChatSessionInfo(BaseModel):
|
||||
prisma_session.successfulAgentSchedules, default={}
|
||||
)
|
||||
|
||||
# Parse typed metadata from the JSON column.
|
||||
raw_metadata = _parse_json_field(prisma_session.metadata, default={})
|
||||
metadata = ChatSessionMetadata.model_validate(raw_metadata or {})
|
||||
|
||||
# Calculate usage from token counts.
|
||||
# NOTE: Per-turn cache_read_tokens / cache_creation_tokens breakdown
|
||||
# is lost after persistence — the DB only stores aggregate prompt and
|
||||
@@ -127,7 +146,7 @@ class ChatSessionInfo(BaseModel):
|
||||
updated_at=prisma_session.updatedAt,
|
||||
successful_agent_runs=successful_agent_runs,
|
||||
successful_agent_schedules=successful_agent_schedules,
|
||||
dry_run=prisma_session.dryRun,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
|
||||
@@ -145,7 +164,7 @@ class ChatSession(ChatSessionInfo):
|
||||
credentials={},
|
||||
started_at=datetime.now(UTC),
|
||||
updated_at=datetime.now(UTC),
|
||||
dry_run=dry_run,
|
||||
metadata=ChatSessionMetadata(dry_run=dry_run),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -533,7 +552,7 @@ async def _save_session_to_db(
|
||||
await db.create_chat_session(
|
||||
session_id=session.session_id,
|
||||
user_id=session.user_id,
|
||||
dry_run=session.dry_run,
|
||||
metadata=session.metadata,
|
||||
)
|
||||
existing_message_count = 0
|
||||
|
||||
@@ -631,7 +650,7 @@ async def create_chat_session(user_id: str, *, dry_run: bool = False) -> ChatSes
|
||||
await chat_db().create_chat_session(
|
||||
session_id=session.session_id,
|
||||
user_id=user_id,
|
||||
dry_run=dry_run,
|
||||
metadata=session.metadata,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create session {session.session_id} in database: {e}")
|
||||
|
||||
@@ -1,2 +1,13 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "ChatSession" ADD COLUMN "dryRun" BOOLEAN NOT NULL DEFAULT false;
|
||||
-- Replace dryRun Boolean column with extensible metadata Json column.
|
||||
-- Migrate existing dryRun values into the new metadata JSON.
|
||||
|
||||
-- Step 1: Add the new metadata column with a default empty JSON object.
|
||||
ALTER TABLE "ChatSession" ADD COLUMN "metadata" JSONB NOT NULL DEFAULT '{}';
|
||||
|
||||
-- Step 2: Migrate existing dryRun=true rows into the metadata column.
|
||||
UPDATE "ChatSession"
|
||||
SET "metadata" = jsonb_build_object('dry_run', true)
|
||||
WHERE "dryRun" = true;
|
||||
|
||||
-- Step 3: Drop the old dryRun column.
|
||||
ALTER TABLE "ChatSession" DROP COLUMN "dryRun";
|
||||
|
||||
@@ -220,9 +220,9 @@ model ChatSession {
|
||||
successfulAgentRuns Json @default("{}") // Map of graph_id -> count
|
||||
successfulAgentSchedules Json @default("{}") // Map of graph_id -> count
|
||||
|
||||
// Dry-run mode: when true, all tool calls (run_block, run_agent) are forced
|
||||
// to use dry-run simulation — no real API calls or side effects.
|
||||
dryRun Boolean @default(false)
|
||||
// Extensible session metadata (typed via ChatSessionMetadata in Python).
|
||||
// Avoids DB migrations for each new flag (e.g. dry_run, future fields).
|
||||
metadata Json @default("{}")
|
||||
|
||||
// Usage tracking
|
||||
totalPromptTokens Int @default(0)
|
||||
|
||||
@@ -8384,6 +8384,14 @@
|
||||
"required": ["query", "conversation_history", "message_id"],
|
||||
"title": "ChatRequest"
|
||||
},
|
||||
"ChatSessionMetadata": {
|
||||
"properties": {
|
||||
"dry_run": { "type": "boolean", "title": "Dry Run", "default": false }
|
||||
},
|
||||
"type": "object",
|
||||
"title": "ChatSessionMetadata",
|
||||
"description": "Typed metadata stored in the ``metadata`` JSON column of ChatSession.\n\nAdd new session-level flags here instead of adding DB columns —\nno migration required for new fields as long as a default is provided."
|
||||
},
|
||||
"ClarificationNeededResponse": {
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -8529,7 +8537,10 @@
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }],
|
||||
"title": "User Id"
|
||||
},
|
||||
"dry_run": { "type": "boolean", "title": "Dry Run", "default": false }
|
||||
"metadata": {
|
||||
"$ref": "#/components/schemas/ChatSessionMetadata",
|
||||
"default": { "dry_run": false }
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["id", "created_at", "user_id"],
|
||||
@@ -12128,7 +12139,10 @@
|
||||
"title": "Total Completion Tokens",
|
||||
"default": 0
|
||||
},
|
||||
"dry_run": { "type": "boolean", "title": "Dry Run", "default": false }
|
||||
"metadata": {
|
||||
"$ref": "#/components/schemas/ChatSessionMetadata",
|
||||
"default": { "dry_run": false }
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["id", "created_at", "updated_at", "user_id", "messages"],
|
||||
|
||||
Reference in New Issue
Block a user