iop/apps/edge/internal/service/run_wire.go
toki fef1f7a9dc feat(liveness): provider 실행 stall 관측을 구현한다
Node의 provider progress 기반 stall timeout, watchdog fencing과 bounded health probe evidence를 실행 경로에 반영한다. Edge-Node 계약과 구현 스펙, 테스트 및 Milestone 완료 evidence를 현재 상태와 맞춘다.
2026-08-05 09:45:14 +09:00

78 lines
1.8 KiB
Go

package service
import (
"fmt"
"sync/atomic"
"time"
"google.golang.org/protobuf/types/known/structpb"
eventpkg "iop/packages/go/events"
"iop/packages/go/execution"
iop "iop/proto/gen/iop"
)
var lastRunIDNanos atomic.Int64
func dispatchResponseStallTimeout(ms int64) int64 {
if ms == 0 {
return execution.DefaultResponseStallTimeoutMS
}
return ms
}
func NewRunID() string {
return newRunIDAt(time.Now().UnixNano())
}
func newRunIDAt(now int64) string {
for {
previous := lastRunIDNanos.Load()
next := now
if next <= previous {
next = previous + 1
}
if lastRunIDNanos.CompareAndSwap(previous, next) {
return fmt.Sprintf("manual-%d", next)
}
}
}
func IsNodeDisconnected(event *iop.EdgeNodeEvent) bool {
return event.GetType() == eventpkg.TypeNodeDisconnected
}
func BuildRunRequest(req SubmitRunRequest) (*iop.RunRequest, string, error) {
inputMap := make(map[string]any, len(req.Input)+1)
for k, v := range req.Input {
inputMap[k] = v
}
if req.Prompt != "" {
if _, ok := inputMap["prompt"]; !ok {
inputMap["prompt"] = req.Prompt
}
}
input, err := structpb.NewStruct(inputMap)
if err != nil {
return nil, "", err
}
runID := req.RunID
if runID == "" {
runID = NewRunID()
}
metadata := make(map[string]string, len(req.Metadata))
for k, v := range req.Metadata {
metadata[k] = v
}
return &iop.RunRequest{
RunId: runID,
Adapter: req.Adapter,
Target: req.Target,
SessionId: NormalizeSessionID(req.SessionID),
Background: req.Background,
Input: input,
TimeoutSec: int32(normalizeTimeoutSec(req.TimeoutSec)),
Metadata: metadata,
ResponseStallTimeoutMs: req.ResponseStallTimeoutMS,
}, runID, nil
}