opencode-free-fleet 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Metadata Oracle - Cross-Provider Model Metadata Lookup
3
+ *
4
+ * v0.2.1 - Build Repair: Removed problematic Z.Ai SDK import
5
+ */
6
+ /**
7
+ * Static knowledge base of confirmed free models
8
+ * This can be updated without code changes
9
+ */
10
+ export const CONFIRMED_FREE_MODELS = new Set([
11
+ // OpenRouter (verified free via pricing)
12
+ 'openrouter/qwen/qwen3-coder:free',
13
+ 'openrouter/deepseek/deepseek-v3.2',
14
+ 'openrouter/deepseek/deepseek-r1-0528:free',
15
+ 'openrouter/z-ai/glm-4.5-air:free',
16
+ 'openrouter/arcee-ai/trinity-large-preview:free',
17
+ 'openrouter/mistralai/mistral-small-3.1-24b-instruct:free',
18
+ 'openrouter/mistralai/mistral-tiny:free',
19
+ 'openrouter/nvidia/nemotron-3-nano-30b-a3b:free',
20
+ 'openrouter/nvidia/nemotron-3-nano-12b-v2-vl:free',
21
+ 'openrouter/nvidia/nemotron-3-nano-9b-v2:free',
22
+ 'openrouter/google/gemma-3n-e2b-it:free',
23
+ 'openrouter/google/gemma-3n-e4b-it:free',
24
+ // DeepSeek (official documentation)
25
+ 'deepseek/deepseek-chat',
26
+ 'deepseek/deepseek-v3',
27
+ 'deepseek/deepseek-r1',
28
+ // Groq (current policy)
29
+ 'groq/llama-3.1-8b-instruct',
30
+ 'groq/llama-3.1-70b-versatile-instruct',
31
+ 'groq/mixtral-8x7b-instruct',
32
+ // Hugging Face (serverless free tier)
33
+ 'huggingface/Qwen/Qwen2.5-72B-Instruct-Turbo',
34
+ // Google (limited free tier)
35
+ 'google/gemini-1.5-flash',
36
+ 'google/gemini-1.5-flash-8b'
37
+ ]);
38
+ /**
39
+ * External metadata APIs
40
+ * Source for free tier verification
41
+ */
42
+ const METADATA_SOURCES = [
43
+ 'models.dev' // Open source model metadata database
44
+ ];
45
+ /**
46
+ * Models.dev metadata API client
47
+ */
48
+ class ModelsDevAdapter {
49
+ providerId = 'models.dev';
50
+ providerName = 'Models.dev';
51
+ async fetchModelsMetadata(modelIds) {
52
+ if (modelIds && modelIds.length === 0) {
53
+ console.log('📊 Models.dev: No model IDs provided, fetching all known free models');
54
+ }
55
+ const all = await this._fetchFromModelsDev();
56
+ return modelIds ? all.filter(m => modelIds.includes(m.id)) : all;
57
+ }
58
+ async fetchModelMetadata(modelId) {
59
+ const all = await this._fetchFromModelsDev();
60
+ const model = all.find(m => m.id === modelId);
61
+ if (!model) {
62
+ return {
63
+ id: modelId,
64
+ provider: this.providerId,
65
+ name: modelId,
66
+ isFree: false,
67
+ confidence: 0,
68
+ reason: 'Model not found in Models.dev',
69
+ pricing: { prompt: '0', completion: '0', request: '0' }
70
+ };
71
+ }
72
+ // Determine if free based on Models.dev data
73
+ const isFree = model.pricing?.prompt === '0' || model.pricing?.prompt === '0.0' ||
74
+ model.pricing?.completion === '0' || model.pricing?.completion === '0.0';
75
+ return {
76
+ id: model.id,
77
+ provider: this.providerId,
78
+ name: model.name || model.id,
79
+ isFree,
80
+ confidence: isFree ? 1.0 : 0.7,
81
+ reason: isFree ? `Confirmed free via Models.dev (prompt=${model.pricing?.prompt}, completion=${model.pricing?.completion})` : 'Uncertain pricing - SDK may differ',
82
+ lastVerified: new Date().toISOString(),
83
+ pricing: {
84
+ prompt: model.pricing?.prompt || '0',
85
+ completion: model.pricing?.completion || '0',
86
+ request: model.pricing?.request || '0'
87
+ }
88
+ };
89
+ }
90
+ async _fetchFromModelsDev() {
91
+ console.log('📊 Models.dev: Fetching model metadata...');
92
+ try {
93
+ const response = await fetch('https://models.dev/api/v1/models', {
94
+ headers: {
95
+ 'Accept': 'application/json'
96
+ }
97
+ });
98
+ if (!response.ok) {
99
+ throw new Error(`Models.dev API error: ${response.status} ${response.statusText}`);
100
+ }
101
+ const data = await response.json();
102
+ const models = data.data || [];
103
+ console.log(`✓ Models.dev: Found ${models.length} models`);
104
+ return models;
105
+ }
106
+ catch (error) {
107
+ console.error('❌ Models.dev API error:', error);
108
+ return [];
109
+ }
110
+ }
111
+ isAvailable() {
112
+ // Always available (public API)
113
+ return true;
114
+ }
115
+ }
116
+ /**
117
+ * Unified metadata oracle
118
+ * Aggregates data from multiple metadata sources
119
+ */
120
+ export class MetadataOracle {
121
+ adapters = new Map();
122
+ constructor() {
123
+ this._initializeAdapters();
124
+ }
125
+ /**
126
+ * Initialize all metadata adapters
127
+ */
128
+ _initializeAdapters() {
129
+ console.log('🔮 Metadata Oracle: Initializing adapters...');
130
+ // Models.dev - Always available
131
+ this.adapters.set('models.dev', new ModelsDevAdapter());
132
+ // Note: Removed Z.Ai SDK, Google Cloud AI SDK, etc.
133
+ // These adapters are now implemented directly in src/core/adapters/index.ts
134
+ }
135
+ /**
136
+ * Check which adapters are available
137
+ */
138
+ getAvailableAdapters() {
139
+ const available = [];
140
+ for (const [providerId, adapter] of this.adapters.entries()) {
141
+ if (adapter.isAvailable()) {
142
+ available.push(providerId);
143
+ }
144
+ }
145
+ return available;
146
+ }
147
+ /**
148
+ * Fetch metadata for a specific model from all available sources
149
+ * This is the main method Scout should call for free tier detection
150
+ */
151
+ async fetchModelMetadata(modelId) {
152
+ console.log(`\n🔮 Metadata Oracle: Fetching metadata for ${modelId}...\n`);
153
+ const availableAdapters = this.getAvailableAdapters();
154
+ const allMetadata = [];
155
+ // Try each available adapter
156
+ for (const providerId of availableAdapters) {
157
+ const adapter = this.adapters.get(providerId);
158
+ if (!adapter)
159
+ continue;
160
+ try {
161
+ const metadata = await adapter.fetchModelMetadata(modelId);
162
+ allMetadata.push(metadata);
163
+ }
164
+ catch (error) {
165
+ console.warn(`⚠️ Metadata Oracle: Adapter ${providerId} failed: ${error}`);
166
+ }
167
+ }
168
+ if (allMetadata.length === 0) {
169
+ return {
170
+ id: modelId,
171
+ provider: 'unknown',
172
+ name: modelId,
173
+ isFree: false,
174
+ confidence: 0,
175
+ reason: 'Model not found in any metadata source',
176
+ pricing: { prompt: '0', completion: '0', request: '0' }
177
+ };
178
+ }
179
+ // Merge results with confidence scoring
180
+ const freeResults = allMetadata.filter(m => m.isFree);
181
+ const hasFreeResult = freeResults.length > 0;
182
+ // Determine overall confidence
183
+ let confidence = 0.3; // Low confidence if no metadata
184
+ let reason = 'No metadata found';
185
+ if (hasFreeResult) {
186
+ confidence = 1.0; // High confidence if at least one source says free
187
+ reason = `Confirmed free by ${freeResults.map(m => m.provider).join(', ')}`;
188
+ }
189
+ else if (allMetadata.length > 0) {
190
+ confidence = 0.7; // Medium confidence if metadata exists but no free result
191
+ reason = `Metadata found but not confirmed free (providers: ${allMetadata.map(m => m.provider).join(', ')})`;
192
+ }
193
+ // Return first free result (highest confidence)
194
+ const finalMetadata = hasFreeResult ? freeResults[0] : allMetadata[0];
195
+ return {
196
+ ...finalMetadata,
197
+ confidence,
198
+ reason,
199
+ lastVerified: new Date().toISOString()
200
+ };
201
+ }
202
+ /**
203
+ * Batch fetch metadata for multiple models
204
+ */
205
+ async fetchModelsMetadata(modelIds) {
206
+ console.log(`\n🔮 Metadata Oracle: Fetching metadata for ${modelIds.length} models...\n`);
207
+ const availableAdapters = this.getAvailableAdapters();
208
+ const allMetadata = [];
209
+ for (const modelId of modelIds) {
210
+ const metadata = await this.fetchModelMetadata(modelId);
211
+ allMetadata.push(metadata);
212
+ }
213
+ return allMetadata;
214
+ }
215
+ /**
216
+ * Get list of all confirmed free models
217
+ */
218
+ getConfirmedFreeModels() {
219
+ return CONFIRMED_FREE_MODELS;
220
+ }
221
+ /**
222
+ * Add a manually confirmed free model
223
+ * This allows updating of static knowledge base
224
+ */
225
+ addConfirmedFreeModel(modelId) {
226
+ CONFIRMED_FREE_MODELS.add(modelId);
227
+ }
228
+ /**
229
+ * Remove a model from confirmed free list
230
+ */
231
+ removeConfirmedFreeModel(modelId) {
232
+ CONFIRMED_FREE_MODELS.delete(modelId);
233
+ }
234
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The Racer - Zero-Latency Model Competition
3
+ *
4
+ * This module implements Promise.any-based racing between multiple free models,
5
+ * accepting the first valid response. This eliminates waterfall latency
6
+ * and optimizes for zero-cost execution.
7
+ *
8
+ * Key Features:
9
+ * - Promise.any for race condition
10
+ * - AbortController for timeout handling
11
+ * - Progress callbacks for monitoring
12
+ * - Error aggregation for all failures
13
+ */
14
+ import type { RaceResult, RaceConfig } from '../types/index.js';
15
+ /**
16
+ * Racer class for competing free models
17
+ */
18
+ export declare class FreeModelRacer {
19
+ private config;
20
+ private activeRaces;
21
+ constructor(config?: RaceConfig);
22
+ /**
23
+ * Compete between multiple free models and return the fastest valid response
24
+ * Uses Promise.any for race condition - fires all requests simultaneously
25
+ * and accepts the first valid response.
26
+ *
27
+ * @param models - Array of model identifiers to compete
28
+ * @param executeWithModel - Function to execute task with a specific model
29
+ * @param raceId - Optional ID for tracking and cancellation
30
+ * @returns Object containing the winning model and its result
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const racer = new FreeModelRacer({ timeoutMs: 15000 });
35
+ *
36
+ * const models = [
37
+ * 'deepseek/deepseek-v3.2',
38
+ * 'zai-coding-plan/glm-4.7-flash',
39
+ * 'openrouter/mistralai/mistral-small-3.1-24b-instruct:free'
40
+ * ];
41
+ *
42
+ * const winner = await racer.race(
43
+ * models,
44
+ * async (model) => {
45
+ * return await client.chat.completions.create({ model, messages });
46
+ * },
47
+ * 'session-123'
48
+ * );
49
+ *
50
+ * console.log(`Fastest: ${winner.model} (${winner.duration}ms)`);
51
+ * return winner.result;
52
+ * ```
53
+ */
54
+ race<T>(models: string[], executeWithModel: (model: string) => Promise<T>, raceId?: string): Promise<RaceResult<T>>;
55
+ /**
56
+ * Race models from a category config
57
+ */
58
+ raceFromCategory<T>(categoryConfig: {
59
+ model: string;
60
+ fallback: string[];
61
+ }, executeWithModel: (model: string) => Promise<T>, raceId?: string): Promise<RaceResult<T>>;
62
+ /**
63
+ * Cancel an active race by ID
64
+ */
65
+ cancelRace(raceId: string): boolean;
66
+ /**
67
+ * Cancel all active races
68
+ */
69
+ cancelAllRaces(): void;
70
+ /**
71
+ * Get count of active races
72
+ */
73
+ getActiveRaceCount(): number;
74
+ /**
75
+ * Check if a race is currently active
76
+ */
77
+ isRaceActive(raceId: string): boolean;
78
+ /**
79
+ * Create a timeout promise that rejects when abort signal is received
80
+ */
81
+ private createTimeoutPromise;
82
+ /**
83
+ * Update race configuration
84
+ */
85
+ updateConfig(config: Partial<RaceConfig>): void;
86
+ /**
87
+ * Get current race configuration
88
+ */
89
+ getConfig(): Readonly<RaceConfig>;
90
+ }
91
+ /**
92
+ * Compete between multiple free models (convenience function)
93
+ *
94
+ * This is a stateless version of FreeModelRacer for simple one-off races.
95
+ *
96
+ * @param models - Array of model identifiers to compete
97
+ * @param executeWithModel - Function to execute task with a specific model
98
+ * @param config - Optional race configuration
99
+ * @returns Object containing the winning model and its result
100
+ */
101
+ export declare function competeFreeModels<T>(models: string[], executeWithModel: (model: string) => Promise<T>, config?: RaceConfig): Promise<RaceResult<T>>;
102
+ /**
103
+ * Create a new Racer instance with optional config
104
+ */
105
+ export declare function createRacer(config?: RaceConfig): FreeModelRacer;
@@ -0,0 +1,209 @@
1
+ /**
2
+ * The Racer - Zero-Latency Model Competition
3
+ *
4
+ * This module implements Promise.any-based racing between multiple free models,
5
+ * accepting the first valid response. This eliminates waterfall latency
6
+ * and optimizes for zero-cost execution.
7
+ *
8
+ * Key Features:
9
+ * - Promise.any for race condition
10
+ * - AbortController for timeout handling
11
+ * - Progress callbacks for monitoring
12
+ * - Error aggregation for all failures
13
+ */
14
+ /**
15
+ * Racer class for competing free models
16
+ */
17
+ export class FreeModelRacer {
18
+ config;
19
+ activeRaces = new Map();
20
+ constructor(config = {}) {
21
+ this.config = {
22
+ timeoutMs: 30000,
23
+ ...config
24
+ };
25
+ }
26
+ /**
27
+ * Compete between multiple free models and return the fastest valid response
28
+ * Uses Promise.any for race condition - fires all requests simultaneously
29
+ * and accepts the first valid response.
30
+ *
31
+ * @param models - Array of model identifiers to compete
32
+ * @param executeWithModel - Function to execute task with a specific model
33
+ * @param raceId - Optional ID for tracking and cancellation
34
+ * @returns Object containing the winning model and its result
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const racer = new FreeModelRacer({ timeoutMs: 15000 });
39
+ *
40
+ * const models = [
41
+ * 'deepseek/deepseek-v3.2',
42
+ * 'zai-coding-plan/glm-4.7-flash',
43
+ * 'openrouter/mistralai/mistral-small-3.1-24b-instruct:free'
44
+ * ];
45
+ *
46
+ * const winner = await racer.race(
47
+ * models,
48
+ * async (model) => {
49
+ * return await client.chat.completions.create({ model, messages });
50
+ * },
51
+ * 'session-123'
52
+ * );
53
+ *
54
+ * console.log(`Fastest: ${winner.model} (${winner.duration}ms)`);
55
+ * return winner.result;
56
+ * ```
57
+ */
58
+ async race(models, executeWithModel, raceId = `race-${Date.now()}`) {
59
+ if (models.length === 0) {
60
+ throw new Error('Racer: No models provided for competition');
61
+ }
62
+ console.log(`🏁 Racer: Starting race '${raceId}' with ${models.length} models`);
63
+ // Create abort controller for this race
64
+ const abortController = new AbortController();
65
+ this.activeRaces.set(raceId, abortController);
66
+ const startTime = performance.now();
67
+ try {
68
+ const racePromises = models.map(async (model) => {
69
+ try {
70
+ // Notify started
71
+ this.config.onProgress?.(model, 'started');
72
+ const result = await Promise.race([
73
+ executeWithModel(model),
74
+ this.createTimeoutPromise(this.config.timeoutMs, abortController.signal)
75
+ ]);
76
+ const duration = performance.now() - startTime;
77
+ // Notify completed
78
+ this.config.onProgress?.(model, 'completed');
79
+ console.log(`✅ Racer: ${model} completed in ${duration.toFixed(0)}ms`);
80
+ return { model, result, duration };
81
+ }
82
+ catch (error) {
83
+ const err = error;
84
+ // Notify failed
85
+ this.config.onProgress?.(model, 'failed', err);
86
+ // Check if aborted
87
+ if (err.name === 'AbortError') {
88
+ console.log(`⏹️ Racer: ${model} aborted`);
89
+ }
90
+ else {
91
+ console.log(`❌ Racer: ${model} failed - ${err.message}`);
92
+ }
93
+ throw new Error(`Model ${model} failed: ${err.message}`);
94
+ }
95
+ });
96
+ const winner = await Promise.any(racePromises);
97
+ // Abort all other pending requests
98
+ abortController.abort();
99
+ this.activeRaces.delete(raceId);
100
+ console.log(`🏆 Racer: Winner is ${winner.model} (${winner.duration.toFixed(0)}ms)`);
101
+ console.log(` Competed against: ${models.join(', ')}`);
102
+ return winner;
103
+ }
104
+ catch (error) {
105
+ // Clean up
106
+ this.activeRaces.delete(raceId);
107
+ // AggregateError: all models failed
108
+ const err = error;
109
+ if (err.name === 'AggregateError' && err.errors) {
110
+ const errorDetails = err.errors.map((e) => e.message).join('\n');
111
+ throw new Error(`Racer: All ${models.length} models failed:\n${errorDetails}`);
112
+ }
113
+ // Check if race was externally aborted
114
+ if (err.name === 'AbortError') {
115
+ console.log(`🛑 Racer: Race '${raceId}' externally aborted`);
116
+ throw new Error(`was aborted`);
117
+ }
118
+ throw err;
119
+ }
120
+ }
121
+ /**
122
+ * Race models from a category config
123
+ */
124
+ async raceFromCategory(categoryConfig, executeWithModel, raceId) {
125
+ const allModels = [categoryConfig.model, ...categoryConfig.fallback];
126
+ return this.race(allModels, executeWithModel, raceId);
127
+ }
128
+ /**
129
+ * Cancel an active race by ID
130
+ */
131
+ cancelRace(raceId) {
132
+ const controller = this.activeRaces.get(raceId);
133
+ if (controller) {
134
+ controller.abort();
135
+ this.activeRaces.delete(raceId);
136
+ console.log(`🛑 Racer: Cancelled race '${raceId}'`);
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ /**
142
+ * Cancel all active races
143
+ */
144
+ cancelAllRaces() {
145
+ for (const [raceId, controller] of this.activeRaces.entries()) {
146
+ controller.abort();
147
+ console.log(`🛑 Racer: Cancelled race '${raceId}'`);
148
+ }
149
+ this.activeRaces.clear();
150
+ }
151
+ /**
152
+ * Get count of active races
153
+ */
154
+ getActiveRaceCount() {
155
+ return this.activeRaces.size;
156
+ }
157
+ /**
158
+ * Check if a race is currently active
159
+ */
160
+ isRaceActive(raceId) {
161
+ return this.activeRaces.has(raceId);
162
+ }
163
+ /**
164
+ * Create a timeout promise that rejects when abort signal is received
165
+ */
166
+ createTimeoutPromise(timeoutMs, signal) {
167
+ return new Promise((_, reject) => {
168
+ const timeout = setTimeout(() => {
169
+ reject(new Error('Timeout'));
170
+ }, timeoutMs);
171
+ signal.addEventListener('abort', () => {
172
+ clearTimeout(timeout);
173
+ reject(new Error('AbortError'));
174
+ }, { once: true });
175
+ });
176
+ }
177
+ /**
178
+ * Update race configuration
179
+ */
180
+ updateConfig(config) {
181
+ this.config = { ...this.config, ...config };
182
+ }
183
+ /**
184
+ * Get current race configuration
185
+ */
186
+ getConfig() {
187
+ return { ...this.config };
188
+ }
189
+ }
190
+ /**
191
+ * Compete between multiple free models (convenience function)
192
+ *
193
+ * This is a stateless version of FreeModelRacer for simple one-off races.
194
+ *
195
+ * @param models - Array of model identifiers to compete
196
+ * @param executeWithModel - Function to execute task with a specific model
197
+ * @param config - Optional race configuration
198
+ * @returns Object containing the winning model and its result
199
+ */
200
+ export async function competeFreeModels(models, executeWithModel, config) {
201
+ const racer = new FreeModelRacer(config);
202
+ return racer.race(models, executeWithModel);
203
+ }
204
+ /**
205
+ * Create a new Racer instance with optional config
206
+ */
207
+ export function createRacer(config) {
208
+ return new FreeModelRacer(config);
209
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The Scout - Multi-Provider Free Model Discovery & Benchmark-Based Ranking
3
+ *
4
+ * v0.2.0 Upgrade: Metadata Oracle + Smart Free Tier Detection
5
+ *
6
+ * This module discovers free LLM models from ALL connected providers,
7
+ * aggregates metadata from external APIs, and ranks them by SOTA benchmark performance.
8
+ */
9
+ import type { ScoutConfig, ScoutResult, ModelCategory } from '../types/index.js';
10
+ /**
11
+ * Scout class for model discovery and ranking
12
+ */
13
+ export declare class Scout {
14
+ private config;
15
+ private blocklist;
16
+ private antigravityActive;
17
+ private metadataOracle;
18
+ constructor(config?: Partial<ScoutConfig>);
19
+ /**
20
+ * Initialize metadata oracle and adapters
21
+ */
22
+ private initialize;
23
+ /**
24
+ * PHASE A: Safety Check - Build blocklist of paid/authenticated models
25
+ *
26
+ * Enhanced in v0.2.0:
27
+ * - Check for Antigravity auth presence/configuration
28
+ * - Respect allowAntigravity flag to optionally include Google/Gemini
29
+ * - NOTE: Blocklist is now used for metadata filtering, not model exclusion
30
+ */
31
+ private buildBlocklist;
32
+ /**
33
+ * Detect active providers from OpenCode configuration
34
+ */
35
+ private detectActiveProviders;
36
+ /**
37
+ * PHASE B: Fetch and Normalize Models with Metadata Oracle
38
+ *
39
+ * Enhanced in v0.2.0:
40
+ * - Fetch models from provider adapters
41
+ * - Enrich with metadata from MetadataOracle (Models.dev)
42
+ * - Determine free tier based on aggregated metadata + confidence scoring
43
+ * - Use metadata.isFree field instead of hardcoded free tier detection
44
+ */
45
+ private fetchAllModels;
46
+ /**
47
+ * Categorize a model based on its ID patterns
48
+ */
49
+ private categorizeModel;
50
+ /**
51
+ * Check if a model is in Elite families
52
+ */
53
+ private isEliteModel;
54
+ /**
55
+ * Extract parameter count from model ID
56
+ * Looks for patterns like "70b", "8b", "32b" in the ID
57
+ */
58
+ private extractParams;
59
+ /**
60
+ * Extract date from model ID (if available)
61
+ * Looks for patterns like "2025-01", "v0.1" in the ID
62
+ */
63
+ private extractDate;
64
+ /**
65
+ * Get provider priority from metadata
66
+ * Higher priority providers are listed first in OpenCode settings
67
+ */
68
+ private getProviderPriority;
69
+ /**
70
+ * Get elite patterns for a specific category
71
+ */
72
+ private _getElitePatterns;
73
+ /**
74
+ * PHASE C: Ranking Algorithm - Multi-Provider SOTA Benchmarking
75
+ *
76
+ * Enhanced in v0.2.0:
77
+ * - Priority 1: Metadata confidence score (from Models.dev, etc.)
78
+ * - Priority 2: Elite family membership (SOTA benchmarks)
79
+ * - Priority 3: Provider priority (from metadata provider ranking)
80
+ * - Priority 4: Parameter count (larger > smaller, except for speed)
81
+ * - Priority 5: Release date (newer > older)
82
+ * - Priority 6: Alphabetical order (tiebreaker)
83
+ */
84
+ private rankModelsByBenchmark;
85
+ /**
86
+ * PHASE D: Functional Categorization
87
+ *
88
+ * Sorts models into functional categories based on ID patterns:
89
+ * - coding: IDs with "coder", "code", "function"
90
+ * - reasoning: IDs with "r1", "reasoning", "cot", "qwq"
91
+ * - speed: IDs with "flash", "distill", "nano", "lite"
92
+ * - multimodal: IDs with "vl", "vision"
93
+ * - writing: General purpose models not in other categories
94
+ */
95
+ private categorizeModels;
96
+ /**
97
+ * Generate category configuration from ranked models
98
+ */
99
+ private generateCategoryConfig;
100
+ /**
101
+ * Main discovery and ranking method
102
+ * Returns categorized and ranked free models from ALL active providers
103
+ * with metadata enrichment from external APIs
104
+ */
105
+ discover(): Promise<Record<ModelCategory, ScoutResult>>;
106
+ /**
107
+ * Print summary of results
108
+ */
109
+ printSummary(results: Record<ModelCategory, ScoutResult>): void;
110
+ /**
111
+ * Get configuration info
112
+ */
113
+ getConfiguration(): Promise<{
114
+ antigravityActive: boolean;
115
+ allowAntigravity: boolean;
116
+ blocklist: string[];
117
+ hasMetadataOracle: boolean;
118
+ providersAvailable?: string[];
119
+ }>;
120
+ }
121
+ /**
122
+ * Create a new Scout instance with optional config
123
+ */
124
+ export declare function createScout(config?: Partial<ScoutConfig>): Scout;