mirror of
https://github.com/open-gitagent/langship.sh.git
synced 2026-08-03 07:21:04 +02:00
117 lines
3.6 KiB
Go
117 lines
3.6 KiB
Go
package orchestrator
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
|
|
restate "github.com/restatedev/sdk-go"
|
|
"github.com/restatedev/sdk-go/ingress"
|
|
|
|
"github.com/lyzrai/flow/pkg/models"
|
|
)
|
|
|
|
// RestateOrchestrator submits workflows to a Restate server via its HTTP ingress.
|
|
// Each node in the workflow becomes a journaled step that survives crashes.
|
|
type RestateOrchestrator struct {
|
|
restateURL string
|
|
}
|
|
|
|
// NewRestateOrchestrator constructs an orchestrator pointed at restateURL
|
|
// (the Restate ingress URL, e.g. http://localhost:8081).
|
|
func NewRestateOrchestrator(restateURL string) *RestateOrchestrator {
|
|
return &RestateOrchestrator{restateURL: restateURL}
|
|
}
|
|
|
|
// IngressURL returns the Restate ingress URL for external API calls
|
|
// (e.g., awakeable resolution from the resume HTTP handler).
|
|
func (o *RestateOrchestrator) IngressURL() string { return o.restateURL }
|
|
|
|
// Run submits a workflow synchronously and blocks until it finishes.
|
|
func (o *RestateOrchestrator) Run(ctx context.Context, req *RunRequest) (string, *models.ExecutionResult, error) {
|
|
client := ingress.NewClient(o.restateURL)
|
|
workflowID := generateWorkflowID()
|
|
|
|
wfReq := &WorkflowRequest{
|
|
RequestMeta: req.RequestMeta,
|
|
Workflow: req.Workflow,
|
|
TriggerData: req.TriggerData,
|
|
}
|
|
|
|
result, err := ingress.Workflow[*WorkflowRequest, *models.ExecutionResult](
|
|
client, "WorkflowExecutor", workflowID, "Run",
|
|
).Request(ctx, wfReq)
|
|
if err != nil {
|
|
return workflowID, nil, fmt.Errorf("workflow execution failed: %w", err)
|
|
}
|
|
|
|
result.ExecutionID = workflowID
|
|
return workflowID, result, nil
|
|
}
|
|
|
|
// RunAsync submits a workflow and returns immediately with an execution ID.
|
|
func (o *RestateOrchestrator) RunAsync(ctx context.Context, req *RunRequest) (string, error) {
|
|
client := ingress.NewClient(o.restateURL)
|
|
workflowID := generateWorkflowID()
|
|
|
|
wfReq := &WorkflowRequest{
|
|
RequestMeta: req.RequestMeta,
|
|
Workflow: req.Workflow,
|
|
TriggerData: req.TriggerData,
|
|
}
|
|
|
|
_, err := ingress.Workflow[*WorkflowRequest, *models.ExecutionResult](
|
|
client, "WorkflowExecutor", workflowID, "Run",
|
|
).Send(ctx, wfReq)
|
|
if err != nil {
|
|
return "", fmt.Errorf("async workflow submission failed: %w", err)
|
|
}
|
|
|
|
return workflowID, nil
|
|
}
|
|
|
|
// GetExecution returns the live status of a workflow execution.
|
|
// While running, it also probes for a pending approval via the shared handler.
|
|
func (o *RestateOrchestrator) GetExecution(ctx context.Context, executionID string) (*ExecutionStatus, error) {
|
|
client := ingress.NewClient(o.restateURL)
|
|
|
|
handle := ingress.WorkflowHandle[*models.ExecutionResult](client, "WorkflowExecutor", executionID)
|
|
|
|
result, err := handle.Output(ctx)
|
|
if err != nil {
|
|
var notReady *ingress.InvocationNotReadyError
|
|
if errors.As(err, ¬Ready) {
|
|
status := &ExecutionStatus{ExecutionID: executionID, Status: "running"}
|
|
approval, aErr := ingress.Workflow[restate.Void, *PendingApproval](
|
|
client, "WorkflowExecutor", executionID, "GetPendingApproval",
|
|
).Request(ctx, restate.Void{})
|
|
if aErr == nil && approval != nil && approval.AwakeableID != "" {
|
|
status.Status = "waiting_for_approval"
|
|
status.PendingApproval = approval
|
|
}
|
|
return status, nil
|
|
}
|
|
var notFound *ingress.InvocationNotFoundError
|
|
if errors.As(err, ¬Found) {
|
|
return nil, fmt.Errorf("execution %q not found", executionID)
|
|
}
|
|
return nil, fmt.Errorf("failed to get execution status: %w", err)
|
|
}
|
|
|
|
return &ExecutionStatus{
|
|
ExecutionID: executionID,
|
|
Status: result.Status,
|
|
Outputs: result.Outputs,
|
|
NodeOutputs: result.NodeOutputs,
|
|
Errors: result.Errors,
|
|
}, nil
|
|
}
|
|
|
|
func generateWorkflowID() string {
|
|
b := make([]byte, 16)
|
|
_, _ = rand.Read(b)
|
|
return hex.EncodeToString(b)
|
|
}
|