This commit is contained in:
liubiren 2026-07-25 18:54:46 +08:00
parent d712a0e137
commit b0323d256a
7 changed files with 283 additions and 303 deletions

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
"""
领域模型
"""
from datetime import datetime
from typing import Dict, List
from pydantic_ai._uuid import uuid7
from pydantic import BaseModel, Field
class Thought(BaseModel):
"""
思考节点领域模型
"""
id: str = Field(..., description="思考唯一标识")
type: str = Field(..., description="思考类型")
content: str = Field(..., description="思考内容")
class Dialog(BaseModel):
"""
对话领域模型
"""
id: str = Field(..., description="对话唯一标识")
user_prompt: str = Field(..., description="用户提示词")
thoughts: List[Thought] = Field(default_factory=list, description="思考列表")
result_output: str = Field(default="", description="结果输出")
is_thinking: bool = Field(
default=False, description="正在思考True 表示正在思考False 表示未正在思考"
)
is_expanded: bool = Field(
default=False, description="思考折叠面板展开状态True 表示展开False 表示折叠"
)
class Conversation(BaseModel):
"""
会话领域模型
"""
id: str = Field(default_factory=lambda: str(uuid7()), description="会话唯一标识")
description: str = Field(default="新会话", description="会话描述")
is_actived: bool = Field(
default=False, description="已激活True 表示激活False 表示不激活"
)
is_running: bool = Field(
default=False, description="正在运行True 表示正在运行False 表示未正在运行"
)
dialogs: List[Dialog] = Field(default_factory=list, description="对话列表")
created_at: datetime = Field(default_factory=datetime.now, description="创建时间")
class ConversationHistoryItem(BaseModel):
"""
会话历史项领域模型
"""
id: str = Field(..., description="会话唯一标识")
description: str = Field(..., description="会话描述")
created_at: str = Field(..., description="会话创建时间")

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
from application.models.tables import (
CaptchaRecord,
ConversationRecord,
DialogRecord,
RunResultRecord,
UserRecord,
)
from application.models.domains import (
Conversation,
Dialog,
ThoughtNode,
)
__all__ = [
"CaptchaRecord",
"Conversation",
"ConversationRecord",
"Dialog",
"DialogRecord",
"RunResultRecord",
"ThoughtNode",
"UserRecord",
]

View File

@ -1,48 +0,0 @@
# -*- coding: utf-8 -*-
"""
reflex运行时领域模型
"""
from datetime import datetime
from typing import Dict
from pydantic import BaseModel, Field
class ThoughtNode(BaseModel):
"""
思考节点领域模型
"""
kind: str
content: str
class Dialog(BaseModel):
"""
对话领域模型
"""
question: str = Field(..., description="问题")
is_thinking: bool = Field(
default=False, description="思考状态True 表示思考中False 表示思考完成"
)
is_collapse_expanded: bool = Field(
default=False, description="思考折叠面板展开状态True 表示展开False 表示折叠"
)
thought_nodes: dict[int, ThoughtNode] = Field(
default_factory=dict, description="思考节点字典"
)
answer: str = Field(default="", description="回答")
class Conversation(BaseModel):
"""
会话领域模型
"""
description: str = Field(default="新会话", description="会话描述")
is_running: bool = Field(
default=False, description="运行状态True 表示运行中False 表示运行完成"
)
dialogs: Dict[str, Dialog] = Field(default_factory=dict, description="对话字典")
created_at: datetime = Field(default_factory=datetime.now, description="创建时间")

View File

