Documentation
¶
Overview ¶
Package controller implements the decision layer of a controller DAG: the catalog of actions offered to the LLM, the goal state it works against, and the planner that turns a conversation into the next action.
Index ¶
- Constants
- func MergeParams(stepParams string, args map[string]any, pinned map[string]struct{}) string
- func ParamString(args map[string]any) string
- func PinnedParams(step core.Step) map[string]struct{}
- func ValidTaskStatus(value string) bool
- type Catalog
- type Decision
- type DecisionKind
- type Event
- type MaskFunc
- type PendingAction
- type Planner
- type State
- func (s *State) Append(msgs ...exec.LLMMessage)
- func (s *State) CompactAllObservations(maxBytes int) int
- func (s *State) CompactObservations(keepRecent, maxBytes int) int
- func (s *State) EnableObservationAging()
- func (s *State) FailedTasks() []TaskState
- func (s *State) FinalizeEvent(step, status, finishedAt, reason string)
- func (s *State) LatestPromptTokens() int
- func (s *State) Marshal() (json.RawMessage, error)
- func (s *State) Messages() []exec.LLMMessage
- func (s *State) OpenTaskNames() []string
- func (s *State) PriorAnswer(question string) (string, bool)
- func (s *State) QuestionCount() int
- func (s *State) RecordAnswer(question, answer string)
- func (s *State) RecordEvent(e Event)
- func (s *State) RecordStepRun(step string) int
- func (s *State) SetTaskStatus(name string, status TaskStatus, reason string) error
- func (s *State) Settled() bool
- func (s *State) StepRunCount(step string) int
- type TaskState
- type TaskStatus
Constants ¶
const ( // SetTaskStatusTool is the name of the tool the controller calls to record // where a task stands. It is reserved and cannot name a step. SetTaskStatusTool = "set_task_status" // AskUserTool is the name of the tool the controller calls to put a question // to a person. It is reserved and cannot name a step. AskUserTool = "ask_user" )
const ( // EventAction is one run of a declared step. EventAction = "action" // EventTaskStatus records the controller settling, or reopening, a task. // The new status is carried on the event. EventTaskStatus = "task_status" // EventAskUser is a question the controller put to a person. EventAskUser = "ask_user" // EventRejected is a tool call the controller could not carry out. EventRejected = "rejected" // EventStalled is a turn where the model declined to act. EventStalled = "stalled" )
Event kinds recorded on the controller's decision timeline.
Variables ¶
This section is empty.
Functions ¶
func MergeParams ¶
MergeParams combines the parameters a step supplies itself with the arguments the controller chose, rendering both as the "key=value" string a child DAG run expects. A pinned parameter is passed through exactly as the step wrote it, and an argument naming one is dropped: it was never offered, so a model that invented it does not get to override the author.
func ParamString ¶
ParamString renders tool-call arguments as the "key=value" parameter string a child DAG run expects. Keys are sorted so the same arguments always produce the same child run ID.
func PinnedParams ¶
PinnedParams lists the parameter names a step supplies itself.
func ValidTaskStatus ¶
ValidTaskStatus reports whether a value names a task status the controller may set. "open" is included: settling a task can be undone.
Types ¶
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is the set of actions a controller may choose from, expressed as LLM function-calling tools.
func NewCatalog ¶
NewCatalog builds the tool catalog from a controller DAG's declared steps. A step that runs a child DAG advertises that DAG's parameters, so the controller can pass arguments through.
func (*Catalog) Definitions ¶
func (c *Catalog) Definitions() []exec.ToolDefinition
Definitions returns the catalog in the form persisted for UI visibility.
func (*Catalog) StepFor ¶
StepFor resolves a tool name back to the step it runs. It returns false for CompleteTaskTool and for unknown names.
type Decision ¶
type Decision struct {
Kind DecisionKind
ToolCallID string
ToolName string
// Step is the declared step to run, set when Kind is DecideRunStep.
Step string
// Args are the child DAG parameters, set when Kind is DecideRunStep.
Args map[string]any
// Task, TaskStatus and Reason are set when Kind is DecideSetTaskStatus.
Task string
TaskStatus TaskStatus
Reason string
// Question is set when Kind is DecideAskUser.
Question string
// Content is the model's prose, set when Kind is DecideStop.
Content string
// Problem describes why the decision was rejected, set when Kind is DecideInvalid.
Problem string
}
Decision is the single action the controller chose for this turn.
type DecisionKind ¶
type DecisionKind int
DecisionKind classifies what the controller chose to do this turn.
const ( // DecideRunStep runs one declared step. DecideRunStep DecisionKind = iota // DecideSetTaskStatus records where a task stands. DecideSetTaskStatus // DecideAskUser puts a question to a person and waits for the answer. DecideAskUser // DecideStop is returned when the model answers without calling a tool. DecideStop // DecideInvalid is returned when the model calls a tool that does not exist // or passes arguments that cannot be decoded. The caller reports the problem // back to the model and continues. DecideInvalid )
type Event ¶
type Event struct {
Turn int `json:"turn"`
Kind string `json:"kind"`
// Name is the step or task the event concerns.
Name string `json:"name,omitempty"`
// Status is the resulting node status, for action events.
Status string `json:"status,omitempty"`
// Attempt counts this run of the step, starting at 1.
Attempt int `json:"attempt,omitempty"`
// Reason carries the controller's justification, or why a call was rejected.
Reason string `json:"reason,omitempty"`
StartedAt string `json:"startedAt,omitempty"`
FinishedAt string `json:"finishedAt,omitempty"`
// ChildDAGRunID identifies the child run this action produced, so the
// timeline can link straight to it. Empty for steps that run no child DAG.
ChildDAGRunID string `json:"childDagRunId,omitempty"`
// ChildDAGName is the child DAG that ran.
ChildDAGName string `json:"childDagName,omitempty"`
}
Event is one entry on the controller's decision timeline. The timeline is what makes a controller run legible: it records what ran, in what order, and when each task was satisfied, none of which a dependency graph can express.
func EventsFromState ¶
func EventsFromState(raw json.RawMessage) []Event
EventsFromState decodes the decision timeline persisted on a controller node. Unreadable or absent state yields no events rather than an error.
type MaskFunc ¶
type MaskFunc func([]exec.LLMMessage) []exec.LLMMessage
Planner asks the model which action to take next. MaskFunc hides secret values in the copy of a conversation that leaves for an external model.
type PendingAction ¶
type PendingAction struct {
ToolCallID string `json:"toolCallId"`
ToolName string `json:"toolName"`
Step string `json:"step"`
// Question is the text put to a person, set when the pending action is a
// question the controller asked.
Question string `json:"question,omitempty"`
}
PendingAction records the tool call whose observation has not been reported back to the LLM yet. It is set while a chosen step runs so that a run which suspends mid-action can report the outcome after it resumes.
type Planner ¶
type Planner struct {
// contains filtered or unexported fields
}
func NewPlanner ¶
func NewPlanner( provider llmpkg.Provider, cfg *core.LLMConfig, catalog *Catalog, system string, mask MaskFunc, ) *Planner
NewPlanner builds a planner over a provider and an action catalog. The system prompt is prepended to the controller's own framing; callers pass it already resolved, so a workflow can parameterise its instructions.
type State ¶
type State struct {
Tasks []TaskState `json:"tasks"`
// Events is the decision timeline, in the order decisions were made.
Events []Event `json:"events,omitempty"`
// StepRuns counts how many times each step has been started.
StepRuns map[string]int `json:"stepRuns,omitempty"`
// Turns counts LLM decisions made so far.
Turns int `json:"turns,omitempty"`
// Pending is set while an action is in flight.
Pending *PendingAction `json:"pending,omitempty"`
// Nudges counts consecutive turns where the LLM declined to act while tasks
// were still open.
Nudges int `json:"nudges,omitempty"`
// Answers records what a person replied to each question, so the controller
// is held to an answer it already has.
Answers map[string]string `json:"answers,omitempty"`
// ObservationAging records that old tool results must remain compacted for
// the rest of this run.
ObservationAging bool `json:"observationAging,omitempty"`
// contains filtered or unexported fields
}
State is the controller's durable memory. It survives suspension because it is persisted on the controller node and carried into the resumed run.
func LoadState ¶
func LoadState(raw json.RawMessage, messages []exec.LLMMessage, dag *core.DAG) (*State, error)
LoadState restores state persisted by an earlier attempt of the same run and reconciles it with the DAG, so that editing the task list between attempts neither drops progress nor resurrects removed tasks.
func (*State) Append ¶
func (s *State) Append(msgs ...exec.LLMMessage)
Append adds a message to the conversation.
func (*State) CompactAllObservations ¶ added in v2.11.3
CompactAllObservations replaces each tool result whose deterministic summary is smaller. It returns the number replaced so callers can avoid retrying an unchanged request.
func (*State) CompactObservations ¶ added in v2.11.3
CompactObservations replaces all but the newest keepRecent tool results with deterministic one-line summaries. Positive maxBytes values also bound each summary. Assistant tool calls and result IDs remain unchanged so the conversation continues to satisfy provider tool protocols. Zero keepRecent disables compaction.
func (*State) EnableObservationAging ¶ added in v2.11.3
func (s *State) EnableObservationAging()
EnableObservationAging keeps old observations compacted for subsequent decisions and across suspension or retry.
func (*State) FailedTasks ¶
FailedTasks lists the tasks the controller declared unachievable, with the reason it gave. A run with any of these has failed.
func (*State) FinalizeEvent ¶
FinalizeEvent updates the most recent event for a step with the outcome it reached. An action that suspended was recorded as waiting, and only the run that resumes knows how it ended.
func (*State) LatestPromptTokens ¶ added in v2.11.3
LatestPromptTokens returns the most recent prompt size reported by the provider. Zero means no decision reported usage.
func (*State) Marshal ¶
func (s *State) Marshal() (json.RawMessage, error)
Marshal serializes the state for persistence on the controller node.
func (*State) Messages ¶
func (s *State) Messages() []exec.LLMMessage
Messages returns the conversation so far.
func (*State) OpenTaskNames ¶
OpenTaskNames lists the tasks the controller has yet to settle.
func (*State) PriorAnswer ¶
PriorAnswer returns what a person already said to this exact question.
func (*State) QuestionCount ¶
QuestionCount reports how many questions have been answered so far.
func (*State) RecordAnswer ¶
RecordAnswer stores a reply so the same question is not put to a person twice.
func (*State) RecordEvent ¶
RecordEvent appends an entry to the decision timeline, stamping it with the turn it belongs to.
func (*State) RecordStepRun ¶
RecordStepRun counts a step start and returns the new total.
func (*State) SetTaskStatus ¶
func (s *State) SetTaskStatus(name string, status TaskStatus, reason string) error
SetTaskStatus records where a task now stands. Naming an unknown task, or restating the status a task already has, is reported back to the controller as a tool error rather than failing the run.
func (*State) StepRunCount ¶
StepRunCount reports how many times a step has been started in this run.
type TaskState ¶
type TaskState struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Status TaskStatus `json:"status,omitempty"`
// Reason is the justification the controller gave for the current status.
Reason string `json:"reason,omitempty"`
// Done is the status this field used to carry. It is read when restoring a
// run that was suspended by an earlier version, and never written.
Done bool `json:"done,omitempty"`
}
TaskState tracks one goal across the lifetime of a controller run.
func TasksFromState ¶
func TasksFromState(raw json.RawMessage) []TaskState
TasksFromState decodes the task progress persisted on a controller node. Unreadable or absent state yields no tasks rather than an error, so a display surface never fails on it.
type TaskStatus ¶
type TaskStatus string
TaskStatus is where a goal stands. A run ends once no task is open.
const ( // TaskOpen is a goal still to be settled. Tasks start here. TaskOpen TaskStatus = "open" // TaskCompleted is a goal that was achieved. TaskCompleted TaskStatus = "completed" // TaskSkipped is a goal the controller judged unnecessary. It does not fail // the run: nothing went wrong, there was simply nothing to do. TaskSkipped TaskStatus = "skipped" // TaskFailed is a goal that cannot be achieved. It fails the run, because // the goal was neither met nor waived. TaskFailed TaskStatus = "failed" )