@azumag/opencode-rate-limit-fallback 1.21.0 → 1.21.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,73 @@
1
+ /**
2
+ * Fallback orchestration logic
3
+ */
4
+ import type { Logger } from '../../logger.js';
5
+ import type { FallbackModel, PluginConfig, OpenCodeClient, MessagePart, SessionHierarchy } from '../types/index.js';
6
+ import { MetricsManager } from '../metrics/MetricsManager.js';
7
+ /**
8
+ * Hierarchy resolver functions
9
+ */
10
+ export type HierarchyResolver = {
11
+ getRootSession: (sessionID: string) => string | null;
12
+ getHierarchy: (sessionID: string) => SessionHierarchy | null;
13
+ };
14
+ /**
15
+ * Fallback Handler class for orchestrating the fallback retry flow
16
+ */
17
+ export declare class FallbackHandler {
18
+ private config;
19
+ private client;
20
+ private logger;
21
+ private modelSelector;
22
+ private currentSessionModel;
23
+ private modelRequestStartTimes;
24
+ private retryState;
25
+ private fallbackInProgress;
26
+ private fallbackMessages;
27
+ private metricsManager;
28
+ private hierarchyResolver;
29
+ constructor(config: PluginConfig, client: OpenCodeClient, logger: Logger, metricsManager: MetricsManager, hierarchyResolver: HierarchyResolver);
30
+ /**
31
+ * Check and mark fallback in progress for deduplication
32
+ */
33
+ private checkAndMarkFallbackInProgress;
34
+ /**
35
+ * Get or create retry state for a specific message
36
+ */
37
+ private getOrCreateRetryState;
38
+ /**
39
+ * Get current model for a session
40
+ */
41
+ getSessionModel(sessionID: string): {
42
+ providerID: string;
43
+ modelID: string;
44
+ } | null;
45
+ /**
46
+ * Abort current session with error handling
47
+ */
48
+ private abortSession;
49
+ /**
50
+ * Retry the prompt with a different model
51
+ */
52
+ retryWithModel(targetSessionID: string, model: FallbackModel, parts: MessagePart[], hierarchy: SessionHierarchy | null): Promise<void>;
53
+ /**
54
+ * Handle the rate limit fallback process
55
+ */
56
+ handleRateLimitFallback(sessionID: string, currentProviderID: string, currentModelID: string): Promise<void>;
57
+ /**
58
+ * Handle message updated events for metrics recording
59
+ */
60
+ handleMessageUpdated(sessionID: string, messageID: string, hasError: boolean, isError: boolean): void;
61
+ /**
62
+ * Set model for a session
63
+ */
64
+ setSessionModel(sessionID: string, providerID: string, modelID: string): void;
65
+ /**
66
+ * Clean up stale entries
67
+ */
68
+ cleanupStaleEntries(): void;
69
+ /**
70
+ * Clean up all resources
71
+ */
72
+ destroy(): void;
73
+ }
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Fallback orchestration logic
3
+ */
4
+ import { ModelSelector } from './ModelSelector.js';
5
+ import { extractMessageParts, convertPartsToSDKFormat, safeShowToast, getStateKey, getModelKey, DEDUP_WINDOW_MS, STATE_TIMEOUT_MS } from '../utils/helpers.js';
6
+ /**
7
+ * Fallback Handler class for orchestrating the fallback retry flow
8
+ */
9
+ export class FallbackHandler {
10
+ config;
11
+ client;
12
+ logger;
13
+ modelSelector;
14
+ currentSessionModel;
15
+ modelRequestStartTimes;
16
+ retryState;
17
+ fallbackInProgress;
18
+ fallbackMessages;
19
+ // Metrics manager reference
20
+ metricsManager;
21
+ // Hierarchy resolver
22
+ hierarchyResolver;
23
+ constructor(config, client, logger, metricsManager, hierarchyResolver) {
24
+ this.config = config;
25
+ this.client = client;
26
+ this.logger = logger;
27
+ this.modelSelector = new ModelSelector(config, client);
28
+ this.metricsManager = metricsManager;
29
+ this.hierarchyResolver = hierarchyResolver;
30
+ this.currentSessionModel = new Map();
31
+ this.modelRequestStartTimes = new Map();
32
+ this.retryState = new Map();
33
+ this.fallbackInProgress = new Map();
34
+ this.fallbackMessages = new Map();
35
+ }
36
+ /**
37
+ * Check and mark fallback in progress for deduplication
38
+ */
39
+ checkAndMarkFallbackInProgress(sessionID, messageID) {
40
+ const key = getStateKey(sessionID, messageID);
41
+ const lastFallback = this.fallbackInProgress.get(key);
42
+ if (lastFallback && Date.now() - lastFallback < DEDUP_WINDOW_MS) {
43
+ return false; // Skip - already processing
44
+ }
45
+ this.fallbackInProgress.set(key, Date.now());
46
+ return true; // Continue processing
47
+ }
48
+ /**
49
+ * Get or create retry state for a specific message
50
+ */
51
+ getOrCreateRetryState(sessionID, messageID) {
52
+ const stateKey = getStateKey(sessionID, messageID);
53
+ let state = this.retryState.get(stateKey);
54
+ if (!state || Date.now() - state.lastAttemptTime > STATE_TIMEOUT_MS) {
55
+ state = { attemptedModels: new Set(), lastAttemptTime: Date.now() };
56
+ this.retryState.set(stateKey, state);
57
+ }
58
+ return state;
59
+ }
60
+ /**
61
+ * Get current model for a session
62
+ */
63
+ getSessionModel(sessionID) {
64
+ const tracked = this.currentSessionModel.get(sessionID);
65
+ return tracked ? { providerID: tracked.providerID, modelID: tracked.modelID } : null;
66
+ }
67
+ /**
68
+ * Abort current session with error handling
69
+ */
70
+ async abortSession(sessionID) {
71
+ try {
72
+ await this.client.session.abort({ path: { id: sessionID } });
73
+ }
74
+ catch (abortError) {
75
+ // Silently ignore abort errors and continue with fallback
76
+ this.logger.debug(`Failed to abort session ${sessionID}`, { error: abortError });
77
+ }
78
+ }
79
+ /**
80
+ * Retry the prompt with a different model
81
+ */
82
+ async retryWithModel(targetSessionID, model, parts, hierarchy) {
83
+ // Track the new model for this session
84
+ this.currentSessionModel.set(targetSessionID, {
85
+ providerID: model.providerID,
86
+ modelID: model.modelID,
87
+ lastUpdated: Date.now(),
88
+ });
89
+ // If this is a root session with subagents, propagate the model to all subagents
90
+ if (hierarchy) {
91
+ if (hierarchy.rootSessionID === targetSessionID) {
92
+ hierarchy.sharedFallbackState = "completed";
93
+ hierarchy.lastActivity = Date.now();
94
+ // Update model tracking for all subagents
95
+ for (const [subagentID, subagent] of hierarchy.subagents.entries()) {
96
+ this.currentSessionModel.set(subagentID, {
97
+ providerID: model.providerID,
98
+ modelID: model.modelID,
99
+ lastUpdated: Date.now(),
100
+ });
101
+ subagent.fallbackState = "completed";
102
+ subagent.lastActivity = Date.now();
103
+ }
104
+ }
105
+ }
106
+ // Record model request for metrics
107
+ if (this.metricsManager) {
108
+ this.metricsManager.recordModelRequest(model.providerID, model.modelID);
109
+ const modelKey = getModelKey(model.providerID, model.modelID);
110
+ this.modelRequestStartTimes.set(modelKey, Date.now());
111
+ }
112
+ // Convert internal MessagePart to SDK-compatible format
113
+ const sdkParts = convertPartsToSDKFormat(parts);
114
+ await this.client.session.prompt({
115
+ path: { id: targetSessionID },
116
+ body: {
117
+ parts: sdkParts,
118
+ model: { providerID: model.providerID, modelID: model.modelID },
119
+ },
120
+ });
121
+ await safeShowToast(this.client, {
122
+ body: {
123
+ title: "Fallback Successful",
124
+ message: `Now using ${model.modelID}`,
125
+ variant: "success",
126
+ duration: 3000,
127
+ },
128
+ });
129
+ }
130
+ /**
131
+ * Handle the rate limit fallback process
132
+ */
133
+ async handleRateLimitFallback(sessionID, currentProviderID, currentModelID) {
134
+ try {
135
+ // Get root session and hierarchy using resolver
136
+ const rootSessionID = this.hierarchyResolver.getRootSession(sessionID);
137
+ const targetSessionID = rootSessionID || sessionID;
138
+ const hierarchy = this.hierarchyResolver.getHierarchy(sessionID);
139
+ // If no model info provided, try to get from tracked session model
140
+ if (!currentProviderID || !currentModelID) {
141
+ const tracked = this.currentSessionModel.get(targetSessionID);
142
+ if (tracked) {
143
+ currentProviderID = tracked.providerID;
144
+ currentModelID = tracked.modelID;
145
+ }
146
+ }
147
+ // Record rate limit metric
148
+ if (currentProviderID && currentModelID && this.metricsManager) {
149
+ this.metricsManager.recordRateLimit(currentProviderID, currentModelID);
150
+ }
151
+ // Abort current session with error handling
152
+ await this.abortSession(targetSessionID);
153
+ await safeShowToast(this.client, {
154
+ body: {
155
+ title: "Rate Limit Detected",
156
+ message: `Switching from ${currentModelID || 'current model'}...`,
157
+ variant: "warning",
158
+ duration: 3000,
159
+ },
160
+ });
161
+ // Get messages from the session
162
+ const messagesResult = await this.client.session.messages({ path: { id: targetSessionID } });
163
+ if (!messagesResult.data) {
164
+ return;
165
+ }
166
+ const messages = messagesResult.data;
167
+ const lastUserMessage = [...messages].reverse().find(m => m.info.role === "user");
168
+ if (!lastUserMessage) {
169
+ return;
170
+ }
171
+ // Check deduplication with message scope
172
+ const dedupSessionID = rootSessionID || sessionID;
173
+ if (!this.checkAndMarkFallbackInProgress(dedupSessionID, lastUserMessage.info.id)) {
174
+ return; // Skip - already processing
175
+ }
176
+ // Update hierarchy state if exists
177
+ if (hierarchy && rootSessionID) {
178
+ hierarchy.sharedFallbackState = "in_progress";
179
+ hierarchy.lastActivity = Date.now();
180
+ const subagent = hierarchy.subagents.get(sessionID);
181
+ if (subagent) {
182
+ subagent.fallbackState = "in_progress";
183
+ subagent.lastActivity = Date.now();
184
+ }
185
+ }
186
+ // Get or create retry state for this message
187
+ const state = this.getOrCreateRetryState(sessionID, lastUserMessage.info.id);
188
+ const stateKey = getStateKey(sessionID, lastUserMessage.info.id);
189
+ const fallbackKey = getStateKey(dedupSessionID, lastUserMessage.info.id);
190
+ // Select the next fallback model
191
+ const nextModel = await this.modelSelector.selectFallbackModel(currentProviderID, currentModelID, state.attemptedModels);
192
+ // Show error if no model is available
193
+ if (!nextModel) {
194
+ await safeShowToast(this.client, {
195
+ body: {
196
+ title: "No Fallback Available",
197
+ message: this.config.fallbackMode === "stop"
198
+ ? "All fallback models exhausted"
199
+ : "All models are rate limited",
200
+ variant: "error",
201
+ duration: 5000,
202
+ },
203
+ });
204
+ this.retryState.delete(stateKey);
205
+ this.fallbackInProgress.delete(fallbackKey);
206
+ return;
207
+ }
208
+ state.attemptedModels.add(getModelKey(nextModel.providerID, nextModel.modelID));
209
+ state.lastAttemptTime = Date.now();
210
+ // Extract message parts
211
+ const parts = extractMessageParts(lastUserMessage);
212
+ if (parts.length === 0) {
213
+ this.fallbackInProgress.delete(fallbackKey);
214
+ return;
215
+ }
216
+ await safeShowToast(this.client, {
217
+ body: {
218
+ title: "Retrying",
219
+ message: `Using ${nextModel.providerID}/${nextModel.modelID}`,
220
+ variant: "info",
221
+ duration: 3000,
222
+ },
223
+ });
224
+ // Record fallback start time
225
+ if (this.metricsManager) {
226
+ this.metricsManager.recordFallbackStart();
227
+ }
228
+ // Track this message as a fallback message for completion detection
229
+ this.fallbackMessages.set(fallbackKey, {
230
+ sessionID: dedupSessionID,
231
+ messageID: lastUserMessage.info.id,
232
+ timestamp: Date.now(),
233
+ });
234
+ // Retry with the selected model
235
+ await this.retryWithModel(dedupSessionID, nextModel, parts, hierarchy);
236
+ // Clean up state
237
+ this.retryState.delete(stateKey);
238
+ }
239
+ catch (err) {
240
+ // Log fallback errors at warn level for visibility
241
+ const errorMessage = err instanceof Error ? err.message : String(err);
242
+ const errorName = err instanceof Error ? err.name : undefined;
243
+ this.logger.warn(`Fallback error for session ${sessionID}`, {
244
+ error: errorMessage,
245
+ name: errorName,
246
+ });
247
+ }
248
+ }
249
+ /**
250
+ * Handle message updated events for metrics recording
251
+ */
252
+ handleMessageUpdated(sessionID, messageID, hasError, isError) {
253
+ if (hasError && !isError) {
254
+ // Non-rate-limit error - record model failure metric
255
+ const tracked = this.currentSessionModel.get(sessionID);
256
+ if (tracked) {
257
+ if (this.metricsManager) {
258
+ this.metricsManager.recordModelFailure(tracked.providerID, tracked.modelID);
259
+ // Check if this was a fallback attempt and record failure
260
+ const fallbackKey = getStateKey(sessionID, messageID);
261
+ const fallbackInfo = this.fallbackMessages.get(fallbackKey);
262
+ if (fallbackInfo) {
263
+ this.metricsManager.recordFallbackFailure();
264
+ this.fallbackInProgress.delete(fallbackKey);
265
+ this.fallbackMessages.delete(fallbackKey);
266
+ }
267
+ }
268
+ }
269
+ }
270
+ else if (!hasError) {
271
+ // Check if this message is a fallback message and clear its in-progress state
272
+ const fallbackKey = getStateKey(sessionID, messageID);
273
+ const fallbackInfo = this.fallbackMessages.get(fallbackKey);
274
+ if (fallbackInfo) {
275
+ // Clear fallback in progress for this message
276
+ this.fallbackInProgress.delete(fallbackKey);
277
+ this.fallbackMessages.delete(fallbackKey);
278
+ this.logger.debug(`Fallback completed for message ${messageID}`, { sessionID });
279
+ // Record fallback success metric
280
+ const tracked = this.currentSessionModel.get(sessionID);
281
+ if (tracked) {
282
+ if (this.metricsManager) {
283
+ this.metricsManager.recordFallbackSuccess(tracked.providerID, tracked.modelID, fallbackInfo.timestamp);
284
+ // Record model performance metric
285
+ const modelKey = getModelKey(tracked.providerID, tracked.modelID);
286
+ const startTime = this.modelRequestStartTimes.get(modelKey);
287
+ if (startTime) {
288
+ const responseTime = Date.now() - startTime;
289
+ this.metricsManager.recordModelSuccess(tracked.providerID, tracked.modelID, responseTime);
290
+ this.modelRequestStartTimes.delete(modelKey);
291
+ }
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+ /**
298
+ * Set model for a session
299
+ */
300
+ setSessionModel(sessionID, providerID, modelID) {
301
+ this.currentSessionModel.set(sessionID, {
302
+ providerID,
303
+ modelID,
304
+ lastUpdated: Date.now(),
305
+ });
306
+ }
307
+ /**
308
+ * Clean up stale entries
309
+ */
310
+ cleanupStaleEntries() {
311
+ const { STATE_TIMEOUT_MS, SESSION_ENTRY_TTL_MS } = require('../types/index.js');
312
+ const now = Date.now();
313
+ for (const [sessionID, entry] of this.currentSessionModel.entries()) {
314
+ if (now - entry.lastUpdated > SESSION_ENTRY_TTL_MS) {
315
+ this.currentSessionModel.delete(sessionID);
316
+ }
317
+ }
318
+ for (const [stateKey, state] of this.retryState.entries()) {
319
+ if (now - state.lastAttemptTime > STATE_TIMEOUT_MS) {
320
+ this.retryState.delete(stateKey);
321
+ }
322
+ }
323
+ for (const [fallbackKey, fallbackInfo] of this.fallbackMessages.entries()) {
324
+ if (now - fallbackInfo.timestamp > SESSION_ENTRY_TTL_MS) {
325
+ this.fallbackInProgress.delete(fallbackKey);
326
+ this.fallbackMessages.delete(fallbackKey);
327
+ }
328
+ }
329
+ this.modelSelector.cleanupStaleEntries();
330
+ }
331
+ /**
332
+ * Clean up all resources
333
+ */
334
+ destroy() {
335
+ this.currentSessionModel.clear();
336
+ this.modelRequestStartTimes.clear();
337
+ this.retryState.clear();
338
+ this.fallbackInProgress.clear();
339
+ this.fallbackMessages.clear();
340
+ }
341
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Model selection logic based on fallback mode
3
+ */
4
+ import type { FallbackModel, PluginConfig, OpenCodeClient } from '../types/index.js';
5
+ /**
6
+ * Model Selector class for handling model selection strategies
7
+ */
8
+ export declare class ModelSelector {
9
+ private rateLimitedModels;
10
+ private config;
11
+ private client;
12
+ constructor(config: PluginConfig, client: OpenCodeClient);
13
+ /**
14
+ * Check if a model is currently rate limited
15
+ */
16
+ private isModelRateLimited;
17
+ /**
18
+ * Mark a model as rate limited
19
+ */
20
+ markModelRateLimited(providerID: string, modelID: string): void;
21
+ /**
22
+ * Find the next available model that is not rate limited
23
+ */
24
+ private findNextAvailableModel;
25
+ /**
26
+ * Apply the fallback mode logic
27
+ */
28
+ private applyFallbackMode;
29
+ /**
30
+ * Select the next fallback model based on current state and fallback mode
31
+ */
32
+ selectFallbackModel(currentProviderID: string, currentModelID: string, attemptedModels: Set<string>): Promise<FallbackModel | null>;
33
+ /**
34
+ * Clean up stale rate-limited models
35
+ */
36
+ cleanupStaleEntries(): void;
37
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Model selection logic based on fallback mode
3
+ */
4
+ import { getModelKey } from '../utils/helpers.js';
5
+ import { safeShowToast } from '../utils/helpers.js';
6
+ /**
7
+ * Model Selector class for handling model selection strategies
8
+ */
9
+ export class ModelSelector {
10
+ rateLimitedModels;
11
+ config;
12
+ client;
13
+ constructor(config, client) {
14
+ this.config = config;
15
+ this.client = client;
16
+ this.rateLimitedModels = new Map();
17
+ }
18
+ /**
19
+ * Check if a model is currently rate limited
20
+ */
21
+ isModelRateLimited(providerID, modelID) {
22
+ const key = getModelKey(providerID, modelID);
23
+ const limitedAt = this.rateLimitedModels.get(key);
24
+ if (!limitedAt)
25
+ return false;
26
+ if (Date.now() - limitedAt > this.config.cooldownMs) {
27
+ this.rateLimitedModels.delete(key);
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
+ /**
33
+ * Mark a model as rate limited
34
+ */
35
+ markModelRateLimited(providerID, modelID) {
36
+ const key = getModelKey(providerID, modelID);
37
+ this.rateLimitedModels.set(key, Date.now());
38
+ }
39
+ /**
40
+ * Find the next available model that is not rate limited
41
+ */
42
+ findNextAvailableModel(currentProviderID, currentModelID, attemptedModels) {
43
+ const currentKey = getModelKey(currentProviderID, currentModelID);
44
+ const startIndex = this.config.fallbackModels.findIndex(m => getModelKey(m.providerID, m.modelID) === currentKey);
45
+ // If current model is not in the fallback list (startIndex is -1), start from 0
46
+ const searchStartIndex = Math.max(0, startIndex);
47
+ // Search forward from current position
48
+ for (let i = searchStartIndex + 1; i < this.config.fallbackModels.length; i++) {
49
+ const model = this.config.fallbackModels[i];
50
+ const key = getModelKey(model.providerID, model.modelID);
51
+ if (!attemptedModels.has(key) && !this.isModelRateLimited(model.providerID, model.modelID)) {
52
+ return model;
53
+ }
54
+ }
55
+ // Search from the beginning
56
+ for (let i = 0; i <= searchStartIndex && i < this.config.fallbackModels.length; i++) {
57
+ const model = this.config.fallbackModels[i];
58
+ const key = getModelKey(model.providerID, model.modelID);
59
+ if (!attemptedModels.has(key) && !this.isModelRateLimited(model.providerID, model.modelID)) {
60
+ return model;
61
+ }
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Apply the fallback mode logic
67
+ */
68
+ applyFallbackMode(currentProviderID, currentModelID, attemptedModels) {
69
+ if (this.config.fallbackMode === "cycle") {
70
+ // Reset and retry from the first model
71
+ attemptedModels.clear();
72
+ if (currentProviderID && currentModelID) {
73
+ attemptedModels.add(getModelKey(currentProviderID, currentModelID));
74
+ }
75
+ return this.findNextAvailableModel("", "", attemptedModels);
76
+ }
77
+ else if (this.config.fallbackMode === "retry-last") {
78
+ // Try the last model in the list once, then reset on next prompt
79
+ const lastModel = this.config.fallbackModels[this.config.fallbackModels.length - 1];
80
+ if (lastModel) {
81
+ const isLastModelCurrent = currentProviderID === lastModel.providerID && currentModelID === lastModel.modelID;
82
+ if (!isLastModelCurrent && !this.isModelRateLimited(lastModel.providerID, lastModel.modelID)) {
83
+ // Use the last model for one more try
84
+ safeShowToast(this.client, {
85
+ body: {
86
+ title: "Last Resort",
87
+ message: `Trying ${lastModel.modelID} one more time...`,
88
+ variant: "warning",
89
+ duration: 3000,
90
+ },
91
+ });
92
+ return lastModel;
93
+ }
94
+ else {
95
+ // Last model also failed, reset for next prompt
96
+ attemptedModels.clear();
97
+ if (currentProviderID && currentModelID) {
98
+ attemptedModels.add(getModelKey(currentProviderID, currentModelID));
99
+ }
100
+ return this.findNextAvailableModel("", "", attemptedModels);
101
+ }
102
+ }
103
+ }
104
+ // "stop" mode: return null
105
+ return null;
106
+ }
107
+ /**
108
+ * Select the next fallback model based on current state and fallback mode
109
+ */
110
+ async selectFallbackModel(currentProviderID, currentModelID, attemptedModels) {
111
+ // Mark current model as rate limited and add to attempted
112
+ if (currentProviderID && currentModelID) {
113
+ this.markModelRateLimited(currentProviderID, currentModelID);
114
+ attemptedModels.add(getModelKey(currentProviderID, currentModelID));
115
+ }
116
+ let nextModel = this.findNextAvailableModel(currentProviderID || "", currentModelID || "", attemptedModels);
117
+ // Handle when no model is found based on fallbackMode
118
+ if (!nextModel && attemptedModels.size > 0) {
119
+ nextModel = this.applyFallbackMode(currentProviderID, currentModelID, attemptedModels);
120
+ }
121
+ return nextModel;
122
+ }
123
+ /**
124
+ * Clean up stale rate-limited models
125
+ */
126
+ cleanupStaleEntries() {
127
+ const now = Date.now();
128
+ for (const [key, limitedAt] of this.rateLimitedModels.entries()) {
129
+ if (now - limitedAt > this.config.cooldownMs) {
130
+ this.rateLimitedModels.delete(key);
131
+ }
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Metrics Manager - Handles metrics collection, aggregation, and reporting
3
+ */
4
+ import type { Logger } from '../../logger.js';
5
+ import type { MetricsConfig, MetricsData, RateLimitMetrics, FallbackTargetMetrics, ModelPerformanceMetrics } from '../types/index.js';
6
+ /**
7
+ * Metrics Manager class for collecting and reporting metrics
8
+ */
9
+ export declare class MetricsManager {
10
+ private metrics;
11
+ private config;
12
+ private logger;
13
+ private resetTimer;
14
+ constructor(config: MetricsConfig, logger: Logger);
15
+ /**
16
+ * Start the automatic reset timer
17
+ */
18
+ private startResetTimer;
19
+ /**
20
+ * Reset all metrics data
21
+ */
22
+ reset(): void;
23
+ /**
24
+ * Record a rate limit event
25
+ */
26
+ recordRateLimit(providerID: string, modelID: string): void;
27
+ /**
28
+ * Record the start of a fallback operation
29
+ * @returns timestamp for tracking duration
30
+ */
31
+ recordFallbackStart(): number;
32
+ /**
33
+ * Record a successful fallback operation
34
+ */
35
+ recordFallbackSuccess(targetProviderID: string, targetModelID: string, startTime: number): void;
36
+ /**
37
+ * Record a failed fallback operation
38
+ */
39
+ recordFallbackFailure(): void;
40
+ /**
41
+ * Record a model request
42
+ */
43
+ recordModelRequest(providerID: string, modelID: string): void;
44
+ /**
45
+ * Record a successful model request
46
+ */
47
+ recordModelSuccess(providerID: string, modelID: string, responseTime: number): void;
48
+ /**
49
+ * Record a failed model request
50
+ */
51
+ recordModelFailure(providerID: string, modelID: string): void;
52
+ /**
53
+ * Get a copy of the current metrics
54
+ */
55
+ getMetrics(): MetricsData;
56
+ /**
57
+ * Export metrics in the specified format
58
+ */
59
+ export(format?: "pretty" | "json" | "csv"): string;
60
+ /**
61
+ * Convert metrics to a plain object (converts Maps to Objects)
62
+ */
63
+ private toPlainObject;
64
+ /**
65
+ * Export metrics in pretty-printed text format
66
+ */
67
+ private exportPretty;
68
+ /**
69
+ * Export metrics in CSV format
70
+ */
71
+ private exportCSV;
72
+ /**
73
+ * Report metrics to configured outputs
74
+ */
75
+ report(): Promise<void>;
76
+ /**
77
+ * Clean up resources
78
+ */
79
+ destroy(): void;
80
+ }
81
+ export type { MetricsConfig, MetricsData, RateLimitMetrics, FallbackTargetMetrics, ModelPerformanceMetrics };