Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions internal/control/credential_collection_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,14 @@ func parseCredentialCollectionQuery(rawQuery string) (CredentialCollectionQuery,
}
for key, entries := range values {
switch key {
case "q", "status", "page", "page_size", "model_cooldown":
case "q", "status", "page", "page_size":
default:
return CredentialCollectionQuery{}, app_errors.ErrBadRequest
}
if len(entries) != 1 {
return CredentialCollectionQuery{}, app_errors.ErrBadRequest
}
}
if entries, exists := values["model_cooldown"]; exists {
if entries[0] != "true" {
return CredentialCollectionQuery{}, app_errors.ErrBadRequest
}
query.ModelCooldown = true
}
if entries, exists := values["q"]; exists {
query.Query = strings.TrimSpace(entries[0])
}
Expand Down
3 changes: 0 additions & 3 deletions internal/control/credential_mutations.go
Original file line number Diff line number Diff line change
Expand Up @@ -785,9 +785,6 @@ func summarizeGroupRuntimeCredentials(
continue
}
summary.Total++
if hasModelCooldown(view.ModelCooldowns, observedAt) {
summary.ModelCooldown++
}
switch classifyHealthKey(groupView, view, observedAt) {
case healthBucketAvailable:
summary.Available++
Expand Down
26 changes: 9 additions & 17 deletions internal/control/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ type CredentialRevealResult struct {
}

type CredentialCollectionQuery struct {
Query string
Status *string
ModelCooldown bool
Page int
PageSize int
Query string
Status *string
Page int
PageSize int
}

type CredentialCollectionResponse struct {
Expand All @@ -59,12 +58,11 @@ type CredentialCollectionResponse struct {
}

type CredentialSummaryResponse struct {
Total int `json:"total"`
Available int `json:"available"`
Cooldown int `json:"cooldown"`
Blacklisted int `json:"blacklisted"`
Disabled int `json:"disabled"`
ModelCooldown int `json:"model_cooldown"`
Total int `json:"total"`
Available int `json:"available"`
Cooldown int `json:"cooldown"`
Blacklisted int `json:"blacklisted"`
Disabled int `json:"disabled"`
}

type CredentialAccountResponse struct {
Expand Down Expand Up @@ -468,9 +466,6 @@ func (s *Service) mapCredentialCollection(
func summarizeCredentialCollection(records []credentialCollectionRecord) CredentialSummaryResponse {
summary := CredentialSummaryResponse{Total: len(records)}
for _, record := range records {
if len(record.item.ModelCooldowns) > 0 {
summary.ModelCooldown++
}
switch record.bucket {
case healthBucketAvailable:
summary.Available++
Expand All @@ -489,9 +484,6 @@ func credentialCollectionMatches(record credentialCollectionRecord, query Creden
if query.Status != nil && record.item.EffectiveStatus != *query.Status {
return false
}
if query.ModelCooldown && len(record.item.ModelCooldowns) == 0 {
return false
}
if query.Query == "" {
return true
}
Expand Down
81 changes: 35 additions & 46 deletions internal/control/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@ type RequestLogStatsReader interface {
}

type healthCountsResponse struct {
Credentials int `json:"credentials"`
Available int `json:"available"`
Cooldown int `json:"cooldown"`
Blacklisted int `json:"blacklisted"`
}

type healthGroupCountsResponse struct {
healthCountsResponse
ModelCooldown int `json:"model_cooldown"`
Credentials int `json:"credentials"`
Available int `json:"available"`
Cooldown int `json:"cooldown"`
Blacklisted int `json:"blacklisted"`
}

type healthGroupResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
Counts healthCountsResponse `json:"counts"`
ID uint `json:"id"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
Counts healthGroupCountsResponse `json:"counts"`
}

type healthRecoveryResponse struct {
Expand Down Expand Up @@ -98,20 +102,19 @@ type requestLogHealthResponse struct {
}

type runtimeHealthResponse struct {
ModelCooldownCredentials []healthModelCooldownCredentialResponse `json:"model_cooldown_credentials"`
ObservedAtMS int64 `json:"observed_at_ms"`
Version string `json:"version"`
UptimeSeconds int64 `json:"uptime_seconds"`
SnapshotRevision uint64 `json:"snapshot_revision"`
StatsWindowSeconds int64 `json:"stats_window_seconds"`
Counts healthCountsResponse `json:"counts"`
Groups []healthGroupResponse `json:"groups"`
CooldownCredentials []healthProblemCredentialResponse `json:"cooldown_credentials"`
BlacklistedCredentials []healthProblemCredentialResponse `json:"blacklisted_credentials"`
LowQuotaCredentials []healthQuotaCredentialResponse `json:"low_quota_credentials"`
ExpiringResetCredits []healthExpiringResetCreditResponse `json:"expiring_reset_credits"`
BlockedAccessKeys []healthAccessKeyCostLimitResponse `json:"blocked_access_keys"`
RequestLog requestLogHealthResponse `json:"request_log"`
ObservedAtMS int64 `json:"observed_at_ms"`
Version string `json:"version"`
UptimeSeconds int64 `json:"uptime_seconds"`
SnapshotRevision uint64 `json:"snapshot_revision"`
StatsWindowSeconds int64 `json:"stats_window_seconds"`
Counts healthCountsResponse `json:"counts"`
Groups []healthGroupResponse `json:"groups"`
CooldownCredentials []healthProblemCredentialResponse `json:"cooldown_credentials"`
BlacklistedCredentials []healthProblemCredentialResponse `json:"blacklisted_credentials"`
LowQuotaCredentials []healthQuotaCredentialResponse `json:"low_quota_credentials"`
ExpiringResetCredits []healthExpiringResetCreditResponse `json:"expiring_reset_credits"`
BlockedAccessKeys []healthAccessKeyCostLimitResponse `json:"blocked_access_keys"`
RequestLog requestLogHealthResponse `json:"request_log"`
}

type healthAccessKeyCostLimitResponse struct {
Expand Down Expand Up @@ -270,16 +273,15 @@ func (service *Service) RuntimeHealth() (runtimeHealthResponse, error) {
return runtimeHealthResponse{}, fmt.Errorf("map runtime health observed_at_ms: %w", err)
}
result := runtimeHealthResponse{
ModelCooldownCredentials: []healthModelCooldownCredentialResponse{},
ObservedAtMS: observedAtMS,
SnapshotRevision: observation.snapshot.Revision,
StatsWindowSeconds: int64(health.StatsWindow / time.Second),
Groups: []healthGroupResponse{},
CooldownCredentials: []healthProblemCredentialResponse{},
BlacklistedCredentials: []healthProblemCredentialResponse{},
LowQuotaCredentials: []healthQuotaCredentialResponse{},
ExpiringResetCredits: []healthExpiringResetCreditResponse{},
BlockedAccessKeys: []healthAccessKeyCostLimitResponse{},
ObservedAtMS: observedAtMS,
SnapshotRevision: observation.snapshot.Revision,
StatsWindowSeconds: int64(health.StatsWindow / time.Second),
Groups: []healthGroupResponse{},
CooldownCredentials: []healthProblemCredentialResponse{},
BlacklistedCredentials: []healthProblemCredentialResponse{},
LowQuotaCredentials: []healthQuotaCredentialResponse{},
ExpiringResetCredits: []healthExpiringResetCreditResponse{},
BlockedAccessKeys: []healthAccessKeyCostLimitResponse{},
}
groupIDs := make([]uint, 0, len(observation.snapshot.GroupCatalog))
for groupID := range observation.snapshot.GroupCatalog {
Expand All @@ -305,23 +307,10 @@ func (service *Service) RuntimeHealth() (runtimeHealthResponse, error) {
}
}
if hasModelCooldown(key.ModelCooldowns, observation.observedAt) {
result.Counts.ModelCooldown++
result.Groups[index].Counts.ModelCooldown++
if len(result.ModelCooldownCredentials) < healthProblemCredentialDetailLimit {
// 停用分组仍保留模型冷却,身份展示使用完整分组目录。
identity, err := service.healthProblemCredentialIdentity(observation.problemCiphertexts, key.ID, group.ChannelID, group.ConnectionType)
if err != nil {
return runtimeHealthResponse{}, err
}
limits, err := modelCooldownResponses(key.ModelCooldowns, observation.observedAt)
if err != nil {
return runtimeHealthResponse{}, err
}
result.ModelCooldownCredentials = append(result.ModelCooldownCredentials, healthModelCooldownCredentialResponse{CredentialID: key.ID, GroupID: key.GroupID, GroupName: group.Name, Identity: identity, ModelCooldowns: limits})
}
}
addHealthCount(&result.Counts, bucket)
addHealthCount(&result.Groups[index].Counts, bucket)
addHealthCount(&result.Groups[index].Counts.healthCountsResponse, bucket)
// 额度只用于管理面展示,不参与健康分桶或调度;低额度凭据在这里单列提示。
if bucket == healthBucketAvailable || bucket == healthBucketCooldown {
if remaining := key.ObservedQuotaRemaining(); remaining != nil &&
Expand Down
2 changes: 1 addition & 1 deletion internal/control/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func TestRuntimeHealthReturnsMutuallyExclusiveCurrentState(t *testing.T) {
got.Groups[1].ID != 2 || got.Groups[2].ID != 3 || got.Groups[3].ID != 4 {
t.Fatalf("group order = %#v", got.Groups)
}
if got.Groups[0].Counts != (healthCountsResponse{
if got.Groups[0].Counts.healthCountsResponse != (healthCountsResponse{
Credentials: 3, Available: 1, Cooldown: 1, Blacklisted: 1,
}) {
t.Fatalf("active group counts = %#v", got.Groups[0].Counts)
Expand Down
8 changes: 0 additions & 8 deletions internal/control/model_cooldown.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ type ModelCooldownResponse struct {
CooldownUntilMS int64 `json:"cooldown_until_ms"`
}

type healthModelCooldownCredentialResponse struct {
CredentialID uint `json:"credential_id"`
GroupID uint `json:"group_id"`
GroupName string `json:"group_name"`
Identity string `json:"identity"`
ModelCooldowns []ModelCooldownResponse `json:"model_cooldowns"`
}

func hasModelCooldown(limits map[string]time.Time, now time.Time) bool {
for _, until := range limits {
if until.After(now) {
Expand Down
44 changes: 33 additions & 11 deletions internal/control/model_cooldown_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package control

import (
"encoding/json"
"testing"
"time"

Expand Down Expand Up @@ -71,7 +72,7 @@ func TestModelCooldownCollectionAndHealthKeepIndependentAccountStatus(t *testing
for _, model := range []string{"b", "a"} {
fixture.registry.SetModelCooldown(ref, model, now.Add(time.Hour), now)
}
query, parseErr := parseCredentialCollectionQuery("model_cooldown=true")
query, parseErr := parseCredentialCollectionQuery("")
if parseErr != nil {
t.Fatal(parseErr)
}
Expand All @@ -87,7 +88,7 @@ func TestModelCooldownCollectionAndHealthKeepIndependentAccountStatus(t *testing
if err != nil {
t.Fatal(err)
}
if collection.Summary.ModelCooldown != 1 || collection.Summary.Available != 2 || collection.Pagination.TotalItems != 1 || len(collection.Items) != 1 {
if collection.Summary.Available != 2 || collection.Pagination.TotalItems != 2 || len(collection.Items) != 2 {
t.Fatalf("collection = %#v", collection)
}
item := collection.Items[0]
Expand All @@ -98,12 +99,38 @@ func TestModelCooldownCollectionAndHealthKeepIndependentAccountStatus(t *testing
if err != nil {
t.Fatal(err)
}
if health.Counts.ModelCooldown != 1 || len(health.ModelCooldownCredentials) != 1 || len(health.CooldownCredentials) != 0 {
if health.Counts.Available != 2 || health.Groups[0].Counts.ModelCooldown != 1 || len(health.CooldownCredentials) != 0 {
t.Fatalf("health = %#v", health)
}
// 删除的展示不再保留对应汇总字段和明细响应。
for name, value := range map[string]any{
"credential_summary": collection.Summary,
"health_counts": health.Counts,
"health": health,
} {
payload, err := json.Marshal(value)
if err != nil {
t.Fatal(err)
}
var fields map[string]json.RawMessage
if err := json.Unmarshal(payload, &fields); err != nil {
t.Fatal(err)
}
for _, removed := range []string{"model_cooldown", "model_cooldown_credentials"} {
if _, exists := fields[removed]; exists {
t.Errorf("%s still exposes %s", name, removed)
}
}
}
}

func TestCredentialCollectionRejectsRemovedModelCooldownFilter(t *testing.T) {
if _, err := parseCredentialCollectionQuery("model_cooldown=true"); err == nil {
t.Fatal("removed model cooldown filter is still accepted")
}
}

func TestRuntimeHealthKeepsModelCooldownIdentityWhenGroupIsDisabled(t *testing.T) {
func TestRuntimeHealthCountsModelCooldownWhenGroupIsDisabled(t *testing.T) {
for _, connectionType := range []string{"api_key", "subscription"} {
t.Run(connectionType, func(t *testing.T) {
var fixture serviceFixture
Expand All @@ -122,7 +149,7 @@ func TestRuntimeHealthKeepsModelCooldownIdentityWhenGroupIsDisabled(t *testing.T
t.Fatal("model cooldown was rejected")
}
before, err := fixture.service.RuntimeHealth()
if err != nil || len(before.ModelCooldownCredentials) != 1 {
if err != nil || len(before.Groups) != 1 || before.Groups[0].Counts.ModelCooldown != 1 {
t.Fatalf("initial health = %#v, %v", before, err)
}
for _, enabled := range []bool{false, true} {
Expand All @@ -135,14 +162,9 @@ func TestRuntimeHealthKeepsModelCooldownIdentityWhenGroupIsDisabled(t *testing.T
if err != nil {
t.Fatalf("health with group enabled=%t: %v", enabled, err)
}
if got.Counts.ModelCooldown != 1 || len(got.ModelCooldownCredentials) != 1 {
if len(got.Groups) != 1 || got.Groups[0].Counts.ModelCooldown != 1 {
t.Fatalf("model cooldown disappeared after group enabled=%t: %#v", enabled, got)
}
detail := got.ModelCooldownCredentials[0]
if detail.CredentialID != credentialID || detail.GroupID != groupID ||
detail.Identity != before.ModelCooldownCredentials[0].Identity || len(detail.ModelCooldowns) != 1 {
t.Fatalf("model cooldown identity changed: %#v", detail)
}
if len(got.Groups) != 1 || got.Groups[0].Enabled != enabled || (got.Counts.Credentials > 0) != enabled {
t.Fatalf("group health changed: %#v", got)
}
Expand Down
5 changes: 0 additions & 5 deletions internal/control/runtime_observation.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ func (service *Service) captureRuntimeHealthObservation() (
problemCiphertexts := make(map[uint]string)
cooldownDetails := 0
blacklistedDetails := 0
modelCooldownDetails := 0
for _, key := range keys {
group, exists := snapshot.GroupCatalog[key.GroupID]
if !exists {
Expand All @@ -114,10 +113,6 @@ func (service *Service) captureRuntimeHealthObservation() (
blacklistedDetails++
needsIdentity = true
}
if hasModelCooldown(key.ModelCooldowns, observedAt) && modelCooldownDetails < healthProblemCredentialDetailLimit {
modelCooldownDetails++
needsIdentity = true
}
if !needsIdentity {
continue
}
Expand Down
2 changes: 1 addition & 1 deletion internal/control/wire_v3_contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestHealthAndRouteInspectionUseCredentialWireNames(t *testing.T) {
t.Fatalf("json.Marshal(health) error = %v", err)
}
for _, token := range []string{
`"counts":{"model_cooldown":0,"credentials":1,"available":1,"cooldown":0,"blacklisted":0}`,
`"counts":{"credentials":1,"available":1,"cooldown":0,"blacklisted":0}`,
`"cooldown_credentials":[{"credential_id":7`,
`"blacklisted_credentials":[]`,
} {
Expand Down
14 changes: 1 addition & 13 deletions web/src/api/control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,6 @@ export interface CredentialTestResultDto {
}

export interface CredentialSummaryDto {
model_cooldown: number
total: number
available: number
cooldown: number
Expand All @@ -370,7 +369,6 @@ export interface CredentialCollectionDto {
}

export interface CredentialCollectionFilters {
model_cooldown?: true
q?: string
status?: CredentialStatus
page: number
Expand Down Expand Up @@ -402,7 +400,6 @@ export interface CredentialCounts {
}

export interface HealthCredentialCountsDto {
model_cooldown: number
credentials: number
available: number
cooldown: number
Expand All @@ -413,7 +410,7 @@ export interface HealthGroupDto {
id: number
name: string
enabled: boolean
counts: HealthCredentialCountsDto
counts: HealthCredentialCountsDto & { model_cooldown: number }
}

export interface HealthRecoveryDto {
Expand Down Expand Up @@ -459,16 +456,7 @@ export interface RequestLogHealthDto {
last_retention_failure_at_ms: number | null
}

export interface HealthModelCooldownCredentialDto {
credential_id: number
group_id: number
group_name: string
identity: string
model_cooldowns: ModelCooldownDto[]
}

export interface RuntimeHealthDto {
model_cooldown_credentials: HealthModelCooldownCredentialDto[]
observed_at_ms: number
version: string
uptime_seconds: number
Expand Down
Loading