Feat: Export Agent Logs. (#13658)

### What problem does this PR solve?
Feat: Export Agent Logs.

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: balibabu <assassin_cike@163.com>
This commit is contained in:
balibabu
2026-03-17 18:51:26 +08:00
committed by GitHub
parent fc4f1e2488
commit 6cae364ac2
13 changed files with 199 additions and 24 deletions

View File

@@ -688,6 +688,8 @@ async def set_session(canvas_id):
session_id=get_uuid()
canvas = Canvas(cvs.dsl, tenant_id, canvas_id, canvas_id=cvs.id)
canvas.reset()
# Get the version title for this canvas (using latest, not necessarily released)
version_title = UserCanvasVersionService.get_latest_version_title(cvs.id, release_mode=False)
conv = {
"id": session_id,
"name": req.get("name", ""),
@@ -697,7 +699,8 @@ async def set_session(canvas_id):
"message": [],
"source": "agent",
"dsl": cvs.dsl,
"reference": []
"reference": [],
"version_title": version_title
}
API4ConversationService.save(**conv)
return get_json_result(data=conv)

View File

@@ -32,6 +32,7 @@ from api.db.services.api_service import API4ConversationService
from api.db.services.canvas_service import UserCanvasService, completion_openai
from api.db.services.canvas_service import completion as agent_completion
from api.db.services.conversation_service import ConversationService
from api.db.services.user_canvas_version import UserCanvasVersionService
from api.db.services.conversation_service import async_iframe_completion as iframe_completion
from api.db.services.conversation_service import async_completion as rag_completion
from api.db.services.dialog_service import DialogService, async_ask, async_chat, gen_mindmap
@@ -104,8 +105,17 @@ async def create_agent_session(tenant_id, agent_id):
canvas.reset()
cvs.dsl = json.loads(str(canvas))
conv = {"id": session_id, "dialog_id": cvs.id, "user_id": user_id,
"message": [{"role": "assistant", "content": canvas.get_prologue()}], "source": "agent", "dsl": cvs.dsl}
# Get the version title based on release_mode
version_title = UserCanvasVersionService.get_latest_version_title(cvs.id, release_mode=release_mode)
conv = {
"id": session_id,
"dialog_id": cvs.id,
"user_id": user_id,
"message": [{"role": "assistant", "content": canvas.get_prologue()}],
"source": "agent",
"dsl": cvs.dsl,
"version_title": version_title
}
API4ConversationService.save(**conv)
conv["agent_id"] = conv.pop("dialog_id")
return get_result(data=conv)

View File

@@ -1034,6 +1034,7 @@ class API4Conversation(DataBaseModel):
round = IntegerField(default=0, index=True)
thumb_up = IntegerField(default=0, index=True)
errors = TextField(null=True, help_text="errors")
version_title = CharField(max_length=255, null=True, help_text="canvas version title when session created", index=False)
class Meta:
db_table = "api_4_conversation"
@@ -1632,6 +1633,7 @@ def migrate_db():
alter_db_add_column(migrator, "memory", "tenant_embd_id", IntegerField(null=True, help_text="id in tenant_llm", index=True))
alter_db_add_column(migrator, "memory", "tenant_llm_id", IntegerField(null=True, help_text="id in tenant_llm", index=True))
alter_db_add_column(migrator, "user_canvas_version", "release", BooleanField(null=False, help_text="is released", default=False, index=True))
alter_db_add_column(migrator, "api_4_conversation", "version_title", CharField(max_length=255, null=True, help_text="canvas version title when session created", index=False))
logging.disable(logging.NOTSET)
# this is after re-enabling logging to allow logging changed user emails
migrate_add_unique_email(migrator)

View File

@@ -250,7 +250,9 @@ async def completion(tenant_id, agent_id, session_id=None, **kwargs):
session_id = get_uuid()
canvas = Canvas(dsl, tenant_id, agent_id, canvas_id=cvs.id, custom_header=custom_header)
canvas.reset()
conv = {"id": session_id, "dialog_id": cvs.id, "user_id": user_id, "message": [], "source": "agent", "dsl": dsl, "reference": []}
# Get the version title based on release_mode
version_title = UserCanvasVersionService.get_latest_version_title(cvs.id, release_mode=release_mode == "true")
conv = {"id": session_id, "dialog_id": cvs.id, "user_id": user_id, "message": [], "source": "agent", "dsl": dsl, "reference": [], "version_title": version_title}
API4ConversationService.save(**conv)
conv = API4Conversation(**conv)

View File

@@ -91,15 +91,36 @@ class UserCanvasVersionService(CommonService):
@classmethod
@DB.connection_context()
def get_latest_released(cls, user_canvas_id):
def _get_latest_by_canvas_id(cls, user_canvas_id, only_released=False):
"""Get the latest version for a canvas, optionally filtered by release status."""
try:
return cls.model.select().where((cls.model.user_canvas_id == user_canvas_id) & (cls.model.release)).order_by(cls.model.create_time.desc()).first()
query = cls.model.select().where(cls.model.user_canvas_id == user_canvas_id)
if only_released:
query = query.where(cls.model.release)
return query.order_by(cls.model.create_time.desc()).first()
except DoesNotExist:
return None
except Exception as e:
logging.exception(e)
return None
@classmethod
def get_latest_released(cls, user_canvas_id):
"""Get the latest released version for a canvas."""
return cls._get_latest_by_canvas_id(user_canvas_id, only_released=True)
@classmethod
def get_latest_version_title(cls, user_canvas_id, release_mode=False):
"""Get the version title for a canvas based on release_mode.
Args:
user_canvas_id: The canvas ID
release_mode: If True, get the latest released version title;
If False, get the latest version title (regardless of release status)
"""
latest = cls._get_latest_by_canvas_id(user_canvas_id, only_released=release_mode)
return latest.title if latest else None
@classmethod
@DB.connection_context()
def save_or_replace_latest(cls, user_canvas_id, dsl, title=None, description=None, release=None):
@@ -137,9 +158,9 @@ class UserCanvasVersionService(CommonService):
return None, True
# Normal case: update existing version
# DSL unchanged: do NOT update title to preserve version identity
# Only update dsl (for normalization consistency), description, and release
update_data = {"dsl": normalized_dsl}
if title is not None:
update_data["title"] = title
if description is not None:
update_data["description"] = description
if release is not None: