mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-30 00:21:34 +00:00
feat(openai): add GPT-6 Astra support
This commit is contained in:
@@ -19,6 +19,8 @@ type Model struct {
|
||||
// DefaultModels OpenAI models list
|
||||
var DefaultModels = []Model{
|
||||
{ID: "gpt-5.6-sol", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 Sol"},
|
||||
{ID: "gpt-6-astra", Object: "model", Created: 1788480000, OwnedBy: "openai", Type: "model", DisplayName: "GPT-6 Astra"},
|
||||
{ID: "gpt-6", Object: "model", Created: 1788480000, OwnedBy: "openai", Type: "model", DisplayName: "GPT-6 (Astra)"},
|
||||
{ID: "gpt-5.6", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 (Sol)"},
|
||||
{ID: "gpt-5.6-terra", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 Terra"},
|
||||
{ID: "gpt-5.6-luna", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 Luna"},
|
||||
|
||||
@@ -10,6 +10,11 @@ func TestDefaultModelsIncludeBareGPT56Alias(t *testing.T) {
|
||||
require.Contains(t, DefaultModelIDs(), "gpt-5.6")
|
||||
}
|
||||
|
||||
func TestDefaultModelsIncludeGPT6Astra(t *testing.T) {
|
||||
require.Contains(t, DefaultModelIDs(), "gpt-6-astra")
|
||||
require.Contains(t, DefaultModelIDs(), "gpt-6")
|
||||
}
|
||||
|
||||
func TestDefaultModelsPreferConcreteGPT56SolForAccountTests(t *testing.T) {
|
||||
require.NotEmpty(t, DefaultModels)
|
||||
require.Equal(t, "gpt-5.6-sol", DefaultModels[0].ID)
|
||||
|
||||
@@ -431,6 +431,20 @@ func (s *BillingService) initFallbackPricing() {
|
||||
SupportsCacheBreakdown: false,
|
||||
}
|
||||
|
||||
s.fallbackPrices["gpt-6-astra"] = &ModelPricing{
|
||||
InputPricePerToken: 10e-6,
|
||||
InputPricePerTokenPriority: 20e-6,
|
||||
OutputPricePerToken: 50e-6,
|
||||
OutputPricePerTokenPriority: 100e-6,
|
||||
CacheCreationPricePerToken: 12.5e-6,
|
||||
CacheCreationPricePerTokenPriority: 25e-6,
|
||||
CacheReadPricePerToken: 1e-6,
|
||||
CacheReadPricePerTokenPriority: 2e-6,
|
||||
LongContextInputThreshold: 272_000,
|
||||
LongContextInputMultiplier: 2,
|
||||
LongContextOutputMultiplier: 1.5,
|
||||
}
|
||||
|
||||
// OpenAI GPT-5.6 官方价格(USD/token)。缓存写入为输入价的 1.25 倍。
|
||||
s.fallbackPrices["gpt-5.6-sol"] = &ModelPricing{
|
||||
InputPricePerToken: 5e-6,
|
||||
@@ -976,6 +990,8 @@ func (s *BillingService) getFallbackPricing(model string) *ModelPricing {
|
||||
// OpenAI(GPT-5 / Codex 族):仅匹配已知型号,避免未知 OpenAI 型号误计价。
|
||||
if normalized := normalizeKnownOpenAICodexModel(modelLower); normalized != "" {
|
||||
switch normalized {
|
||||
case "gpt-6-astra":
|
||||
return s.fallbackPrices["gpt-6-astra"]
|
||||
case "gpt-5.6-sol":
|
||||
return s.fallbackPrices["gpt-5.6-sol"]
|
||||
case "gpt-5.6-terra":
|
||||
@@ -1653,7 +1669,7 @@ func (s *BillingService) applyModelSpecificPricingPolicyEx(model string, pricing
|
||||
// 档的模型(如 gpt-5.5-pro、gpt-5.4-mini/nano)返回 0。
|
||||
func openAIModelFastPricingRatio(normalized string) float64 {
|
||||
switch normalized {
|
||||
case "gpt-5.4", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna":
|
||||
case "gpt-6-astra", "gpt-5.4", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna":
|
||||
return 2.0
|
||||
case "gpt-5.5":
|
||||
return 2.5
|
||||
|
||||
@@ -311,6 +311,7 @@ const (
|
||||
configuredCodexGrokContext = 500_000
|
||||
configuredCodexGrokBuildContext = 256_000
|
||||
configuredCodexGPT56MaxContext = 872_000
|
||||
configuredCodexGPT6AstraContext = 1_050_000
|
||||
configuredCodexToolOutputMaxTokens = 10_000
|
||||
)
|
||||
|
||||
@@ -490,6 +491,10 @@ func newConfiguredCodexModelDescriptor(modelID string) configuredCodexModelDescr
|
||||
if isOpenAIGPT56Model(modelID) {
|
||||
descriptor.MaxContextWindow = configuredCodexGPT56MaxContext
|
||||
}
|
||||
if isOpenAIGPT6AstraModel(modelID) {
|
||||
descriptor.ContextWindow = configuredCodexGPT6AstraContext
|
||||
descriptor.MaxContextWindow = configuredCodexGPT6AstraContext
|
||||
}
|
||||
}
|
||||
if SupportsVerbosity(modelID) {
|
||||
defaultVerbosity := "low"
|
||||
@@ -503,6 +508,9 @@ func newConfiguredCodexModelDescriptor(modelID string) configuredCodexModelDescr
|
||||
|
||||
func configuredCodexSupportsPriorityServiceTier(modelID string) bool {
|
||||
normalized := canonicalizeOpenAIModelAliasSpelling(modelID)
|
||||
if isOpenAIGPT6AstraModel(normalized) {
|
||||
return true
|
||||
}
|
||||
for _, family := range []string{"gpt-5.4", "gpt-5.5", "gpt-5.6"} {
|
||||
if normalized == family || strings.HasPrefix(normalized, family+"-") {
|
||||
return true
|
||||
@@ -567,7 +575,7 @@ func configuredCodexGPTReasoningLevels(modelID string) []configuredCodexReasonin
|
||||
{Effort: "xhigh", Description: "Extra-high reasoning depth for difficult tasks"},
|
||||
}
|
||||
normalized := getNormalizedCodexModel(modelID)
|
||||
if isOpenAIGPT56Model(modelID) {
|
||||
if isOpenAIGPT6AstraModel(modelID) || isOpenAIGPT56Model(modelID) {
|
||||
levels = append(levels, configuredCodexReasoningLevel{
|
||||
Effort: "max",
|
||||
Description: "Maximum reasoning depth for complex tasks",
|
||||
@@ -592,12 +600,13 @@ func isOpenAICodexGPTModel(modelID string) bool {
|
||||
|
||||
func isOpenAICodexReasoningGPTModel(modelID string) bool {
|
||||
normalized := canonicalizeOpenAIModelAliasSpelling(modelID)
|
||||
return strings.HasPrefix(normalized, "gpt-5")
|
||||
return isOpenAIGPT6AstraModel(normalized) || strings.HasPrefix(normalized, "gpt-5")
|
||||
}
|
||||
|
||||
func isOpenAICodexImageInputModel(modelID string) bool {
|
||||
normalized := canonicalizeOpenAIModelAliasSpelling(modelID)
|
||||
return strings.HasPrefix(normalized, "gpt-5") ||
|
||||
return isOpenAIGPT6AstraModel(normalized) ||
|
||||
strings.HasPrefix(normalized, "gpt-5") ||
|
||||
strings.HasPrefix(normalized, "gpt-4o") ||
|
||||
strings.HasPrefix(normalized, "gpt-4.1") ||
|
||||
strings.HasPrefix(normalized, "gpt-4.5") ||
|
||||
@@ -1834,6 +1843,7 @@ func CodexModelsManifestETag(body []byte) string {
|
||||
}
|
||||
|
||||
var apiKeyCodexModelsWithoutResponsesLite = map[string]struct{}{
|
||||
"gpt-6-astra": {},
|
||||
"gpt-5.6-sol": {},
|
||||
"gpt-5.6-terra": {},
|
||||
"gpt-5.6-luna": {},
|
||||
|
||||
@@ -349,6 +349,25 @@ func TestNewConfiguredCodexModelDescriptorUsesProviderMetadataAndSafeFallback(t
|
||||
require.Equal(t, []string{"low", "medium", "high", "xhigh", "max"}, effortsFromConfiguredCodexLevels(gpt56Luna.SupportedReasoningLevels))
|
||||
require.Equal(t, "medium", *gpt56Luna.DefaultReasoningLevel)
|
||||
|
||||
astra := newConfiguredCodexModelDescriptor("gpt-6-astra")
|
||||
require.Equal(t, "GPT-6 Astra", astra.DisplayName)
|
||||
require.NotNil(t, astra.DefaultReasoningLevel)
|
||||
require.Equal(t, "medium", *astra.DefaultReasoningLevel)
|
||||
require.Equal(t, []string{"low", "medium", "high", "xhigh", "max"}, effortsFromConfiguredCodexLevels(astra.SupportedReasoningLevels))
|
||||
require.NotContains(t, astra.SupportedReasoningLevels, configuredCodexReasoningLevel{Effort: "none"})
|
||||
require.NotContains(t, astra.SupportedReasoningLevels, configuredCodexReasoningLevel{Effort: "minimal"})
|
||||
require.Len(t, astra.ServiceTiers, 1)
|
||||
require.Equal(t, "priority", astra.ServiceTiers[0].ID)
|
||||
require.True(t, astra.SupportsParallelToolCalls)
|
||||
require.True(t, astra.SupportVerbosity)
|
||||
require.Equal(t, int64(1_050_000), astra.ContextWindow)
|
||||
require.Equal(t, int64(1_050_000), astra.MaxContextWindow)
|
||||
require.Equal(t, configuredCodexTruncationPolicy{Mode: "tokens", Limit: 10_000}, astra.TruncationPolicy)
|
||||
gpt6 := newConfiguredCodexModelDescriptor("gpt-6")
|
||||
require.Equal(t, "GPT-6 (Astra)", gpt6.DisplayName)
|
||||
require.Equal(t, []string{"low", "medium", "high", "xhigh", "max"}, effortsFromConfiguredCodexLevels(gpt6.SupportedReasoningLevels))
|
||||
require.Equal(t, int64(1_050_000), gpt6.ContextWindow)
|
||||
|
||||
gpt55 := newConfiguredCodexModelDescriptor("gpt-5.5")
|
||||
require.Equal(t, "GPT-5.5", gpt55.DisplayName)
|
||||
require.NotNil(t, gpt55.DefaultReasoningLevel)
|
||||
@@ -2129,12 +2148,12 @@ func TestCompleteAPIKeyCodexModelsManifestForClientMarksOnlyOfficialVisionGPTIma
|
||||
t.Parallel()
|
||||
|
||||
svc := &OpenAIGatewayService{}
|
||||
manifest := &CodexModelsManifest{Body: []byte(`{"models":[{"slug":"gpt-5.6-sol"},{"slug":"gpt-4o"},{"slug":"gpt-3.5-turbo"},{"slug":"gpt-4"}]}`)}
|
||||
manifest := &CodexModelsManifest{Body: []byte(`{"models":[{"slug":"gpt-6-astra"},{"slug":"gpt-5.6-sol"},{"slug":"gpt-4o"},{"slug":"gpt-3.5-turbo"},{"slug":"gpt-4"}]}`)}
|
||||
account := newCodexModelsAPIKeyTestAccount("")
|
||||
|
||||
require.NoError(t, svc.CompleteAPIKeyCodexModelsManifestForClient(manifest, account))
|
||||
models := decodeCodexManifestModels(t, manifest.Body)
|
||||
require.Len(t, models, 4)
|
||||
require.Len(t, models, 5)
|
||||
|
||||
bySlug := make(map[string]map[string]any, len(models))
|
||||
for _, model := range models {
|
||||
@@ -2142,7 +2161,7 @@ func TestCompleteAPIKeyCodexModelsManifestForClientMarksOnlyOfficialVisionGPTIma
|
||||
require.True(t, ok)
|
||||
bySlug[slug] = model
|
||||
}
|
||||
for _, slug := range []string{"gpt-5.6-sol", "gpt-4o"} {
|
||||
for _, slug := range []string{"gpt-6-astra", "gpt-5.6-sol", "gpt-4o"} {
|
||||
require.Equal(t, []any{"text", "image"}, bySlug[slug]["input_modalities"])
|
||||
require.Equal(t, true, bySlug[slug]["supports_image_detail_original"])
|
||||
}
|
||||
@@ -2173,8 +2192,8 @@ func TestAdjustAPIKeyCodexModelsManifest(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "affected models disable responses lite and preserve unknown fields",
|
||||
body: `{"models":[{"slug":"gpt-5.6-sol","use_responses_lite":true,"unknown_model":{"enabled":true}},{"slug":"gpt-5.6-terra","use_responses_lite":true},{"slug":"gpt-5.6-luna","use_responses_lite":true}],"unknown_top":{"version":1}}`,
|
||||
want: `{"models":[{"slug":"gpt-5.6-sol","unknown_model":{"enabled":true},"use_responses_lite":false},{"slug":"gpt-5.6-terra","use_responses_lite":false},{"slug":"gpt-5.6-luna","use_responses_lite":false}],"unknown_top":{"version":1}}`,
|
||||
body: `{"models":[{"slug":"gpt-6-astra","use_responses_lite":true},{"slug":"gpt-5.6-sol","use_responses_lite":true,"unknown_model":{"enabled":true}},{"slug":"gpt-5.6-terra","use_responses_lite":true},{"slug":"gpt-5.6-luna","use_responses_lite":true}],"unknown_top":{"version":1}}`,
|
||||
want: `{"models":[{"slug":"gpt-6-astra","use_responses_lite":false},{"slug":"gpt-5.6-sol","unknown_model":{"enabled":true},"use_responses_lite":false},{"slug":"gpt-5.6-terra","use_responses_lite":false},{"slug":"gpt-5.6-luna","use_responses_lite":false}],"unknown_top":{"version":1}}`,
|
||||
},
|
||||
{
|
||||
name: "unaffected model unchanged",
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var codexModelMap = map[string]string{
|
||||
"gpt-6-astra": "gpt-6-astra",
|
||||
"gpt-5.6-sol": "gpt-5.6-sol",
|
||||
"gpt-5.6-terra": "gpt-5.6-terra",
|
||||
"gpt-5.6-luna": "gpt-5.6-luna",
|
||||
|
||||
@@ -1361,6 +1361,10 @@ func TestApplyCodexOAuthTransform_EmptyInput(t *testing.T) {
|
||||
|
||||
func TestNormalizeCodexModel_Gpt53(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"gpt-6-astra": "gpt-6-astra",
|
||||
"openai/gpt-6-astra": "gpt-6-astra",
|
||||
"gpt-6": "gpt-6-astra",
|
||||
"openai/gpt-6": "gpt-6-astra",
|
||||
"gpt-5.4": "gpt-5.4",
|
||||
"gpt5.5": "gpt-5.5",
|
||||
"openai/gpt5.5": "gpt-5.5",
|
||||
|
||||
@@ -2162,7 +2162,7 @@ func normalizeOpenAIReasoningEffortForModel(raw, model string) string {
|
||||
// supportsOpenAIReasoningEffortMax reports model families whose upstream scale
|
||||
// has a distinct max level. Other models keep the legacy max -> xhigh behavior.
|
||||
func supportsOpenAIReasoningEffortMax(model string) bool {
|
||||
if isOpenAIGPT56Model(model) {
|
||||
if isOpenAIGPT6AstraModel(model) || isOpenAIGPT56Model(model) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,14 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func TestNormalizeOpenAIReasoningEffortForGPT56(t *testing.T) {
|
||||
func TestNormalizeOpenAIReasoningEffortForMaxCapableModels(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
raw string
|
||||
model string
|
||||
want string
|
||||
}{
|
||||
{name: "Astra 保留 max", raw: "max", model: "gpt-6-astra", want: "max"},
|
||||
{name: "Sol 保留 max", raw: "max", model: "gpt-5.6-sol", want: "max"},
|
||||
{name: "Terra 保留 max", raw: "max", model: "openai/gpt-5.6-terra", want: "max"},
|
||||
{name: "Luna 后缀保留 max", raw: "max", model: "gpt-5.6-luna-2026-07-09", want: "max"},
|
||||
|
||||
@@ -65,6 +65,8 @@ func normalizeKnownOpenAICodexModel(model string) string {
|
||||
}
|
||||
|
||||
switch {
|
||||
case isOpenAIGPT6AstraModel(normalized):
|
||||
return "gpt-6-astra"
|
||||
case strings.Contains(normalized, "gpt-5.6-sol"):
|
||||
return "gpt-5.6-sol"
|
||||
case strings.Contains(normalized, "gpt-5.6-terra"):
|
||||
@@ -106,6 +108,11 @@ func normalizeKnownOpenAICodexModel(model string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func isOpenAIGPT6AstraModel(model string) bool {
|
||||
normalized := canonicalizeOpenAIModelAliasSpelling(model)
|
||||
return normalized == "gpt-6" || normalized == "gpt-6-astra"
|
||||
}
|
||||
|
||||
// isOpenAIGPT56Model 判断是否 GPT-5.6 系列模型;入参可为原始模型名
|
||||
// (含大小写/路径/后缀变体)或已归一化的基名,两者均能正确识别。
|
||||
func isOpenAIGPT56Model(model string) bool {
|
||||
|
||||
@@ -6,6 +6,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNormalizeKnownOpenAICodexModelGPT6Astra(t *testing.T) {
|
||||
for _, model := range []string{"gpt-6-astra", "openai/gpt-6-astra", "gpt-6", "openai/gpt-6"} {
|
||||
require.Equal(t, "gpt-6-astra", normalizeKnownOpenAICodexModel(model))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeKnownOpenAICodexModel_BareGPT56RoutesToSol(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"gpt-5.6": "gpt-5.6-sol",
|
||||
|
||||
@@ -44,6 +44,23 @@ var (
|
||||
Mode: "chat",
|
||||
SupportsPromptCaching: true,
|
||||
}
|
||||
openAIGPT6AstraFallbackPricing = &LiteLLMModelPricing{
|
||||
InputCostPerToken: 1e-05,
|
||||
InputCostPerTokenPriority: 2e-05,
|
||||
OutputCostPerToken: 5e-05,
|
||||
OutputCostPerTokenPriority: 1e-04,
|
||||
CacheCreationInputTokenCost: 1.25e-05,
|
||||
CacheCreationInputTokenCostPriority: 2.5e-05,
|
||||
CacheReadInputTokenCost: 1e-06,
|
||||
CacheReadInputTokenCostPriority: 2e-06,
|
||||
LongContextInputTokenThreshold: 272_000,
|
||||
LongContextInputCostMultiplier: 2,
|
||||
LongContextOutputCostMultiplier: 1.5,
|
||||
SupportsServiceTier: true,
|
||||
LiteLLMProvider: "openai",
|
||||
Mode: "chat",
|
||||
SupportsPromptCaching: true,
|
||||
}
|
||||
openAIGPT56SolFallbackPricing = &LiteLLMModelPricing{
|
||||
InputCostPerToken: 5e-06,
|
||||
InputCostPerTokenPriority: 1e-05,
|
||||
@@ -1100,6 +1117,9 @@ func normalizeModelNameForPricing(model string) string {
|
||||
|
||||
model = strings.TrimLeft(model, "/")
|
||||
if canonical := canonicalizeOpenAIModelAliasSpelling(model); canonical != "" {
|
||||
if canonical == "gpt-6" {
|
||||
return "gpt-6-astra"
|
||||
}
|
||||
if canonical == "gpt-5.6" {
|
||||
return "gpt-5.6-sol"
|
||||
}
|
||||
@@ -1300,6 +1320,12 @@ func (s *PricingService) matchOpenAIModel(model string) *LiteLLMModelPricing {
|
||||
}
|
||||
}
|
||||
|
||||
if isOpenAIGPT6AstraModel(model) {
|
||||
logger.With(zap.String("component", "service.pricing")).
|
||||
Info(fmt.Sprintf("[Pricing] OpenAI fallback matched %s -> %s", model, "gpt-6-astra(static)"))
|
||||
return openAIGPT6AstraFallbackPricing
|
||||
}
|
||||
|
||||
if strings.HasPrefix(model, "gpt-5.6-sol") {
|
||||
logger.With(zap.String("component", "service.pricing")).
|
||||
Info(fmt.Sprintf("[Pricing] OpenAI fallback matched %s -> %s", model, "gpt-5.6-sol(static)"))
|
||||
|
||||
@@ -77,6 +77,95 @@ func TestParsePricingData_ParsesPriorityAndServiceTierFields(t *testing.T) {
|
||||
require.True(t, pricing.SupportsServiceTier)
|
||||
}
|
||||
|
||||
const gpt6AstraCatalogJSON = `{
|
||||
"gpt-6-astra": {
|
||||
"litellm_provider": "openai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 1e-05,
|
||||
"input_cost_per_token_priority": 2e-05,
|
||||
"output_cost_per_token": 5e-05,
|
||||
"output_cost_per_token_priority": 1e-04,
|
||||
"cache_creation_input_token_cost": 1.25e-05,
|
||||
"cache_creation_input_token_cost_priority": 2.5e-05,
|
||||
"cache_read_input_token_cost": 1e-06,
|
||||
"cache_read_input_token_cost_priority": 2e-06,
|
||||
"input_cost_per_token_above_272k_tokens": 2e-05,
|
||||
"output_cost_per_token_above_272k_tokens": 7.5e-05,
|
||||
"cache_creation_input_token_cost_above_272k_tokens": 2.5e-05,
|
||||
"cache_read_input_token_cost_above_272k_tokens": 2e-06
|
||||
}
|
||||
}`
|
||||
|
||||
func TestBillingServiceGPT6AstraUsesOfficialPricingAcrossTiersAndLongContext(t *testing.T) {
|
||||
svc := NewBillingService(&config.Config{}, newStubPricingServiceFromJSON(t, gpt6AstraCatalogJSON))
|
||||
boundaryTokens := UsageTokens{InputTokens: 100_000, CacheCreationTokens: 100_000, CacheReadTokens: 72_000, OutputTokens: 10}
|
||||
boundary, err := svc.CalculateCost("gpt-6-astra", boundaryTokens, 1)
|
||||
require.NoError(t, err)
|
||||
require.False(t, boundary.LongContextBillingApplied)
|
||||
require.InDelta(t, 100_000*10e-6, boundary.InputCost, 1e-12)
|
||||
require.InDelta(t, 100_000*12.5e-6, boundary.CacheCreationCost, 1e-12)
|
||||
require.InDelta(t, 72_000*1e-6, boundary.CacheReadCost, 1e-12)
|
||||
require.InDelta(t, 10*50e-6, boundary.OutputCost, 1e-12)
|
||||
|
||||
tokens := UsageTokens{InputTokens: 100_000, CacheCreationTokens: 100_000, CacheReadTokens: 73_000, OutputTokens: 10}
|
||||
tiers := []struct {
|
||||
name string
|
||||
serviceTier string
|
||||
priceScale float64
|
||||
}{
|
||||
{name: "standard", priceScale: 1},
|
||||
{name: "fast", serviceTier: "priority", priceScale: 2},
|
||||
{name: "flex", serviceTier: "flex", priceScale: 0.5},
|
||||
}
|
||||
for _, tier := range tiers {
|
||||
t.Run(tier.name, func(t *testing.T) {
|
||||
cost, err := svc.CalculateCostWithServiceTier("gpt-6-astra", tokens, 1, tier.serviceTier)
|
||||
require.NoError(t, err)
|
||||
require.True(t, cost.LongContextBillingApplied)
|
||||
require.InDelta(t, 100_000*10e-6*tier.priceScale*2, cost.InputCost, 1e-12)
|
||||
require.InDelta(t, 100_000*12.5e-6*tier.priceScale*2, cost.CacheCreationCost, 1e-12)
|
||||
require.InDelta(t, 73_000*1e-6*tier.priceScale*2, cost.CacheReadCost, 1e-12)
|
||||
require.InDelta(t, 10*50e-6*tier.priceScale*1.5, cost.OutputCost, 1e-12)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGPT6AstraDedicatedFallbacksUseOfficialRates(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
svc *BillingService
|
||||
}{
|
||||
{name: "pricing_service", svc: NewBillingService(&config.Config{}, &PricingService{pricingData: map[string]*LiteLLMModelPricing{}})},
|
||||
{name: "billing_service", svc: NewBillingService(&config.Config{}, nil)},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pricing, err := tt.svc.GetModelPricing("gpt-6-astra")
|
||||
require.NoError(t, err)
|
||||
require.InDelta(t, 10e-6, pricing.InputPricePerToken, 1e-12)
|
||||
require.InDelta(t, 20e-6, pricing.InputPricePerTokenPriority, 1e-12)
|
||||
require.InDelta(t, 50e-6, pricing.OutputPricePerToken, 1e-12)
|
||||
require.InDelta(t, 100e-6, pricing.OutputPricePerTokenPriority, 1e-12)
|
||||
require.InDelta(t, 12.5e-6, pricing.CacheCreationPricePerToken, 1e-12)
|
||||
require.InDelta(t, 25e-6, pricing.CacheCreationPricePerTokenPriority, 1e-12)
|
||||
require.InDelta(t, 1e-6, pricing.CacheReadPricePerToken, 1e-12)
|
||||
require.InDelta(t, 2e-6, pricing.CacheReadPricePerTokenPriority, 1e-12)
|
||||
require.Equal(t, 272_000, pricing.LongContextInputThreshold)
|
||||
require.InDelta(t, 2.0, pricing.LongContextInputMultiplier, 1e-12)
|
||||
require.InDelta(t, 1.5, pricing.LongContextOutputMultiplier, 1e-12)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPricingServiceBareGPT6AliasUsesAstra(t *testing.T) {
|
||||
astraPricing := &LiteLLMModelPricing{InputCostPerToken: 123e-6, OutputCostPerToken: 456e-6}
|
||||
pricingSvc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{"gpt-6-astra": astraPricing}}
|
||||
for _, model := range []string{"gpt-6", "openai/gpt-6"} {
|
||||
pricing := pricingSvc.GetModelPricing(model)
|
||||
require.Same(t, astraPricing, pricing)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBillingService_GPT56CacheWritePricingUsesOfficialMultiplier(t *testing.T) {
|
||||
tests := []struct {
|
||||
model string
|
||||
|
||||
Reference in New Issue
Block a user