Changeset 0.28.0.2 (#174)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// Package scheduler — executor.go
|
||||
//
|
||||
// v0.27.2: Headless task execution via coreToolLoop.
|
||||
// v0.28.0: Action task type (no LLM), trigger payload passthrough,
|
||||
// webhook payload shape fix (D1).
|
||||
//
|
||||
// The Executor bridges the task scheduler with the completion pipeline.
|
||||
// It resolves providers, builds tool definitions, runs the core tool loop
|
||||
@@ -49,6 +51,12 @@ func NewExecutor(stores store.Stores, vault *crypto.KeyResolver, hub *events.Hub
|
||||
func (e *Executor) Execute(ctx context.Context, task models.Task, run *models.TaskRun, channelID string) {
|
||||
startTime := time.Now()
|
||||
|
||||
// v0.28.0: Action tasks skip the LLM pipeline entirely.
|
||||
if task.TaskType == "action" {
|
||||
e.executeAction(ctx, task, run, channelID, startTime)
|
||||
return
|
||||
}
|
||||
|
||||
// ── 1. Resolve provider ────────────────────
|
||||
providerConfigID := ""
|
||||
if task.ProviderConfigID != nil {
|
||||
@@ -92,11 +100,17 @@ func (e *Executor) Execute(ctx context.Context, task models.Task, run *models.Ta
|
||||
})
|
||||
}
|
||||
|
||||
// User prompt
|
||||
if task.UserPrompt != "" {
|
||||
// User prompt — with optional trigger payload prepended
|
||||
userContent := task.UserPrompt
|
||||
if run.TriggerPayload != "" && userContent != "" {
|
||||
userContent = "[Webhook trigger data]\n```json\n" + run.TriggerPayload + "\n```\n\n[Task instructions]\n" + userContent
|
||||
} else if run.TriggerPayload != "" {
|
||||
userContent = run.TriggerPayload
|
||||
}
|
||||
if userContent != "" {
|
||||
messages = append(messages, providers.Message{
|
||||
Role: "user",
|
||||
Content: task.UserPrompt,
|
||||
Content: userContent,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -216,18 +230,15 @@ func (e *Executor) Execute(ctx context.Context, task models.Task, run *models.Ta
|
||||
errMsg = "budget exceeded: " + result.BudgetExceeded
|
||||
}
|
||||
|
||||
tokensUsed := result.InputTokens + result.OutputTokens
|
||||
|
||||
// ── 8. Update run record ───────────────────
|
||||
_ = e.stores.Tasks.UpdateRun(ctx, run.ID, status,
|
||||
result.InputTokens+result.OutputTokens,
|
||||
result.ToolCallCount,
|
||||
wallClock,
|
||||
errMsg)
|
||||
tokensUsed, result.ToolCallCount, wallClock, errMsg)
|
||||
_ = e.stores.Tasks.IncrementRunCount(ctx, task.ID)
|
||||
|
||||
log.Printf("[executor] Task %s (%s) → %s (tokens=%d, tools=%d, wall=%ds)",
|
||||
task.ID, task.Name, status,
|
||||
result.InputTokens+result.OutputTokens,
|
||||
result.ToolCallCount, wallClock)
|
||||
task.ID, task.Name, status, tokensUsed, result.ToolCallCount, wallClock)
|
||||
|
||||
// ── 9. Owner notification ──────────────────
|
||||
e.notifyOwner(ctx, task, status, errMsg)
|
||||
@@ -236,16 +247,61 @@ func (e *Executor) Execute(ctx context.Context, task models.Task, run *models.Ta
|
||||
if task.WebhookURL != "" {
|
||||
go webhook.Deliver(task.WebhookURL, task.WebhookSecret, webhook.Payload{
|
||||
TaskID: task.ID,
|
||||
RunID: run.ID,
|
||||
TaskName: task.Name,
|
||||
ChannelID: channelID,
|
||||
Status: status,
|
||||
CompletedAt: time.Now().UTC(),
|
||||
Output: result.Content,
|
||||
TokensUsed: tokensUsed,
|
||||
Error: errMsg,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// executeAction handles non-LLM action tasks.
|
||||
// Skips provider resolution and completion entirely.
|
||||
func (e *Executor) executeAction(ctx context.Context, task models.Task, run *models.TaskRun, channelID string, startTime time.Time) {
|
||||
wallClock := int(time.Since(startTime).Seconds())
|
||||
status := "completed"
|
||||
|
||||
// v0.28.0: Action tasks relay trigger payload to outbound webhook.
|
||||
// No LLM execution — the value is in the automation plumbing.
|
||||
output := run.TriggerPayload
|
||||
if output == "" {
|
||||
output = "{}"
|
||||
}
|
||||
|
||||
// Persist to channel if output_mode == "channel"
|
||||
if task.OutputMode == "channel" && e.stores.Messages != nil {
|
||||
_ = e.stores.Messages.Create(ctx, &models.Message{
|
||||
ChannelID: channelID,
|
||||
Role: "system",
|
||||
Content: "Action task executed. Trigger payload:\n```json\n" + output + "\n```",
|
||||
})
|
||||
}
|
||||
|
||||
_ = e.stores.Tasks.UpdateRun(ctx, run.ID, status, 0, 0, wallClock, "")
|
||||
_ = e.stores.Tasks.IncrementRunCount(ctx, task.ID)
|
||||
|
||||
log.Printf("[executor] Action task %s (%s) → %s (wall=%ds)", task.ID, task.Name, status, wallClock)
|
||||
|
||||
e.notifyOwner(ctx, task, status, "")
|
||||
|
||||
// Fire outbound webhook with trigger payload
|
||||
if task.WebhookURL != "" {
|
||||
go webhook.Deliver(task.WebhookURL, task.WebhookSecret, webhook.Payload{
|
||||
TaskID: task.ID,
|
||||
RunID: run.ID,
|
||||
TaskName: task.Name,
|
||||
ChannelID: channelID,
|
||||
Status: status,
|
||||
CompletedAt: time.Now().UTC(),
|
||||
Output: output,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// failRun marks a run as failed before completion was attempted.
|
||||
func (e *Executor) failRun(ctx context.Context, task models.Task, run *models.TaskRun, errMsg string) {
|
||||
log.Printf("[executor] Task %s (%s) pre-execution failure: %s", task.ID, task.Name, errMsg)
|
||||
@@ -254,6 +310,7 @@ func (e *Executor) failRun(ctx context.Context, task models.Task, run *models.Ta
|
||||
if task.WebhookURL != "" {
|
||||
go webhook.Deliver(task.WebhookURL, task.WebhookSecret, webhook.Payload{
|
||||
TaskID: task.ID,
|
||||
RunID: run.ID,
|
||||
TaskName: task.Name,
|
||||
Status: "failed",
|
||||
CompletedAt: time.Now().UTC(),
|
||||
|
||||
Reference in New Issue
Block a user