From cf2d677043767e19736b9cda96569bbdae59f7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 17 Mar 2026 16:32:03 +0800 Subject: [PATCH] ocm: emit status updates for plan-weight-only changes --- service/ocm/credential_external.go | 4 +++- service/ocm/credential_status_test.go | 33 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/service/ocm/credential_external.go b/service/ocm/credential_external.go index f4a3889f8..dd13aca60 100644 --- a/service/ocm/credential_external.go +++ b/service/ocm/credential_external.go @@ -518,7 +518,9 @@ func (c *externalCredential) updateStateFromHeaders(headers http.Header) { } c.logger.Debug("usage update for ", c.tag, ": 5h=", c.state.fiveHourUtilization, "%, weekly=", c.state.weeklyUtilization, "%", resetSuffix) } - shouldEmit := hadData && (c.state.fiveHourUtilization != oldFiveHour || c.state.weeklyUtilization != oldWeekly || c.state.remotePlanWeight != oldPlanWeight) + utilizationChanged := c.state.fiveHourUtilization != oldFiveHour || c.state.weeklyUtilization != oldWeekly + planWeightChanged := c.state.remotePlanWeight != oldPlanWeight + shouldEmit := (hadData && utilizationChanged) || planWeightChanged shouldInterrupt := c.checkTransitionLocked() c.stateAccess.Unlock() if shouldInterrupt { diff --git a/service/ocm/credential_status_test.go b/service/ocm/credential_status_test.go index d45fdebf0..955338fce 100644 --- a/service/ocm/credential_status_test.go +++ b/service/ocm/credential_status_test.go @@ -195,6 +195,39 @@ func TestExternalCredentialConnectStatusStreamMultiFrameKeepsLastUpdated(t *test } } +func TestExternalCredentialPlanWeightOnlyRateLimitsEventEmitsStatus(t *testing.T) { + subscriber := observable.NewSubscriber[struct{}](8) + subscription, _ := subscriber.Subscription() + credential := &externalCredential{ + tag: "test", + logger: newTestLogger(), + statusSubscriber: subscriber, + } + credential.stateAccess.Lock() + credential.state.remotePlanWeight = 2 + oldTime := time.Unix(123, 0) + credential.state.lastUpdated = oldTime + credential.stateAccess.Unlock() + + (&Service{}).handleWebSocketRateLimitsEvent([]byte(`{"plan_weight":3}`), credential) + + if weight := credential.planWeight(); weight != 3 { + t.Fatalf("expected plan weight 3, got %v", weight) + } + if count := drainStatusEvents(subscription); count != 1 { + t.Fatalf("expected 1 status event, got %d", count) + } + if !credential.lastUpdatedTime().Equal(oldTime) { + t.Fatalf("expected lastUpdated to stay %v, got %v", oldTime, credential.lastUpdatedTime()) + } + + (&Service{}).handleWebSocketRateLimitsEvent([]byte(`{"plan_weight":3}`), credential) + + if count := drainStatusEvents(subscription); count != 0 { + t.Fatalf("expected no status event for unchanged plan weight, got %d", count) + } +} + func TestDefaultCredentialAvailabilityChangesEmitStatus(t *testing.T) { credentialPath := filepath.Join(t.TempDir(), "auth.json") err := os.WriteFile(credentialPath, []byte("{\"OPENAI_API_KEY\":\"sk-test\"}\n"), 0o600)