@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
"""
数据表模型
"""
from datetime import datetime, timedelta
from random import choices
from pydantic_ai._uuid import uuid7
from sqlmodel import Field, JSON, SQLModel
class CaptchaRecord(SQLModel, table=True):
"""
验证码记录
邮箱验证码和失效时间联合作为主键
"""
email: str = Field(..., primary_key=True, description="邮箱")
captcha: str = Field(
default_factory=lambda: "".join(choices("0123456789", k=6)),
primary_key=True,
description="验证码",
)
is_valid: bool = Field(
default=True,
index=True,
description="验证码有效True 表示有效False 表示无效",
)
is_verified: bool = Field(
default=False,
index=True,
description="验证码已核验True 表示已核验False 表示未核验",
)
expired_at: datetime = Field(
default_factory=lambda: datetime.now() + timedelta(minutes=30),
primary_key=True,
description="失效时间",
)
# 用户记录
class UserRecord(SQLModel, table=True):
id: str = Field(
default_factory=lambda: str(uuid7()),
primary_key=True,
description="用户唯一标识",
)
email: str = Field(..., index=True, description="邮箱")
# 会话表
class ConversationRecord(SQLModel, table=True):
id: str = Field(
default_factory=lambda: str(uuid7()),
primary_key=True,
description="会话唯一标识",
)
user_id: str = Field(..., index=True, description="用户唯一标识")
description: str = Field(..., description="会话描述")
created_at: datetime = Field(default_factory=datetime.now, description="创建时间")
is_deleted: bool = Field(
default=False,
index=True,
description="已删除True 表示已删除False 表示未删除",
)
# 思考节点表(仅定义)
class ThoughtNodeRecord(SQLModel):
kind: str = Field(description="思考节点类型")
content: str = Field(default="", description="思考节点内容")
# 对话表
class DialogRecord(SQLModel, table=True):
id: str = Field(
default_factory=lambda: str(uuid7()),
primary_key=True,
description="对话唯一标识",
)
conversation_id: str = Field(..., index=True, description="会话唯一标识")
question: str = Field(..., description="问题")
thought_nodes: dict[int, ThoughtNodeRecord] = Field(
default_factory=dict, sa_type=JSON, description="思考节点字典"
)
answer: str = Field(default="", description="回答")
# 运行结果表
class RunResultRecord(SQLModel, table=True):
conversation_id: str = Field(primary_key=True, description="会话唯一标识")
dialog_id: str = Field(primary_key=True, description="对话唯一标识")
new_messages: str = Field(description="新增消息")

View File

@ -235,10 +235,9 @@ class AuthState(rx.State):
# 创建用户记录 # 创建用户记录
user_id = await database_state.create_user_record(email=self.email) user_id = await database_state.create_user_record(email=self.email)
# 获取会话状态 # 就会话状态设置用户唯一标识
conversation_state = await self.get_state(ConversationState) conversation_state = await self.get_state(ConversationState)
# 加载 await conversation_state.set_user_id(user_id=user_id)
await conversation_state.load(user_id=user_id)
self.user_id = user_id self.user_id = user_id
@ -269,7 +268,7 @@ class AuthState(rx.State):
conversation_state = await self.get_state(ConversationState) conversation_state = await self.get_state(ConversationState)
conversation_state.user_id = "" conversation_state.user_id = ""
conversation_state.conversations = {} conversation_state.conversations = []
conversation_state.conversation_id = "" conversation_state.conversation_id = ""
conversation_state.is_conversation_history_shown = False conversation_state.is_conversation_history_shown = False
conversation_state.is_conversation_creating = False conversation_state.is_conversation_creating = False

View File

