2026-01-29 13:44:04 +08:00
|
|
|
from typing import Annotated, NotRequired, TypedDict
|
2026-01-14 12:32:34 +08:00
|
|
|
|
|
|
|
|
from langchain.agents import AgentState
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SandboxState(TypedDict):
|
2026-01-14 23:29:18 +08:00
|
|
|
sandbox_id: NotRequired[str | None]
|
2026-01-14 12:32:34 +08:00
|
|
|
|
|
|
|
|
|
2026-01-15 13:22:30 +08:00
|
|
|
class ThreadDataState(TypedDict):
|
|
|
|
|
workspace_path: NotRequired[str | None]
|
|
|
|
|
uploads_path: NotRequired[str | None]
|
|
|
|
|
outputs_path: NotRequired[str | None]
|
|
|
|
|
|
|
|
|
|
|
2026-01-29 13:44:04 +08:00
|
|
|
class ViewedImageData(TypedDict):
|
|
|
|
|
base64: str
|
|
|
|
|
mime_type: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def merge_artifacts(existing: list[str] | None, new: list[str] | None) -> list[str]:
|
|
|
|
|
"""Reducer for artifacts list - merges and deduplicates artifacts."""
|
|
|
|
|
if existing is None:
|
|
|
|
|
return new or []
|
|
|
|
|
if new is None:
|
|
|
|
|
return existing
|
|
|
|
|
# Use dict.fromkeys to deduplicate while preserving order
|
|
|
|
|
return list(dict.fromkeys(existing + new))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def merge_viewed_images(existing: dict[str, ViewedImageData] | None, new: dict[str, ViewedImageData] | None) -> dict[str, ViewedImageData]:
|
|
|
|
|
"""Reducer for viewed_images dict - merges image dictionaries.
|
|
|
|
|
|
|
|
|
|
Special case: If new is an empty dict {}, it clears the existing images.
|
|
|
|
|
This allows middlewares to clear the viewed_images state after processing.
|
|
|
|
|
"""
|
|
|
|
|
if existing is None:
|
|
|
|
|
return new or {}
|
|
|
|
|
if new is None:
|
|
|
|
|
return existing
|
|
|
|
|
# Special case: empty dict means clear all viewed images
|
|
|
|
|
if len(new) == 0:
|
|
|
|
|
return {}
|
|
|
|
|
# Merge dictionaries, new values override existing ones for same keys
|
|
|
|
|
return {**existing, **new}
|
|
|
|
|
|
|
|
|
|
|
2026-01-14 12:32:34 +08:00
|
|
|
class ThreadState(AgentState):
|
2026-01-14 23:29:18 +08:00
|
|
|
sandbox: NotRequired[SandboxState | None]
|
2026-01-15 13:22:30 +08:00
|
|
|
thread_data: NotRequired[ThreadDataState | None]
|
2026-01-14 23:29:18 +08:00
|
|
|
title: NotRequired[str | None]
|
2026-01-29 13:44:04 +08:00
|
|
|
artifacts: Annotated[list[str], merge_artifacts]
|
2026-01-21 16:14:00 +08:00
|
|
|
todos: NotRequired[list | None]
|
2026-01-23 18:47:39 +08:00
|
|
|
uploaded_files: NotRequired[list[dict] | None]
|
2026-01-29 13:44:04 +08:00
|
|
|
viewed_images: Annotated[dict[str, ViewedImageData], merge_viewed_images] # image_path -> {base64, mime_type}
|