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
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ else
CODESIGN_FLAGS := --force --deep --options runtime --timestamp --sign "$(SIGN_IDENTITY)" $(CODESIGN_EXTRA_FLAGS)
endif

.PHONY: build run probe test-rate-limits test-statistics-time-zone test-token-counter test-model-usage-trend test-app-server-pipe test-task-runtime test-leadership-model test-leadership-assets test-claude-skill-paths test-codex-session-link test-performance-monitor test-phase-one-gate test-particle-animation test-palettes test-macos-compatibility memory-risk-check phase-one-check phase-one-soak install dmg dmg-arm64 dmg-intel checksum checksum-arm64 checksum-intel release release-arm64 release-intel release-all release-package release-check notarize verify clean clean-dist
.PHONY: build run probe test-rate-limits test-statistics-time-zone test-token-counter test-model-pricing test-model-usage-trend test-app-server-pipe test-task-runtime test-leadership-model test-leadership-assets test-claude-skill-paths test-codex-session-link test-performance-monitor test-phase-one-gate test-particle-animation test-palettes test-macos-compatibility memory-risk-check phase-one-check phase-one-soak install dmg dmg-arm64 dmg-intel checksum checksum-arm64 checksum-intel release release-arm64 release-intel release-all release-package release-check notarize verify clean clean-dist

build: test-leadership-assets
rm -rf "$(APP_DIR)"
Expand Down Expand Up @@ -67,6 +67,9 @@ test-statistics-time-zone:
test-token-counter: build
"$(MACOS_DIR)/$(APP_NAME)" --self-test-token-counter

test-model-pricing: build
"$(MACOS_DIR)/$(APP_NAME)" --self-test-model-pricing

test-model-usage-trend: build
"$(MACOS_DIR)/$(APP_NAME)" --self-test-model-usage-trend

Expand Down
2 changes: 1 addition & 1 deletion Sources/CodexUsageWidget/Domain/ModelUsageTrend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ enum ModelUsageTrendSelfTest {
sourceQuality: .detailed
)
expect(catalogPriced.first?.usesReferencePricing == false, "catalog-priced models should not be marked as reference pricing")
expect(modelUsageUsesReferencePricing("gpt-5.6-luna"), "unknown models should use the documented reference price")
expect(!modelUsageUsesReferencePricing("gpt-5.6-luna"), "known GPT-5.6 models should use their explicit price")
expect(!modelUsageUsesReferencePricing("gpt-5.5"), "catalog-priced models should retain their explicit price basis")

let unsupportedTrend = UsageTrend(
Expand Down
55 changes: 54 additions & 1 deletion Sources/CodexUsageWidget/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ final class UsageStore: ObservableObject {

final class CodexUsageReader {
private let fileManager = FileManager.default
private let localAnalyticsCacheVersion = 11
private let localAnalyticsCacheVersion = 12
private let sessionUsageCacheVersion = 8
private static let memorySessionUsageCacheLimit = 64
private static let persistentSessionUsageCacheLimit = 1_024
Expand Down Expand Up @@ -3365,6 +3365,15 @@ func estimateStaticTokens(_ text: String) -> Int64 {
private func modelTokenPrice(for model: String?) -> ModelTokenPrice {
let normalized = (model ?? "").lowercased()

if normalized.contains("gpt-5.6-sol") || normalized == "gpt-5.6" {
return ModelTokenPrice(model: "gpt-5.6-sol", inputPerMillion: 5, cachedInputPerMillion: 0.5, outputPerMillion: 30, usesReferencePricing: false)
}
if normalized.contains("gpt-5.6-terra") {
return ModelTokenPrice(model: "gpt-5.6-terra", inputPerMillion: 2, cachedInputPerMillion: 0.2, outputPerMillion: 12, usesReferencePricing: false)
}
if normalized.contains("gpt-5.6-luna") {
return ModelTokenPrice(model: "gpt-5.6-luna", inputPerMillion: 0.2, cachedInputPerMillion: 0.02, outputPerMillion: 1.2, usesReferencePricing: false)
}
if normalized.contains("gpt-5.5-pro") {
return ModelTokenPrice(model: "gpt-5.5-pro", inputPerMillion: 30, cachedInputPerMillion: 30, outputPerMillion: 180, usesReferencePricing: false)
}
Expand Down Expand Up @@ -3425,6 +3434,46 @@ private func estimatedCostUSD(tokens: TokenBreakdown, price: ModelTokenPrice) ->
return uncachedInputCost + cachedInputCost + outputCost
}

private enum ModelPricingSelfTest {
static func run() -> Bool {
var failures: [String] = []
func expect(_ condition: @autoclosure () -> Bool, _ message: String) {
if !condition() { failures.append(message) }
}
func nearlyEqual(_ lhs: Double, _ rhs: Double) -> Bool {
abs(lhs - rhs) < 0.000_001
}

let sampleTokens = TokenBreakdown(
inputTokens: 1_000_000,
cachedInputTokens: 400_000,
outputTokens: 100_000,
reasoningOutputTokens: 0,
totalTokens: 1_100_000
)
let sol = modelTokenPrice(for: "gpt-5.6")
let terra = modelTokenPrice(for: "gpt-5.6-terra-2026-02-16")
let luna = modelTokenPrice(for: "GPT-5.6-LUNA")

expect(sol.model == "gpt-5.6-sol", "gpt-5.6 should resolve to gpt-5.6-sol")
expect(!sol.usesReferencePricing, "gpt-5.6 should use an explicit price")
expect(terra.model == "gpt-5.6-terra", "terra snapshots should preserve the terra price")
expect(luna.model == "gpt-5.6-luna", "luna matching should be case-insensitive")
expect(nearlyEqual(estimatedCostUSD(tokens: sampleTokens, price: sol), 6.2), "Sol cached input estimate should use the split rates")
expect(nearlyEqual(estimatedCostUSD(tokens: sampleTokens, price: terra), 2.48), "Terra should use the official standard API rates")
expect(nearlyEqual(estimatedCostUSD(tokens: sampleTokens, price: luna), 0.248), "Luna should use the official standard API rates")
expect(!modelUsageUsesReferencePricing("gpt-5.6-luna"), "known GPT-5.6 models should not use reference pricing")
expect(modelUsageUsesReferencePricing("future-model"), "unknown models should retain reference pricing")

if failures.isEmpty {
print("model pricing self-test passed")
return true
}
failures.forEach { print("model pricing self-test failed: \($0)") }
return false
}
}

private func parseSimpleTOML(_ text: String) -> [String: String] {
var fields: [String: String] = [:]

Expand Down Expand Up @@ -11632,6 +11681,10 @@ struct codexUMain {
exit(CodexTokenCounterNormalizerSelfTest.run() ? 0 : 1)
}

if CommandLine.arguments.contains("--self-test-model-pricing") {
exit(ModelPricingSelfTest.run() ? 0 : 1)
}

if CommandLine.arguments.contains("--self-test-model-usage-trend") {
exit(ModelUsageTrendSelfTest.run() ? 0 : 1)
}
Expand Down
Loading