132 lines
4.2 KiB
Go
132 lines
4.2 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"chat-switchboard/models"
|
|
)
|
|
|
|
type PricingStore struct{}
|
|
|
|
func NewPricingStore() *PricingStore { return &PricingStore{} }
|
|
|
|
// GetForModel returns the pricing entry for a specific provider+model pair.
|
|
func (s *PricingStore) GetForModel(ctx context.Context, providerConfigID, modelID string) (*models.PricingEntry, error) {
|
|
var p models.PricingEntry
|
|
err := DB.QueryRowContext(ctx, `
|
|
SELECT id, provider_config_id, model_id,
|
|
input_per_m, output_per_m, cache_create_per_m, cache_read_per_m,
|
|
currency, source, updated_at, updated_by
|
|
FROM model_pricing
|
|
WHERE provider_config_id = $1 AND model_id = $2
|
|
`, providerConfigID, modelID).Scan(
|
|
&p.ID, &p.ProviderConfigID, &p.ModelID,
|
|
&p.InputPerM, &p.OutputPerM, &p.CacheCreatePerM, &p.CacheReadPerM,
|
|
&p.Currency, &p.Source, &p.UpdatedAt, &p.UpdatedBy,
|
|
)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
return &p, err
|
|
}
|
|
|
|
// Upsert inserts or updates a pricing entry (manual admin override).
|
|
func (s *PricingStore) Upsert(ctx context.Context, entry *models.PricingEntry) error {
|
|
_, err := DB.ExecContext(ctx, `
|
|
INSERT INTO model_pricing (
|
|
provider_config_id, model_id,
|
|
input_per_m, output_per_m, cache_create_per_m, cache_read_per_m,
|
|
currency, source, updated_by, updated_at
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW())
|
|
ON CONFLICT (provider_config_id, model_id)
|
|
DO UPDATE SET
|
|
input_per_m = EXCLUDED.input_per_m,
|
|
output_per_m = EXCLUDED.output_per_m,
|
|
cache_create_per_m = EXCLUDED.cache_create_per_m,
|
|
cache_read_per_m = EXCLUDED.cache_read_per_m,
|
|
currency = EXCLUDED.currency,
|
|
source = EXCLUDED.source,
|
|
updated_by = EXCLUDED.updated_by,
|
|
updated_at = NOW()
|
|
`,
|
|
entry.ProviderConfigID, entry.ModelID,
|
|
entry.InputPerM, entry.OutputPerM, entry.CacheCreatePerM, entry.CacheReadPerM,
|
|
entry.Currency, entry.Source, entry.UpdatedBy,
|
|
)
|
|
return err
|
|
}
|
|
|
|
// UpsertFromCatalog inserts or updates pricing from a catalog sync.
|
|
// Manual overrides (source='manual') are never overwritten.
|
|
func (s *PricingStore) UpsertFromCatalog(ctx context.Context, providerConfigID, modelID string, pricing *models.ModelPricing) error {
|
|
if pricing == nil || (pricing.InputPerM == 0 && pricing.OutputPerM == 0) {
|
|
return nil // No pricing to store
|
|
}
|
|
|
|
currency := pricing.Currency
|
|
if currency == "" {
|
|
currency = "USD"
|
|
}
|
|
|
|
_, err := DB.ExecContext(ctx, `
|
|
INSERT INTO model_pricing (
|
|
provider_config_id, model_id,
|
|
input_per_m, output_per_m,
|
|
currency, source, updated_at
|
|
) VALUES ($1, $2, $3, $4, $5, 'catalog', NOW())
|
|
ON CONFLICT (provider_config_id, model_id)
|
|
DO UPDATE SET
|
|
input_per_m = EXCLUDED.input_per_m,
|
|
output_per_m = EXCLUDED.output_per_m,
|
|
currency = EXCLUDED.currency,
|
|
updated_at = NOW()
|
|
WHERE model_pricing.source = 'catalog'
|
|
`,
|
|
providerConfigID, modelID,
|
|
pricing.InputPerM, pricing.OutputPerM,
|
|
currency,
|
|
)
|
|
return err
|
|
}
|
|
|
|
// List returns pricing entries for admin-managed providers (global + team).
|
|
// Excludes personal BYOK providers — those are not the admin's concern.
|
|
func (s *PricingStore) List(ctx context.Context) ([]models.PricingEntry, error) {
|
|
rows, err := DB.QueryContext(ctx, `
|
|
SELECT mp.id, mp.provider_config_id, mp.model_id,
|
|
mp.input_per_m, mp.output_per_m, mp.cache_create_per_m, mp.cache_read_per_m,
|
|
mp.currency, mp.source, mp.updated_at, mp.updated_by
|
|
FROM model_pricing mp
|
|
JOIN provider_configs pc ON pc.id = mp.provider_config_id
|
|
WHERE pc.scope != 'personal'
|
|
ORDER BY mp.provider_config_id, mp.model_id
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var results []models.PricingEntry
|
|
for rows.Next() {
|
|
var p models.PricingEntry
|
|
if err := rows.Scan(
|
|
&p.ID, &p.ProviderConfigID, &p.ModelID,
|
|
&p.InputPerM, &p.OutputPerM, &p.CacheCreatePerM, &p.CacheReadPerM,
|
|
&p.Currency, &p.Source, &p.UpdatedAt, &p.UpdatedBy,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
results = append(results, p)
|
|
}
|
|
return results, rows.Err()
|
|
}
|
|
|
|
// Delete removes a pricing entry.
|
|
func (s *PricingStore) Delete(ctx context.Context, providerConfigID, modelID string) error {
|
|
_, err := DB.ExecContext(ctx, `
|
|
DELETE FROM model_pricing WHERE provider_config_id = $1 AND model_id = $2
|
|
`, providerConfigID, modelID)
|
|
return err
|
|
}
|