File size: 4,261 Bytes
15a5288
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
122
123
124
125
126
127
128
129
130
import { BaseProviderFetcher } from './base';
import { ProviderEntry, FireworksModel, FireworksDetailedModel } from './types';

export class FireworksFetcher extends BaseProviderFetcher {
  name = 'fireworks';

  constructor(apiKey?: string) {
    super('https://api.fireworks.ai', apiKey, {
      requestsPerMinute: 60  // Conservative default
    });
  }

  async fetchModels(): Promise<ProviderEntry[]> {
    try {
      const response = await this.fetchWithRetry<{ data: FireworksModel[] }>(
        `${this.baseUrl}/inference/v1/models`
      );

      // Map basic model data
      const basicEntries = response.data.map(model => this.mapBasicModelToProviderEntry(model));

      // Optionally enrich with detailed data for important models
      // This can be done selectively to avoid too many API calls
      const enrichedEntries = await this.enrichModels(basicEntries, response.data);

      return enrichedEntries;
    } catch (error) {
      console.error(`Failed to fetch Fireworks models: ${error}`);
      return [];
    }
  }

  private async enrichModels(
    basicEntries: ProviderEntry[], 
    models: FireworksModel[]
  ): Promise<ProviderEntry[]> {
    // For now, we'll return basic entries
    // In production, you might want to selectively enrich important models
    // to avoid hitting rate limits
    return basicEntries;
  }

  async fetchDetailedModel(accountId: string, modelId: string): Promise<ProviderEntry | null> {
    try {
      const response = await this.fetchWithRetry<FireworksDetailedModel>(
        `${this.baseUrl}/v1/accounts/${accountId}/models/${modelId}`
      );

      return this.mapDetailedModelToProviderEntry(response);
    } catch (error) {
      console.error(`Failed to fetch detailed Fireworks model ${modelId}: ${error}`);
      return null;
    }
  }

  private mapBasicModelToProviderEntry(model: FireworksModel): ProviderEntry {
    const entry: ProviderEntry = {
      provider: this.name,
      context_length: model.context_length,
      owned_by: model.owned_by,
      supports_image_input: model.supports_image_input,
      supports_tools: model.supports_tools,
      supports_function_calling: model.supports_tools
    };

    // Set model type based on chat support
    if (model.supports_chat) {
      entry.model_type = 'chat';
    }

    return entry;
  }

  private mapDetailedModelToProviderEntry(model: FireworksDetailedModel): ProviderEntry {
    const entry: ProviderEntry = {
      provider: this.name,
      context_length: model.contextLength,
      status: model.state === 'READY' ? 'live' : 'offline',
      description: model.description,
      quantization: model.baseModelDetails.defaultPrecision,
      supports_image_input: model.supportsImageInput,
      supports_tools: model.supportsTools,
      supports_function_calling: model.supportsTools
    };

    // Check deprecation
    if (model.deprecationDate) {
      entry.status = 'deprecated';
      entry.deprecated_at = model.deprecationDate;
    }

    // Parse parameter count if available
    if (model.baseModelDetails.parameterCount) {
      // Store as metadata - you might want to parse this into a number
      entry.owned_by = model.displayName;
    }

    // Parse supported parameters from defaultSamplingParams
    if (model.defaultSamplingParams) {
      const paramCapabilities = this.parseSupportedParameters(model.defaultSamplingParams);
      Object.assign(entry, paramCapabilities);
    }

    // Additional capabilities from model details
    if (model.supportsLora) {
      // Custom capability - not in standard ProviderEntry but could be added
      // entry.supports_lora = true;
    }

    // Map supported precisions
    if (model.supportedPrecisions && model.supportedPrecisions.length > 0) {
      // Could store as metadata or custom field
    }

    return entry;
  }

  // Helper to extract model ID parts from Fireworks model ID format
  private parseModelId(id: string): { accountId: string; modelId: string } | null {
    // Format: "accounts/fireworks/models/qwen3-235b-a22b-thinking-2507"
    const match = id.match(/accounts\/([^\/]+)\/models\/([^\/]+)/);
    if (match) {
      return {
        accountId: match[1],
        modelId: match[2]
      };
    }
    return null;
  }
}