初始提交:边缘AI算力机统一AI通讯层
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
CI / security-scan (push) Has been cancelled

This commit is contained in:
freedakgmail
2026-08-03 07:44:05 +08:00
commit 93a469061d
51 changed files with 11565 additions and 0 deletions
+190
View File
@@ -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
}