Files
tutti/services/tuttid/service/modelplan/native_subscription_detection.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

160 lines
5.3 KiB
Go

package modelplan
import (
"context"
"strings"
"time"
modelplanbiz "github.com/tutti-os/tutti/services/tuttid/biz/modelplan"
)
// NativeSubscriptionProbe verifies an official provider subscription through
// the provider's own Agent runtime and stored login. It never receives or
// returns provider credentials.
type NativeSubscriptionProbe interface {
ProbeNativeSubscription(context.Context, NativeSubscriptionProbeInput) (NativeSubscriptionProbeResult, error)
}
type NativeSubscriptionProbeInput struct {
WorkspaceID string
Protocol modelplanbiz.Protocol
Model string
Models []modelplanbiz.Model
}
type NativeSubscriptionProbeResult struct {
RuntimeAvailable bool
RuntimeDetail string
RuntimeLatencyMs int64
Authenticated bool
AuthDetail string
DiscoveredModels []modelplanbiz.Model
DiscoveryDetail string
DiscoveryLatencyMs int64
InferenceAttempted bool
InferencePassed bool
InferenceModel string
InferenceDetail string
InferenceLatencyMs int64
}
func (s *Service) detectNativeSubscription(
ctx context.Context,
input NativeSubscriptionProbeInput,
now time.Time,
) (modelplanbiz.DetectionSnapshot, []modelplanbiz.Model) {
snapshot := modelplanbiz.DetectionSnapshot{CheckedAt: now, Model: strings.TrimSpace(input.Model)}
if s.NativeSubscriptionProbe == nil {
snapshot.Stages = nativeSubscriptionUnavailableStages(now, "native subscription probe is unavailable")
return snapshot, nil
}
result, err := s.NativeSubscriptionProbe.ProbeNativeSubscription(ctx, input)
if err != nil {
snapshot.Stages = nativeSubscriptionUnavailableStages(now, sanitizeNativeProbeDetail(err.Error()))
return snapshot, nil
}
if model := strings.TrimSpace(result.InferenceModel); model != "" {
snapshot.Model = model
}
network := modelplanbiz.StageResult{
Stage: modelplanbiz.StageNetwork,
Status: modelplanbiz.StagePassed,
LatencyMs: result.RuntimeLatencyMs,
Detail: sanitizeNativeProbeDetail(result.RuntimeDetail),
CheckedAt: now,
}
if !result.RuntimeAvailable {
network.Status = modelplanbiz.StageFailed
network.FailureReason = FailureProviderRuntime
network.Remedy = RemedyEnableProvider
}
auth := modelplanbiz.StageResult{
Stage: modelplanbiz.StageAuth,
Status: modelplanbiz.StageSkipped,
Detail: sanitizeNativeProbeDetail(result.AuthDetail),
CheckedAt: now,
}
if result.RuntimeAvailable {
auth.Status = modelplanbiz.StagePassed
if !result.Authenticated {
auth.Status = modelplanbiz.StageFailed
auth.FailureReason = FailureProviderAuth
auth.Remedy = RemedyLoginProvider
}
}
discovered := modelplanbiz.NormalizeModels(result.DiscoveredModels)
discovery := modelplanbiz.StageResult{
Stage: modelplanbiz.StageModelDiscovery,
Status: modelplanbiz.StageSkipped,
LatencyMs: result.DiscoveryLatencyMs,
Detail: sanitizeNativeProbeDetail(result.DiscoveryDetail),
CheckedAt: now,
}
if network.Status == modelplanbiz.StagePassed && auth.Status == modelplanbiz.StagePassed {
switch {
case len(discovered) > 0:
discovery.Status = modelplanbiz.StagePassed
case len(modelplanbiz.NormalizeModels(input.Models)) > 0:
discovery.Status = modelplanbiz.StageSkipped
if discovery.Detail == "" {
discovery.Detail = "using configured model list"
}
default:
discovery.Status = modelplanbiz.StageFailed
discovery.FailureReason = FailureCatalogNotFound
discovery.Remedy = RemedyAddModelsManually
}
}
inference := modelplanbiz.StageResult{
Stage: modelplanbiz.StageInference,
Status: modelplanbiz.StageSkipped,
LatencyMs: result.InferenceLatencyMs,
Detail: sanitizeNativeProbeDetail(result.InferenceDetail),
CheckedAt: now,
}
if network.Status == modelplanbiz.StagePassed && auth.Status == modelplanbiz.StagePassed && discovery.Status != modelplanbiz.StageFailed {
switch {
case strings.TrimSpace(snapshot.Model) == "":
inference.Status = modelplanbiz.StageFailed
inference.FailureReason = FailureNoModel
inference.Remedy = RemedySelectModel
case !result.InferenceAttempted:
inference.Status = modelplanbiz.StageFailed
inference.FailureReason = FailureInference
inference.Remedy = RemedyRetryInference
case result.InferencePassed:
inference.Status = modelplanbiz.StagePassed
default:
inference.Status = modelplanbiz.StageFailed
inference.FailureReason = FailureInference
inference.Remedy = RemedyRetryInference
}
}
snapshot.Stages = []modelplanbiz.StageResult{network, auth, discovery, inference}
return snapshot, discovered
}
func nativeSubscriptionUnavailableStages(now time.Time, detail string) []modelplanbiz.StageResult {
return []modelplanbiz.StageResult{
{Stage: modelplanbiz.StageNetwork, Status: modelplanbiz.StageFailed, FailureReason: FailureProviderRuntime, Remedy: RemedyEnableProvider, Detail: detail, CheckedAt: now},
{Stage: modelplanbiz.StageAuth, Status: modelplanbiz.StageSkipped, CheckedAt: now},
{Stage: modelplanbiz.StageModelDiscovery, Status: modelplanbiz.StageSkipped, CheckedAt: now},
{Stage: modelplanbiz.StageInference, Status: modelplanbiz.StageSkipped, CheckedAt: now},
}
}
func sanitizeNativeProbeDetail(detail string) string {
detail = strings.TrimSpace(detail)
const maxDetailBytes = 500
if len(detail) > maxDetailBytes {
return detail[:maxDetailBytes]
}
return detail
}