初始提交:边缘AI算力机统一AI通讯层
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/edgeai/gateway/internal/observability"
|
||||
)
|
||||
|
||||
// TaskState represents the lifecycle state of a task.
|
||||
type TaskState string
|
||||
|
||||
const (
|
||||
StateQueued TaskState = "QUEUED"
|
||||
StateRunning TaskState = "RUNNING"
|
||||
StateStreaming TaskState = "STREAMING"
|
||||
StateCompleted TaskState = "COMPLETED"
|
||||
StateFailed TaskState = "FAILED"
|
||||
StateCancelled TaskState = "CANCELLED"
|
||||
)
|
||||
|
||||
// TaskPriority levels (P0 highest, P4 lowest).
|
||||
type TaskPriority int
|
||||
|
||||
const (
|
||||
PriorityRealtime TaskPriority = 0 // P0
|
||||
PriorityHigh TaskPriority = 1 // P1
|
||||
PriorityNormal TaskPriority = 2 // P2 (default)
|
||||
PriorityLow TaskPriority = 3 // P3
|
||||
PriorityBackground TaskPriority = 4 // P4
|
||||
)
|
||||
|
||||
// Task represents an inference task in the system.
|
||||
type Task struct {
|
||||
ID string
|
||||
RequestID string
|
||||
SessionID string
|
||||
AppID string
|
||||
TenantID string
|
||||
LogicalModel string
|
||||
ActualModel string
|
||||
Priority TaskPriority
|
||||
State TaskState
|
||||
Stream bool
|
||||
CreatedAt time.Time
|
||||
StartedAt *time.Time
|
||||
CompletedAt *time.Time
|
||||
CancelReason string
|
||||
ErrorMessage string
|
||||
InputTokens int
|
||||
OutputTokens int
|
||||
NodeID string
|
||||
Degraded bool
|
||||
cancelCh chan struct{}
|
||||
cancelOnce sync.Once
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewTask creates a new task in QUEUED state.
|
||||
func NewTask(id, requestID, appID, tenantID, logicalModel string, priority TaskPriority, stream bool) *Task {
|
||||
return &Task{
|
||||
ID: id,
|
||||
RequestID: requestID,
|
||||
AppID: appID,
|
||||
TenantID: tenantID,
|
||||
LogicalModel: logicalModel,
|
||||
Priority: priority,
|
||||
State: StateQueued,
|
||||
Stream: stream,
|
||||
CreatedAt: time.Now(),
|
||||
cancelCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// AllowedTransitions defines valid state transitions.
|
||||
var allowedTransitions = map[TaskState][]TaskState{
|
||||
StateQueued: {StateRunning, StateFailed, StateCancelled},
|
||||
StateRunning: {StateStreaming, StateCompleted, StateFailed, StateCancelled},
|
||||
StateStreaming: {StateCompleted, StateFailed, StateCancelled},
|
||||
StateCompleted: {},
|
||||
StateFailed: {},
|
||||
StateCancelled: {},
|
||||
}
|
||||
|
||||
// Transition changes the task state if the transition is valid.
|
||||
func (t *Task) Transition(to TaskState) error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
allowed, ok := allowedTransitions[t.State]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown current state: %s", t.State)
|
||||
}
|
||||
|
||||
valid := false
|
||||
for _, s := range allowed {
|
||||
if s == to {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("invalid transition: %s -> %s", t.State, to)
|
||||
}
|
||||
|
||||
from := t.State
|
||||
t.State = to
|
||||
now := time.Now()
|
||||
|
||||
switch to {
|
||||
case StateRunning:
|
||||
t.StartedAt = &now
|
||||
case StateCompleted, StateFailed, StateCancelled:
|
||||
t.CompletedAt = &now
|
||||
}
|
||||
|
||||
_ = from
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cancel signals task cancellation and transitions to CANCELLED if possible.
|
||||
func (t *Task) Cancel(reason string) error {
|
||||
t.cancelOnce.Do(func() {
|
||||
close(t.cancelCh)
|
||||
})
|
||||
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
if t.State == StateCompleted || t.State == StateFailed || t.State == StateCancelled {
|
||||
return errors.New("task already in terminal state")
|
||||
}
|
||||
|
||||
t.CancelReason = reason
|
||||
t.State = StateCancelled
|
||||
now := time.Now()
|
||||
t.CompletedAt = &now
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cancelled returns a channel that's closed when the task is cancelled.
|
||||
func (t *Task) Cancelled() <-chan struct{} {
|
||||
return t.cancelCh
|
||||
}
|
||||
|
||||
// IsCancelled returns true if the task has been cancelled.
|
||||
func (t *Task) IsCancelled() bool {
|
||||
select {
|
||||
case <-t.cancelCh:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// GetState returns the current state (thread-safe).
|
||||
func (t *Task) GetState() TaskState {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
return t.State
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the task is in a terminal state.
|
||||
func (t *Task) IsTerminal() bool {
|
||||
s := t.GetState()
|
||||
return s == StateCompleted || s == StateFailed || s == StateCancelled
|
||||
}
|
||||
|
||||
// StateMachineLogger logs state transitions.
|
||||
type StateMachineLogger struct {
|
||||
logger *observability.Logger
|
||||
}
|
||||
|
||||
func NewStateMachineLogger(logger *observability.Logger) *StateMachineLogger {
|
||||
return &StateMachineLogger{logger: logger}
|
||||
}
|
||||
|
||||
// LogTransition logs a state transition.
|
||||
func (sml *StateMachineLogger) LogTransition(task *Task, from, to TaskState, reason string) {
|
||||
sml.logger.Info("task state transition",
|
||||
observability.F().
|
||||
Event("state_transition").
|
||||
TaskID(task.ID).
|
||||
Set("from_state", string(from)).
|
||||
Set("to_state", string(to)).
|
||||
Reason(reason))
|
||||
_ = from // used in log field above
|
||||
}
|
||||
Reference in New Issue
Block a user