"""SSE event payload schemas — typed Pydantic models for every SSE event."""
from pydantic import BaseModel, Field
from veupath_chatbot.platform.types import JSONObject
from veupath_chatbot.transport.http.schemas.chat import (
CitationResponse,
PlanningArtifactResponse,
)
from veupath_chatbot.transport.http.schemas.optimization import (
OptimizationParameterSpecData as OptimizationParameterSpecData,
)
from veupath_chatbot.transport.http.schemas.optimization import (
OptimizationProgressEventData as OptimizationProgressEventData,
)
from veupath_chatbot.transport.http.schemas.optimization import (
OptimizationTrialData as OptimizationTrialData,
)
# ── Message lifecycle ────────────────────────────────────────────────
[docs]
class MessageStartEventData(BaseModel):
"""Payload for ``message_start`` SSE events."""
strategy_id: str | None = Field(default=None, alias="strategyId")
strategy: JSONObject | None = None
model_config = {"populate_by_name": True}
[docs]
class UserMessageEventData(BaseModel):
"""Payload for ``user_message`` SSE events."""
message_id: str | None = Field(default=None, alias="messageId")
content: str | None = None
model_config = {"populate_by_name": True}
[docs]
class AssistantDeltaEventData(BaseModel):
"""Payload for ``assistant_delta`` SSE events (streaming tokens)."""
message_id: str | None = Field(default=None, alias="messageId")
delta: str | None = None
model_config = {"populate_by_name": True}
[docs]
class AssistantMessageEventData(BaseModel):
"""Payload for ``assistant_message`` SSE events (complete message)."""
message_id: str | None = Field(default=None, alias="messageId")
content: str | None = None
model_config = {"populate_by_name": True}
[docs]
class MessageEndEventData(BaseModel):
"""Payload for ``message_end`` SSE events — mirrors TokenUsageResponse."""
model_id: str | None = Field(default=None, alias="modelId")
prompt_tokens: int | None = Field(default=None, alias="promptTokens")
completion_tokens: int | None = Field(default=None, alias="completionTokens")
total_tokens: int | None = Field(default=None, alias="totalTokens")
cached_tokens: int | None = Field(default=None, alias="cachedTokens")
tool_call_count: int | None = Field(default=None, alias="toolCallCount")
registered_tool_count: int | None = Field(default=None, alias="registeredToolCount")
llm_call_count: int | None = Field(default=None, alias="llmCallCount")
sub_kani_prompt_tokens: int | None = Field(
default=None, alias="subKaniPromptTokens"
)
sub_kani_completion_tokens: int | None = Field(
default=None, alias="subKaniCompletionTokens"
)
sub_kani_call_count: int | None = Field(default=None, alias="subKaniCallCount")
estimated_cost_usd: float | None = Field(default=None, alias="estimatedCostUsd")
model_config = {"populate_by_name": True}
# ── Tool calls ───────────────────────────────────────────────────────
# ── Sub-kani events ──────────────────────────────────────────────────
[docs]
class SubKaniTaskStartEventData(BaseModel):
"""Payload for ``subkani_task_start`` SSE events."""
task: str | None = None
model_id: str | None = Field(default=None, alias="modelId")
model_config = {"populate_by_name": True}
[docs]
class SubKaniTaskEndEventData(BaseModel):
"""Payload for ``subkani_task_end`` SSE events."""
task: str | None = None
status: str | None = None
model_id: str | None = Field(default=None, alias="modelId")
prompt_tokens: int | None = Field(default=None, alias="promptTokens")
completion_tokens: int | None = Field(default=None, alias="completionTokens")
llm_call_count: int | None = Field(default=None, alias="llmCallCount")
estimated_cost_usd: float | None = Field(default=None, alias="estimatedCostUsd")
model_config = {"populate_by_name": True}
# ── Token usage / model selection ────────────────────────────────────
[docs]
class TokenUsagePartialEventData(BaseModel):
"""Payload for ``token_usage_partial`` SSE events."""
prompt_tokens: int | None = Field(default=None, alias="promptTokens")
registered_tool_count: int | None = Field(default=None, alias="registeredToolCount")
model_config = {"populate_by_name": True}
[docs]
class ModelSelectedEventData(BaseModel):
"""Payload for ``model_selected`` SSE events."""
model_id: str = Field(alias="modelId")
model_config = {"populate_by_name": True}
# ── Optimization events (imported from optimization.py) ─────────────
# OptimizationTrialData, OptimizationParameterSpecData, and
# OptimizationProgressEventData are re-exported from the import above.
# ── Error ────────────────────────────────────────────────────────────
[docs]
class ErrorEventData(BaseModel):
"""Payload for ``error`` SSE events."""
error: str
model_config = {"populate_by_name": True}
# ── Strategy / graph events ──────────────────────────────────────────
[docs]
class GraphSnapshotEventData(BaseModel):
"""Payload for ``graph_snapshot`` SSE events."""
graph_snapshot: JSONObject | None = Field(default=None, alias="graphSnapshot")
model_config = {"populate_by_name": True}
[docs]
class GraphPlanEventData(BaseModel):
"""Payload for ``graph_plan`` SSE events."""
graph_id: str | None = Field(default=None, alias="graphId")
plan: JSONObject | None = None
name: str | None = None
record_type: str | None = Field(default=None, alias="recordType")
description: str | None = None
model_config = {"populate_by_name": True}
[docs]
class StrategyUpdateEventData(BaseModel):
"""Payload for ``strategy_update`` SSE events."""
graph_id: str | None = Field(default=None, alias="graphId")
step: JSONObject | None = None
model_config = {"populate_by_name": True}
[docs]
class StrategyLinkEventData(BaseModel):
"""Payload for ``strategy_link`` SSE events."""
graph_id: str | None = Field(default=None, alias="graphId")
wdk_strategy_id: int | None = Field(default=None, alias="wdkStrategyId")
wdk_url: str | None = Field(default=None, alias="wdkUrl")
name: str | None = None
description: str | None = None
is_saved: bool | None = Field(default=None, alias="isSaved")
model_config = {"populate_by_name": True}
[docs]
class GraphClearedEventData(BaseModel):
"""Payload for ``graph_cleared`` SSE events."""
graph_id: str | None = Field(default=None, alias="graphId")
model_config = {"populate_by_name": True}
[docs]
class ExecutorBuildRequestEventData(BaseModel):
"""Payload for ``executor_build_request`` SSE events."""
executor_build_request: JSONObject | None = Field(
default=None, alias="executorBuildRequest"
)
model_config = {"populate_by_name": True}
# ── Workbench gene sets ──────────────────────────────────────────────
[docs]
class GeneSetSummary(BaseModel):
"""Summary of a gene set — nested model, not a top-level event."""
id: str | None = None
name: str | None = None
gene_count: int | None = Field(default=None, alias="geneCount")
source: str | None = None
site_id: str | None = Field(default=None, alias="siteId")
model_config = {"populate_by_name": True}
[docs]
class WorkbenchGeneSetEventData(BaseModel):
"""Payload for ``workbench_gene_set`` SSE events."""
gene_set: GeneSetSummary | None = Field(default=None, alias="geneSet")
model_config = {"populate_by_name": True}
# ── Miscellaneous events ────────────────────────────────────────────
[docs]
class CitationsEventData(BaseModel):
"""Payload for ``citations`` SSE events."""
citations: list[CitationResponse] | None = None
model_config = {"populate_by_name": True}
[docs]
class PlanningArtifactEventData(BaseModel):
"""Payload for ``planning_artifact`` SSE events."""
planning_artifact: PlanningArtifactResponse | None = Field(
default=None, alias="planningArtifact"
)
model_config = {"populate_by_name": True}
[docs]
class ReasoningEventData(BaseModel):
"""Payload for ``reasoning`` SSE events."""
reasoning: str | None = None
model_config = {"populate_by_name": True}