All files / src/models PricingRegistry.ts

79.31% Statements 23/29
78.57% Branches 11/14
100% Functions 5/5
81.48% Lines 22/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121                85x 85x             85x                                                                                     528x 528x 2x       526x 1565x 22x         504x 504x             2x             1x 1x 1x 1x 1x 1x 1x 1x     1x 1x                                            
import { ModelRegistry } from "./ModelRegistry.js";
import { ModelPricing } from "./types.js";
 
/**
 * Registry for LLM pricing information.
 * Priority: Runtime Overrides > Library Default Patterns > ModelRegistry (models.ts)
 */
export class PricingRegistry {
  private static pricingOverrides = new Map<string, ModelPricing>();
  private static lastUpdated: Date | null = new Date();
 
  // Library-level fallbacks for common model families
  private static DEFAULT_PATTERNS: Array<{
    pattern: RegExp;
    provider: string;
    pricing: ModelPricing;
  }> = [
    {
      provider: "anthropic",
      pattern: /claude-3-7/,
      pricing: {
        text_tokens: {
          standard: {
            input_per_million: 3.0,
            output_per_million: 15.0,
            reasoning_output_per_million: 37.5
          },
          batch: {
            input_per_million: 1.5,
            output_per_million: 7.5,
            reasoning_output_per_million: 18.75
          }
        }
      }
    },
    {
      provider: "anthropic",
      pattern: /claude-3/,
      pricing: {
        text_tokens: {
          standard: { input_per_million: 3.0, output_per_million: 15.0 },
          batch: { input_per_million: 1.5, output_per_million: 7.5 }
        }
      }
    },
    {
      provider: "openai",
      pattern: /gpt-3/,
      pricing: {
        text_tokens: { standard: { input_per_million: 0.5, output_per_million: 1.5 } }
      }
    }
  ];
 
  /**
   * Get pricing for a model.
   */
  static getPricing(modelId: string, provider: string): ModelPricing | undefined {
    // 1. Check custom overrides (Runtime/Remote)
    const key = `${provider}/${modelId}`;
    if (this.pricingOverrides.has(key)) {
      return this.pricingOverrides.get(key);
    }
 
    // 2. Check library default patterns (Core Overrides)
    for (const entry of this.DEFAULT_PATTERNS) {
      if (entry.provider === provider && entry.pattern.test(modelId)) {
        return entry.pricing;
      }
    }
 
    // 3. Fallback to registry lookup (models.ts)
    const model = ModelRegistry.find(modelId, provider);
    return model?.pricing;
  }
 
  /**
   * Register or override pricing at runtime.
   */
  static register(provider: string, modelId: string, pricing: ModelPricing) {
    this.pricingOverrides.set(`${provider}/${modelId}`, pricing);
  }
 
  /**
   * Fetch updates from a remote URL.
   */
  static async fetchUpdates(url: string) {
    try {
      const response = await fetch(url);
      Iif (!response.ok) throw new Error(response.statusText);
      const data = await response.json();
      Eif (data.models) {
        for (const [key, pricing] of Object.entries(data.models)) {
          const [provider, modelId] = key.split("/");
          Eif (provider && modelId) this.register(provider, modelId, pricing as ModelPricing);
        }
      }
      this.lastUpdated = new Date();
      return true;
    } catch (error) {
      console.error("[NodeLLM] Error fetching pricing updates:", error);
      return false;
    }
  }
 
  /**
   * Get the timestamp of the last update.
   */
  static getLastUpdated(): Date | null {
    return this.lastUpdated;
  }
 
  /**
   * Reset overrides.
   */
  static reset() {
    this.pricingOverrides.clear();
    this.lastUpdated = new Date();
  }
}