211 lines
5.3 KiB
Python
211 lines
5.3 KiB
Python
import datetime
|
|
from typing import Annotated, Any, Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from src.config import ReasoningLevel
|
|
from src.schemas import (
|
|
DreamType,
|
|
MessageConfiguration,
|
|
SessionConfiguration,
|
|
SessionPeerConfig,
|
|
WorkspaceConfiguration,
|
|
)
|
|
|
|
|
|
class TestStep(BaseModel):
|
|
description: str | None = None
|
|
|
|
|
|
# --- Configuration Actions ---
|
|
|
|
|
|
class SetWorkspaceConfigAction(TestStep):
|
|
step_type: Literal["set_workspace_config"] = "set_workspace_config"
|
|
config: WorkspaceConfiguration
|
|
|
|
|
|
class SetSessionConfigAction(TestStep):
|
|
step_type: Literal["set_session_config"] = "set_session_config"
|
|
session_id: str
|
|
config: SessionConfiguration
|
|
|
|
|
|
# --- Interaction Actions ---
|
|
|
|
|
|
class CreateSessionAction(TestStep):
|
|
step_type: Literal["create_session"] = "create_session"
|
|
session_id: str
|
|
peer_configs: dict[str, SessionPeerConfig] | None = None
|
|
config: SessionConfiguration | None = None
|
|
|
|
|
|
class AddMessageAction(TestStep):
|
|
step_type: Literal["add_message"] = "add_message"
|
|
session_id: str
|
|
peer_id: str
|
|
content: str
|
|
created_at: datetime.datetime | None = None
|
|
config: MessageConfiguration | None = None
|
|
|
|
|
|
class MessageItem(BaseModel):
|
|
peer_id: str
|
|
content: str
|
|
config: MessageConfiguration | None = None
|
|
created_at: datetime.datetime | None = None
|
|
|
|
|
|
class AddMessagesAction(TestStep):
|
|
step_type: Literal["add_messages"] = "add_messages"
|
|
session_id: str
|
|
messages: list[MessageItem]
|
|
|
|
|
|
class CreateScopeAction(TestStep):
|
|
"""Create a scope and optionally add member sessions.
|
|
|
|
Driven over raw HTTP rather than the SDK: scopes are a new API surface the
|
|
published SDK does not expose yet, and gating coverage on an SDK release
|
|
would leave the feature untested at exactly the point it needs testing.
|
|
"""
|
|
|
|
step_type: Literal["create_scope"] = "create_scope"
|
|
scope_id: str = Field(..., description="Unprefixed scope name")
|
|
session_ids: list[str] = Field(
|
|
default_factory=list,
|
|
description="Existing sessions to add as members of the scope",
|
|
)
|
|
|
|
|
|
# --- Wait Actions ---
|
|
|
|
|
|
class WaitAction(TestStep):
|
|
step_type: Literal["wait"] = "wait"
|
|
duration: float | None = Field(
|
|
None, description="Wait for a specific duration in seconds"
|
|
)
|
|
target: Literal["queue_empty"] = "queue_empty"
|
|
timeout: int = 60
|
|
flush: bool = Field(
|
|
False,
|
|
description="Enable flush mode to bypass batch token threshold before waiting",
|
|
)
|
|
|
|
|
|
# --- Dream Actions ---
|
|
|
|
|
|
class ScheduleDreamAction(TestStep):
|
|
step_type: Literal["schedule_dream"] = "schedule_dream"
|
|
observer: str = Field(..., description="Observer peer name")
|
|
observed: str | None = Field(
|
|
None, description="Observed peer name (defaults to observer if not specified)"
|
|
)
|
|
session_id: str = Field(..., description="Session ID to scope the dream to")
|
|
dream_type: DreamType = Field(..., description="Type of dream to schedule")
|
|
|
|
|
|
# --- Assertions ---
|
|
|
|
|
|
class Assertion(BaseModel):
|
|
pass
|
|
|
|
|
|
class LLMJudgeAssertion(Assertion):
|
|
assertion_type: Literal["llm_judge"] = "llm_judge"
|
|
prompt: str
|
|
pass_if: bool = True
|
|
|
|
|
|
class ContainsAssertion(Assertion):
|
|
assertion_type: Literal["contains"] = "contains"
|
|
text: str
|
|
case_sensitive: bool = False
|
|
|
|
|
|
class NotContainsAssertion(Assertion):
|
|
assertion_type: Literal["not_contains"] = "not_contains"
|
|
text: str
|
|
case_sensitive: bool = False
|
|
|
|
|
|
class ExactMatchAssertion(Assertion):
|
|
assertion_type: Literal["exact_match"] = "exact_match"
|
|
text: str
|
|
|
|
|
|
class JsonMatchAssertion(Assertion):
|
|
assertion_type: Literal["json_match"] = "json_match"
|
|
schema_path: str | None = None # Optional JSON schema path
|
|
key_value_pairs: dict[str, Any] | None = None
|
|
|
|
|
|
# --- Query/Assertion Actions ---
|
|
|
|
|
|
class QueryAction(TestStep):
|
|
step_type: Literal["query"] = "query"
|
|
target: Literal[
|
|
"chat",
|
|
"get_context",
|
|
"get_peer_card",
|
|
"get_representation",
|
|
"workspace_chat",
|
|
]
|
|
|
|
session_id: str | None = None
|
|
|
|
input: str | None = None
|
|
|
|
# for get_context
|
|
summary: bool = False
|
|
max_tokens: int | None = None
|
|
|
|
observed_peer_id: str | None = None
|
|
observer_peer_id: str | None = None
|
|
|
|
# for chat - reasoning level
|
|
reasoning_level: ReasoningLevel | None = None
|
|
|
|
# for chat - optional JSON Schema the response must conform to
|
|
response_format: dict[str, Any] | None = None
|
|
|
|
# Confine the read to one scope (observer swap on peer chat) or to the
|
|
# union of several scopes' member sessions. Peer-chat/representation/
|
|
# context go over raw HTTP; workspace_chat uses the SDK `scope` argument.
|
|
scope: str | list[str] | None = None
|
|
|
|
assertions: list[
|
|
LLMJudgeAssertion
|
|
| ContainsAssertion
|
|
| NotContainsAssertion
|
|
| ExactMatchAssertion
|
|
| JsonMatchAssertion
|
|
]
|
|
|
|
|
|
# --- Unified Step Type ---
|
|
|
|
|
|
class TestDefinition(BaseModel):
|
|
description: str | None = None
|
|
workspace_config: WorkspaceConfiguration | None = None
|
|
steps: list[
|
|
Annotated[
|
|
SetWorkspaceConfigAction
|
|
| SetSessionConfigAction
|
|
| CreateSessionAction
|
|
| AddMessageAction
|
|
| AddMessagesAction
|
|
| CreateScopeAction
|
|
| WaitAction
|
|
| ScheduleDreamAction
|
|
| QueryAction,
|
|
Field(discriminator="step_type"),
|
|
]
|
|
]
|