fac9578975
Poll /api/oauth/usage every 5 min and extract utilization from /v1/messages response headers for real-time updates. Track proxy tokens in/out per rate limit window (5h/7d), resetting on window change. Expose as OTel observable gauges for Grafana dashboards. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
63 lines
1.6 KiB
Go
63 lines
1.6 KiB
Go
package ratelimit
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
const usageURL = "https://api.anthropic.com/api/oauth/usage"
|
|
|
|
type RateLimit struct {
|
|
Utilization *float64 `json:"utilization"` // 0-100
|
|
ResetsAt *string `json:"resets_at"` // ISO 8601
|
|
}
|
|
|
|
type ExtraUsage struct {
|
|
IsEnabled bool `json:"is_enabled"`
|
|
MonthlyLimit *float64 `json:"monthly_limit"`
|
|
UsedCredits *float64 `json:"used_credits"`
|
|
Utilization *float64 `json:"utilization"`
|
|
}
|
|
|
|
type UsageResponse struct {
|
|
FiveHour *RateLimit `json:"five_hour"`
|
|
SevenDay *RateLimit `json:"seven_day"`
|
|
SevenDaySonnet *RateLimit `json:"seven_day_sonnet"`
|
|
ExtraUsage *ExtraUsage `json:"extra_usage"`
|
|
}
|
|
|
|
func fetchUsage(ctx context.Context, token string) (*UsageResponse, error) {
|
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
defer cancel()
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, usageURL, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build request: %w", err)
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("anthropic-beta", "oauth-2025-04-20")
|
|
req.Header.Set("User-Agent", "claude-cli/2.1.92")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, _ := io.ReadAll(resp.Body)
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("usage returned %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var usage UsageResponse
|
|
if err := json.Unmarshal(body, &usage); err != nil {
|
|
return nil, fmt.Errorf("decode: %w", err)
|
|
}
|
|
return &usage, nil
|
|
}
|