@ -3,9 +3,7 @@
会话状态 会话状态
""" """
from datetime import datetime from datetime import datetime
from typing import Any, AsyncGenerator, Dict from typing import Any, AsyncGenerator, Dict, List
from pydantic import BaseModel, Field
from pydantic_ai import Agent, ThinkingPartDelta from pydantic_ai import Agent, ThinkingPartDelta
from pydantic_ai.messages import ( from pydantic_ai.messages import (
FunctionToolCallEvent, FunctionToolCallEvent,
@ -26,6 +24,7 @@ from pydantic_ai.run import AgentRunResultEvent
import reflex as rx import reflex as rx
from application.states.database import DatabaseState from application.states.database import DatabaseState
from application.domain_models import Conversation, Dialog, ConversationHistoryItem
instructions: str = """ instructions: str = """
# 角色 # 角色
@ -59,46 +58,6 @@ agent: Agent = Agent(
) )
class ThoughtNode(BaseModel):
"""
思考节点领域模型
"""
kind: str
content: str
class Dialog(BaseModel):
"""
对话领域模型
"""
question: str = Field(..., description="问题")
is_thinking: bool = Field(
default=False, description="思考状态True 表示思考中False 表示思考完成"
)
is_collapse_expanded: bool = Field(
default=False, description="思考折叠面板展开状态True 表示展开False 表示折叠"
)
thought_nodes: dict[int, ThoughtNode] = Field(
default_factory=dict, description="思考节点字典"
)
answer: str = Field(default="", description="回答")
class Conversation(BaseModel):
"""
会话领域模型
"""
description: str = Field(default="新会话", description="会话描述")
is_running: bool = Field(
default=False, description="运行状态True 表示运行中False 表示运行完成"
)
dialogs: Dict[str, Dialog] = Field(default_factory=dict, description="对话字典")
created_at: datetime = Field(default_factory=datetime.now, description="创建时间")
class ConversationState(rx.State): class ConversationState(rx.State):
""" """
会话状态 会话状态
@ -106,9 +65,8 @@ class ConversationState(rx.State):
# 当前用户唯一标识 # 当前用户唯一标识
user_id: str = "" user_id: str = ""
# 会话列表
# 当前用户的会话字典 conversations: Dict[str, Conversation] = {} # 按照会话唯一标识顺序排序
conversations: Dict[str, Conversation] = {}
# 当前会话唯一标识 # 当前会话唯一标识
conversation_id: str = "" conversation_id: str = ""
@ -118,9 +76,9 @@ class ConversationState(rx.State):
# 会话创建状态True表示正在创建False表示未正在创建 # 会话创建状态True表示正在创建False表示未正在创建
is_conversation_creating: bool = False is_conversation_creating: bool = False
async def load(self, user_id: str) -> None: async def set_user_id(self, user_id: str) -> None:
""" """
加载 设置用户唯一标识
:param user_id: 用户唯一标识 :param user_id: 用户唯一标识
:return: None :return: None
""" """
@ -128,37 +86,68 @@ class ConversationState(rx.State):
if not self.user_id: if not self.user_id:
return return
# 数据库状态
database_state = await self.get_state(DatabaseState) database_state = await self.get_state(DatabaseState)
# 获取会话字典 # 获取会话列表
self.conversations = await database_state.get_conversations( self.conversations = await database_state.get_conversations(
user_id=self.user_id user_id=self.user_id
) )
# 若会话字典为空则先创建会话记录再在会话字典中添加 # 若会话列表为空则先创建会话记录再添加会话实例
if not self.conversations: if not self.conversations:
conversation_id, created_at = ( self.conversations.append(
await database_state.create_conversations_record(user_id=self.user_id) await database_state.create_conversations_record(user_id=self.user_id)
) )
self.conversations[conversation_id] = Conversation(created_at=created_at) # 将最后一个会话作为当前会话
self.idx = -1
# 将最后一个会话的唯一标识设置为当前会话唯一标识 self.conversation = self.conversations[self.idx]
self.conversation_id = next(reversed(self.conversations.keys())) # 将当前会话设置为已激活
self.conversation.is_actived = True
# 获取对话列表
self.conversation.dialogs = await database_state.get_dialogs(
conversation_id=self.conversation.id
)
@rx.event @rx.event
def toggle_conversation_history_shown(self) -> None: def toggle_conversation_history_shown(self) -> None:
""" """
切换会话历史展示状态 切换会话历史展示状态用于前端是否渲染会话历史
:return: None :return: None
""" """
self.is_conversation_history_shown = not self.is_conversation_history_shown self.is_conversation_history_shown = not self.is_conversation_history_shown
@staticmethod
def format_created_at(created_at: datetime) -> str:
"""
格式化创建时间
:param created_at: 创建时间
:return: 格式化后的创建时间
"""
match (datetime.now().date() - created_at.date()).days:
case 0:
formated_created_at = f"今天 {created_at.strftime('%H:%M')}"
case 1:
formated_created_at = f"昨天 {created_at.strftime('%H:%M')}"
case _:
formated_created_at = created_at.strftime("%Y-%m-%d %H:%M")
return formated_created_at
@rx.var @rx.var
def conversation_history(self) -> Dict[str, Conversation]: def conversation_history_items(self) -> List[ConversationHistoryItem]:
""" """
获取会话历史用于前端渲染会话历史 获取会话历史项列表用于前端渲染会话历史
:return: 会话历史 :return: 会话历史项列表
""" """
return dict(reversed(self.conversations.items())) items = []
for conversation in reversed(self.conversations): # 按照创建顺序倒序排序
items.append(
ConversationHistoryItem(
id=conversation.id,
description=conversation.description,
created_at=self.format_created_at(
created_at=conversation.created_at
),
)
)
return items
@rx.event @rx.event
async def create_conversation(self, form_data: Dict[str, Any]) -> None: async def create_conversation(self, form_data: Dict[str, Any]) -> None:
@ -167,43 +156,44 @@ class ConversationState(rx.State):
:param form_data: 表单数据 :param form_data: 表单数据
:return: None :return: None
""" """
# 获取会话描述 # 解析会话描述
description = form_data["description"].strip() or "新会话" description = form_data["description"].strip() or "新会话"
# 先创建会话记录再在会话字典中添加 # 创建会话记录再添加会话实例
database_state = await self.get_state(DatabaseState) database_state = await self.get_state(DatabaseState)
conversation_id, created_at = await database_state.create_conversations_record( self.conversations.append(
await database_state.create_conversations_record(
user_id=self.user_id, description=description user_id=self.user_id, description=description
) )
self.conversations[conversation_id] = Conversation(
description=description, created_at=created_at
) )
self.conversation_id = conversation_id # 将当前会话设置为未激活
self.conversation.is_actived = False
# 将最后一个会话作为当前会话并设置为已激活
self.conversation = self.conversations[-1]
self.conversation.is_actived = True
# 会话创建状态设置为未正在创建
self.is_conversation_creating = False self.is_conversation_creating = False
@rx.event @rx.event
async def delete_conversation(self, conversation_id: str) -> None: async def delete_conversation(self, idx: int) -> None:
""" """
删除指定会话 删除指定索引的会话
:param conversation_id: 指定会话唯一标识 :param idx: 指定索引
:return: None :return: None
""" """
if conversation_id not in self.conversations: # 先逻辑删除会话记录再在会话列表删除会话实例
return
# 先逻辑删除会话记录再在会话字典中删除会话
database_state = await self.get_state(DatabaseState) database_state = await self.get_state(DatabaseState)
await database_state.delete_conversations_record( await database_state.delete_conversations_record(
conversation_id=conversation_id conversation_id=self.conversations[idx].id
) )
del self.conversations[conversation_id] del self.conversations[idx]
# 删除后,若会话字典为空则先创建会话记录再在会话字典中添加默认会话 # 删除后,若会话列表为空则先创建会话记录再添加会话实例
if not self.conversations: if not self.conversations:
conversation_id, created_at = ( self.conversations.append(
await database_state.create_conversations_record(user_id=self.user_id) await database_state.create_conversations_record(user_id=self.user_id)
) )
self.conversations[conversation_id] = Conversation(created_at=created_at)
# 删除后,若当前会话唯一标识不存在则将最后一个会话的唯一标识设置为当前会话唯一标识 # 删除后,若当前会话唯一标识不存在则将最后一个会话的唯一标识设置为当前会话唯一标识
if self.conversation_id not in self.conversations: if self.conversation_id not in self.conversations:

View File

@ -2,31 +2,125 @@
""" """
数据库状态 数据库状态
""" """
from datetime import datetime from datetime import datetime, timedelta
from typing import Dict, List from random import choices
from typing import Dict, List, Tuple
from pydantic import TypeAdapter from pydantic import TypeAdapter
from pydantic_ai import ModelMessage, ModelMessagesTypeAdapter from pydantic_ai import ModelMessage, ModelMessagesTypeAdapter
from pydantic_ai._uuid import uuid7
import reflex as rx import reflex as rx
from sqlalchemy import desc from sqlalchemy import desc
from sqlmodel import select, update from sqlmodel import Field, JSON, SQLModel, select, update
from application.models import ( from application.domain_models import Conversation, Dialog, Thought
CaptchaRecord,
Conversation,
ConversationRecord,
Dialog,
DialogRecord,
RunResultRecord,
ThoughtNode,
UserRecord,
)
from datetime import datetime
# 思考节点领域模型类型适配器 class CaptchaRecord(SQLModel, table=True):
ThoughtNodeTypeAdapter = TypeAdapter(dict[int, ThoughtNode]) """
验证码记录
邮箱验证码和失效时间联合作为主键
"""
email: str = Field(..., primary_key=True, description="邮箱")
captcha: str = Field(
default_factory=lambda: "".join(choices("0123456789", k=6)),
primary_key=True,
description="验证码",
)
is_valid: bool = Field(
default=True,
index=True,
description="验证码有效True 表示有效False 表示无效",
)
is_verified: bool = Field(
default=False,
index=True,
description="验证码已核验True 表示已核验False 表示未核验",
)
expired_at: datetime = Field(
default_factory=lambda: datetime.now() + timedelta(minutes=30),
primary_key=True,
description="失效时间",
)
class UserRecord(SQLModel, table=True):
"""
用户记录
"""
id: str = Field(
default_factory=lambda: str(uuid7()),
primary_key=True,
description="用户唯一标识",
)
email: str = Field(..., index=True, description="邮箱")
class ConversationRecord(SQLModel, table=True):
"""
会话记录
"""
id: str = Field(
default_factory=lambda: str(uuid7()),
primary_key=True,
description="会话记录唯一标识",
)
user_id: str = Field(..., index=True, description="用户唯一标识")
description: str = Field(..., description="会话描述")
is_deleted: bool = Field(
default=False,
index=True,
description="会话已删除True 表示已删除False 表示未删除",
)
created_at: datetime = Field(
default_factory=datetime.now, description="会话创建时间"
)
class ThoughtRecord(SQLModel):
"""
思考记录不创建数据表
"""
id: str = Field(
default_factory=lambda: str(uuid7()),
primary_key=True,
description="思考唯一标识",
)
type: str = Field(description="思考类型")
content: str = Field(default="", description="思考内容")
class DialogRecord(SQLModel, table=True):
"""
对话记录
"""
id: str = Field(
default_factory=lambda: str(uuid7()),
primary_key=True,
description="对话唯一标识",
)
conversation_id: str = Field(..., index=True, description="会话唯一标识")
user_prompt: str = Field(..., description="用户提示词")
thoughts: List[ThoughtRecord] = Field(
default_factory=List, sa_type=JSON, description="思考列表"
)
result_output: str = Field(default="", description="结果输出")
# 运行结果表
class RunResultRecord(SQLModel, table=True):
conversation_id: str = Field(primary_key=True, description="会话唯一标识")
dialog_id: str = Field(primary_key=True, description="对话唯一标识")
new_messages: str = Field(description="新增消息")
# 思考领域模型类型适配器
ThoughtTypeAdapter = TypeAdapter(List[Thought])
class DatabaseState(rx.State): class DatabaseState(rx.State):
@ -66,7 +160,7 @@ class DatabaseState(rx.State):
核验验证码 核验验证码
:param email: 邮箱 :param email: 邮箱
:param captcha: 验证码 :param captcha: 验证码
:return: 是否核验成功True 表示核验成功False 表示核验失败根据邮箱和验证码未到有效且未核验的记录或失效 :return: 是否核验成功True 表示核验成功False 表示核验失败根据邮箱和验证码未查询到有效且未核验的记录或失效
""" """
async with rx.asession() as session: async with rx.asession() as session:
result = await session.exec( result = await session.exec(
@ -75,7 +169,7 @@ class DatabaseState(rx.State):
CaptchaRecord.captcha == captcha, CaptchaRecord.captcha == captcha,
CaptchaRecord.is_valid == True, CaptchaRecord.is_valid == True,
CaptchaRecord.is_verified == False, CaptchaRecord.is_verified == False,
CaptchaRecord.expired_at > datetime.now(), # 不区分是否过期 CaptchaRecord.expired_at > datetime.now(),
) )
) )
record = result.first() record = result.first()
@ -97,7 +191,7 @@ class DatabaseState(rx.State):
select(UserRecord).where(UserRecord.email == email) select(UserRecord).where(UserRecord.email == email)
) )
record = result.first() record = result.first()
# 若记录存在则返回用户唯一标识,否则创建并返回所创建记录的用户唯一标识 # 若记录存在则返回用户唯一标识,否则创建并返回所创建记录的用户唯一标识
if record: if record:
return record.id return record.id
record = UserRecord(email=email) record = UserRecord(email=email)
@ -108,63 +202,68 @@ class DatabaseState(rx.State):
async def get_conversations(self, user_id: str) -> Dict[str, Conversation]: async def get_conversations(self, user_id: str) -> Dict[str, Conversation]:
""" """
获取会话字典 获取会话列表
:param user_id: 用户唯一标识 :param user_id: 用户唯一标识
:return: 会话字典 :return: 会话列表
""" """
records: Dict[str, Conversation] = {} records: Dict[str, Conversation] = {}
async with rx.asession() as session: async with rx.asession() as session:
result = await session.exec( result = await session.exec(
select(ConversationRecord, DialogRecord) select(ConversationRecord, DialogRecord)
.outerjoin( .outerjoin(DialogRecord, ConversationRecord.id == DialogRecord.conversation_id) # type: ignore
DialogRecord,
ConversationRecord.id == DialogRecord.conversation_id, # type: ignore
)
.where( .where(
ConversationRecord.user_id == user_id, ConversationRecord.user_id == user_id,
ConversationRecord.is_deleted == False, ConversationRecord.is_deleted == False,
) )
.order_by(ConversationRecord.id, DialogRecord.id) .order_by(ConversationRecord.id, DialogRecord.id)
) )
for conversation, dialog in result.all(): for conversation_record, dialog_record in result.all():
record = records.setdefault( record = records.setdefault(
conversation.id, conversation_record.id,
Conversation( Conversation(
description=conversation.description, id=conversation_record.id,
created_at=conversation.created_at, description=conversation_record.description,
created_at=conversation_record.created_at,
), ),
) )
if not dialog: if not dialog_record:
continue continue
record.dialogs[dialog.id] = Dialog( record.dialogs.append(
question=dialog.question, Dialog(
thought_nodes=ThoughtNodeTypeAdapter.validate_python( id=dialog_record.id,
dialog.thought_nodes user_prompt=dialog_record.user_prompt,
thoughts=ThoughtTypeAdapter.validate_python(
dialog_record.thoughts
), ),
answer=dialog.answer, result_output=dialog_record.result_output,
)
) )
return records return records
async def create_conversations_record( async def create_conversations_record(
self, user_id: str, description: str = "新会话" self, user_id: str, description: str = "新会话"
) -> tuple[str, datetime]: ) -> Conversation:
""" """
创建会话记录 创建会话记录
:param user_id: 用户唯一标识 :param user_id: 用户唯一标识
:param description: 会话描述 :param description: 会话描述默认为"新会话"
:return: 创建会话记录的唯一标识和创建时间 :return: 会话实例
""" """
async with rx.asession() as session: async with rx.asession() as session:
record = ConversationRecord(user_id=user_id, description=description) record = ConversationRecord(user_id=user_id, description=description)
session.add(record) session.add(record)
await session.commit() await session.commit()
await session.refresh(record) await session.refresh(record)
return record.id, record.created_at return Conversation(
id=record.id,
description=record.description,
created_at=record.created_at,
)
async def delete_conversations_record(self, conversation_id: str) -> None: async def delete_conversations_record(self, conversation_id: str) -> None:
""" """
逻辑删除会话记录 逻辑删除会话记录
:param conversation_id: 指定会话记录唯一标识 :param conversation_id: 指定会话唯一标识
:return: None :return: None
""" """
async with rx.asession() as session: async with rx.asession() as session:
@ -184,7 +283,7 @@ class DatabaseState(rx.State):
async with rx.asession() as session: async with rx.asession() as session:
record = DialogRecord( record = DialogRecord(
conversation_id=conversation_id, conversation_id=conversation_id,
question=question, user_prompt=question,
) )
session.add(record) session.add(record)
await session.commit() await session.commit()
@ -194,24 +293,22 @@ class DatabaseState(rx.State):
async def update_dialog_record( async def update_dialog_record(
self, self,
dialog_id: str, dialog_id: str,
thought_nodes: dict[int, ThoughtNode], thoughts: List[Thought],
answer: str, result_output: str,
) -> None: ) -> None:
""" """
更新对话记录 更新对话记录
:param dialog_id: 对话唯一标识 :param dialog_id: 对话唯一标识
:param thought_nodes: 思考节点列表 :param thoughts: 思考列表
:param answer: 回答 :param result_output: 结果输出
:return: None :return: None
""" """
async with rx.asession() as session: async with rx.asession() as session:
record = await session.get(DialogRecord, dialog_id) record = await session.get(DialogRecord, dialog_id)
if not record: if not record:
return return
record.thought_nodes = ThoughtNodeTypeAdapter.dump_python( record.thoughts = ThoughtTypeAdapter.dump_python(thoughts)
{k: v for k, v in thought_nodes.items()} record.result_output = result_output
)
record.answer = answer
await session.commit() await session.commit()
async def create_run_result_record( async def create_run_result_record(