mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-31 19:45:15 +02:00
feat(automation): add Pydantic schemas for the automation definition
Three layers of Pydantic models under app/automations/schemas/, one
file per concern (SRP), matching the envelope in
automation-design-plan.md §5.
definition/ — the editable envelope persisted in
automations.definition:
- envelope.py AutomationDefinition (top-level shape)
- plan_step.py PlanStep (one step in the sequential plan)
- inputs.py InputsBlock (the inputs JSON Schema wrapper)
- execution.py ExecutionBlock (timeouts, retries, concurrency,
budget cap, on_failure plan)
- metadata.py MetadataBlock (tags + created_from_nl + extras)
- trigger_spec.py TriggerSpec (one entry in triggers[])
triggers/ — per-trigger config schemas, dispatched by registry on the
TriggerSpec.type discriminator:
- schedule.py ScheduleTriggerConfig(cron, timezone)
- manual.py ManualTriggerConfig() — empty in v1
actions/ — per-action config schemas, dispatched by registry on the
PlanStep.action discriminator:
- agent_task.py AgentTaskActionConfig(prompt, tools, model,
output_schema)
Design properties verified by an inline smoke test:
- The §5 worked example round-trips through model_validate_json /
model_dump_json byte-for-byte (InputsBlock uses
serialize_by_alias so the JSON key stays "schema" not
"schema_").
- Envelope rejects unknown top-level keys (extra="forbid").
- MetadataBlock tolerates unknown keys (extra="allow").
- ExecutionBlock defaults apply when the block is omitted.
- retry_backoff and concurrency are typed as Literal — bogus
values rejected at validation time.
- Per-type configs enforce their required fields (cron + timezone
on schedule; non-empty prompt on agent_task).
The envelope keeps trigger and action configs as untyped dicts on
purpose — per-type validation is a registry-driven dispatch (commit
10), keeping the envelope free of every-type-knows-every-type
coupling.
This commit is contained in:
parent
d9183464d9
commit
be4d43d6c9
13 changed files with 539 additions and 4 deletions
|
|
@ -0,0 +1,66 @@
|
|||
"""``AgentTaskActionConfig`` — config for the ``agent_task`` action type."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class AgentTaskActionConfig(BaseModel):
|
||||
"""Config for an ``agent_task`` plan step.
|
||||
|
||||
Validated against ``PlanStep.config`` whenever the step's
|
||||
``action`` is ``agent_task``. The step instructs the LangGraph
|
||||
Deep Agent runtime to:
|
||||
|
||||
1. Receive ``prompt`` (with all preceding-step outputs and inputs
|
||||
already rendered by the template engine).
|
||||
2. Run the agent with access to *exactly* the capabilities named
|
||||
in ``tools`` — nothing else from the registry is visible to
|
||||
this agent invocation.
|
||||
3. Return a JSON object matching ``output_schema`` (recommended;
|
||||
the executor validates and re-prompts on mismatch).
|
||||
|
||||
``output_schema`` is the design's "dynamic output contract" —
|
||||
instead of locking the output shape on the ActionDefinition (as
|
||||
tight actions do), the user declares the shape they want for this
|
||||
specific step, and the agent has to match it.
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
prompt: str = Field(
|
||||
...,
|
||||
description=(
|
||||
"The task prompt rendered through the Jinja sandbox. May "
|
||||
"reference automation inputs and prior-step outputs."
|
||||
),
|
||||
min_length=1,
|
||||
)
|
||||
tools: list[str] = Field(
|
||||
default_factory=list,
|
||||
description=(
|
||||
"Allowlist of capability IDs the agent may call (e.g., "
|
||||
"'search_space.query'). Empty list = no tool access; the "
|
||||
"agent must answer from the prompt alone."
|
||||
),
|
||||
)
|
||||
model: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Optional LiteLLM model identifier (e.g., "
|
||||
"'anthropic/claude-sonnet-4-7'). Omitted means the "
|
||||
"automation falls back to the search space's default "
|
||||
"agent_llm_id."
|
||||
),
|
||||
)
|
||||
output_schema: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Optional JSON Schema declaring the shape the agent must "
|
||||
"return. Strongly recommended; the editor warns when "
|
||||
"missing. Validated by the executor before binding to "
|
||||
"``output_as``."
|
||||
),
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue