release: bump version to 0.25.1

This commit is contained in:
linkong
2026-04-10 16:19:57 +08:00
parent 62ad09e816
commit a2210f0f78
10 changed files with 258 additions and 252 deletions

View File

@@ -44,6 +44,56 @@ class _ActiveRun:
_ACTIVE_RUNS: dict[str, _ActiveRun] = {}
async def _get_session_by_key(
db: AsyncSession,
*,
user_id: int,
session_key: str,
) -> PlaygroundSession | None:
result = await db.execute(
select(PlaygroundSession).where(
PlaygroundSession.user_id == user_id,
PlaygroundSession.session_key == session_key,
)
)
return result.scalar_one_or_none()
async def _require_session(
db: AsyncSession,
*,
user_id: int,
session_key: str,
) -> PlaygroundSession:
session = await _get_session_by_key(db, user_id=user_id, session_key=session_key)
if session is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Playground session not found")
return session
async def _require_visible_message(
db: AsyncSession,
*,
user_id: int,
public_id: str,
role: str | None = None,
) -> PlaygroundMessage:
conditions = [
PlaygroundMessage.user_id == user_id,
PlaygroundMessage.public_id == public_id,
PlaygroundMessage.is_visible.is_(True),
]
if role is not None:
conditions.append(PlaygroundMessage.role == role)
result = await db.execute(select(PlaygroundMessage).where(*conditions))
message = result.scalar_one_or_none()
if message is None:
detail = "User message not found" if role == "user" else "Playground message not found"
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=detail)
return message
def _message_to_record(message: PlaygroundMessage, parent_public_id: str | None = None) -> PlaygroundMessageRecord:
return PlaygroundMessageRecord(
id=message.public_id,
@@ -142,18 +192,26 @@ async def get_thread(
user_id: int,
session_key: str,
) -> PlaygroundThreadResponse | None:
result = await db.execute(
select(PlaygroundSession).where(
PlaygroundSession.user_id == user_id,
PlaygroundSession.session_key == session_key,
)
)
session = result.scalar_one_or_none()
session = await _get_session_by_key(db, user_id=user_id, session_key=session_key)
if session is None:
return None
return await _build_thread_response(db, session=session)
async def _build_action_response(
db: AsyncSession,
*,
session: PlaygroundSession,
active_message_id: str | None = None,
) -> PlaygroundMessageActionResponse:
thread = await _build_thread_response(db, session=session)
return PlaygroundMessageActionResponse(
session=thread.session,
messages=thread.messages,
active_message_id=active_message_id,
)
def _collect_constraints(raw_constraints: str) -> list[str]:
return [item.strip() for item in raw_constraints.split("\n") if item.strip()]
@@ -190,6 +248,31 @@ async def _set_session_state(
return session
def _spawn_assistant_run(
*,
user_id: int,
session_id: int,
session_key: str,
user_message_id: int,
assistant_message_id: int,
assistant_public_id: str,
payload: PlaygroundMessageCreateRequest,
provider_client: AIProviderClient,
) -> None:
task = asyncio.create_task(
_run_assistant_message(
user_id=user_id,
session_id=session_id,
session_key=session_key,
user_message_id=user_message_id,
assistant_message_id=assistant_message_id,
payload=payload,
provider_client=provider_client,
)
)
_ACTIVE_RUNS[assistant_public_id] = _ActiveRun(task)
async def create_turn(
db: AsyncSession,
*,
@@ -252,23 +335,20 @@ async def create_turn(
await db.refresh(user_message)
await db.refresh(assistant_message)
task = asyncio.create_task(
_run_assistant_message(
user_id=user_id,
session_id=session.id,
session_key=payload.session_key,
user_message_id=user_message.id,
assistant_message_id=assistant_message.id,
payload=payload,
provider_client=provider_client,
)
_spawn_assistant_run(
user_id=user_id,
session_id=session.id,
session_key=payload.session_key,
user_message_id=user_message.id,
assistant_message_id=assistant_message.id,
assistant_public_id=assistant_message.public_id,
payload=payload,
provider_client=provider_client,
)
_ACTIVE_RUNS[assistant_message.public_id] = _ActiveRun(task)
thread = await _build_thread_response(db, session=session)
return PlaygroundMessageActionResponse(
session=thread.session,
messages=thread.messages,
return await _build_action_response(
db,
session=session,
active_message_id=assistant_message.public_id,
)
@@ -303,23 +383,20 @@ async def _create_assistant_retry_turn(
await db.refresh(session)
await db.refresh(assistant_message)
task = asyncio.create_task(
_run_assistant_message(
user_id=user_id,
session_id=session.id,
session_key=payload.session_key,
user_message_id=user_message.id,
assistant_message_id=assistant_message.id,
payload=payload,
provider_client=provider_client,
)
_spawn_assistant_run(
user_id=user_id,
session_id=session.id,
session_key=payload.session_key,
user_message_id=user_message.id,
assistant_message_id=assistant_message.id,
assistant_public_id=assistant_message.public_id,
payload=payload,
provider_client=provider_client,
)
_ACTIVE_RUNS[assistant_message.public_id] = _ActiveRun(task)
thread = await _build_thread_response(db, session=session)
return PlaygroundMessageActionResponse(
session=thread.session,
messages=thread.messages,
return await _build_action_response(
db,
session=session,
active_message_id=assistant_message.public_id,
)
@@ -330,34 +407,11 @@ async def stop_message(
user_id: int,
payload: PlaygroundMessageStopRequest,
) -> PlaygroundMessageActionResponse:
result = await db.execute(
select(PlaygroundSession).where(
PlaygroundSession.user_id == user_id,
PlaygroundSession.session_key == payload.session_key,
)
)
session = result.scalar_one_or_none()
if session is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Playground session not found")
result = await db.execute(
select(PlaygroundMessage).where(
PlaygroundMessage.user_id == user_id,
PlaygroundMessage.public_id == payload.message_id,
PlaygroundMessage.is_visible.is_(True),
)
)
message = result.scalar_one_or_none()
if message is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Playground message not found")
session = await _require_session(db, user_id=user_id, session_key=payload.session_key)
message = await _require_visible_message(db, user_id=user_id, public_id=payload.message_id)
if message.status not in {"pending", "thinking", "answering"}:
thread = await _build_thread_response(db, session=session)
return PlaygroundMessageActionResponse(
session=thread.session,
messages=thread.messages,
active_message_id=None,
)
return await _build_action_response(db, session=session)
active_run = _ACTIVE_RUNS.get(message.public_id)
if active_run is not None:
@@ -371,12 +425,7 @@ async def stop_message(
await db.commit()
await db.refresh(message)
thread = await _build_thread_response(db, session=session)
return PlaygroundMessageActionResponse(
session=thread.session,
messages=thread.messages,
active_message_id=None,
)
return await _build_action_response(db, session=session)
async def resend_turn(
@@ -386,27 +435,13 @@ async def resend_turn(
payload: PlaygroundMessageResendRequest,
provider_client: AIProviderClient,
) -> PlaygroundMessageActionResponse:
result = await db.execute(
select(PlaygroundSession).where(
PlaygroundSession.user_id == user_id,
PlaygroundSession.session_key == payload.session_key,
)
session = await _require_session(db, user_id=user_id, session_key=payload.session_key)
user_message = await _require_visible_message(
db,
user_id=user_id,
public_id=payload.user_message_id,
role="user",
)
session = result.scalar_one_or_none()
if session is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Playground session not found")
result = await db.execute(
select(PlaygroundMessage).where(
PlaygroundMessage.user_id == user_id,
PlaygroundMessage.public_id == payload.user_message_id,
PlaygroundMessage.role == "user",
PlaygroundMessage.is_visible.is_(True),
)
)
user_message = result.scalar_one_or_none()
if user_message is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User message not found")
later_messages = await db.execute(
select(PlaygroundMessage).where(
@@ -451,39 +486,20 @@ async def edit_user_message(
user_id: int,
payload: PlaygroundMessageEditRequest,
) -> PlaygroundMessageActionResponse:
result = await db.execute(
select(PlaygroundSession).where(
PlaygroundSession.user_id == user_id,
PlaygroundSession.session_key == payload.session_key,
)
session = await _require_session(db, user_id=user_id, session_key=payload.session_key)
user_message = await _require_visible_message(
db,
user_id=user_id,
public_id=payload.user_message_id,
role="user",
)
session = result.scalar_one_or_none()
if session is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Playground session not found")
result = await db.execute(
select(PlaygroundMessage).where(
PlaygroundMessage.user_id == user_id,
PlaygroundMessage.public_id == payload.user_message_id,
PlaygroundMessage.role == "user",
PlaygroundMessage.is_visible.is_(True),
)
)
user_message = result.scalar_one_or_none()
if user_message is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User message not found")
user_message.content = payload.content.strip()
await db.flush()
await db.commit()
await db.refresh(user_message)
thread = await _build_thread_response(db, session=session)
return PlaygroundMessageActionResponse(
session=thread.session,
messages=thread.messages,
active_message_id=None,
)
return await _build_action_response(db, session=session)
async def _append_meta_if_missing(db: AsyncSession, message_id: int, meta_line: str) -> None: