* feat: apply local user changes and fixes * ;D * Implement OpenAI support, model-specific rate limiting, and robustness fixes * docs: update pr title * feat: ensure unique openai models endpoint * fix: startup banner alignment and removed duplicates * feat: add model fallback system with --fallback flag * fix: accounts cli hanging after completion * feat: add exit option to accounts cli menu * fix: remove circular dependency warning for fallback flag * feat: show active modes in banner and hide their flags * Remove OpenAI compatibility and fallback features from PR #35 Cherry-picked selective fixes from PR #35 while removing: - OpenAI-compatible API endpoints (/openai/v1/*) - Model fallback system (fallback-config.js) - Thinking block skip for Gemini models - Unnecessary files (pullrequest.md, test-fix.js, test-openai.js) Retained improvements: - Network error handling with retry logic - Model-specific rate limiting - Enhanced health check with quota info - CLI fixes (exit option, process.exit) - Startup banner alignment (debug mode only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * banner alignment fix * Refactor: Model-specific rate limits and cleanup deprecated code - Remove global rate limit fields (isRateLimited, rateLimitResetTime) in favor of model-specific limits (modelRateLimits[modelId]) - Remove deprecated wrapper functions (is429Error, isAuthInvalidError) from handlers - Filter fetchAvailableModels to only return Claude and Gemini models - Fix getCurrentStickyAccount() to pass model param after waiting - Update /account-limits endpoint to show model-specific limits - Remove multi-account OAuth flow to avoid state mismatch errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: show (x/y) limited status in account-limits table - Status is now "ok" only when all models are available - Shows "(x/y) limited" when x out of y models are exhausted - Provides better visibility into partial rate limiting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: update CLAUDE.md with model-specific rate limiting - Document modelRateLimits[modelId] for per-model rate tracking - Add isNetworkError() helper to utilities section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: M1noa <minoa@minoa.cat> Co-authored-by: Minoa <altgithub@minoa.cat> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
2d05dd5b62
commit
9c4a712a9a
@@ -81,18 +81,20 @@ export class AccountManager {
|
||||
|
||||
/**
|
||||
* Check if all accounts are rate-limited
|
||||
* @param {string} [modelId] - Optional model ID
|
||||
* @returns {boolean} True if all accounts are rate-limited
|
||||
*/
|
||||
isAllRateLimited() {
|
||||
return checkAllRateLimited(this.#accounts);
|
||||
isAllRateLimited(modelId = null) {
|
||||
return checkAllRateLimited(this.#accounts, modelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of available (non-rate-limited, non-invalid) accounts
|
||||
* @param {string} [modelId] - Optional model ID
|
||||
* @returns {Array<Object>} Array of available account objects
|
||||
*/
|
||||
getAvailableAccounts() {
|
||||
return getAvailable(this.#accounts);
|
||||
getAvailableAccounts(modelId = null) {
|
||||
return getAvailable(this.#accounts, modelId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,10 +129,11 @@ export class AccountManager {
|
||||
/**
|
||||
* Pick the next available account (fallback when current is unavailable).
|
||||
* Sets activeIndex to the selected account's index.
|
||||
* @param {string} [modelId] - Optional model ID
|
||||
* @returns {Object|null} The next available account or null if none available
|
||||
*/
|
||||
pickNext() {
|
||||
const { account, newIndex } = selectNext(this.#accounts, this.#currentIndex, () => this.saveToDisk());
|
||||
pickNext(modelId = null) {
|
||||
const { account, newIndex } = selectNext(this.#accounts, this.#currentIndex, () => this.saveToDisk(), modelId);
|
||||
this.#currentIndex = newIndex;
|
||||
return account;
|
||||
}
|
||||
@@ -138,10 +141,11 @@ export class AccountManager {
|
||||
/**
|
||||
* Get the current account without advancing the index (sticky selection).
|
||||
* Used for cache continuity - sticks to the same account until rate-limited.
|
||||
* @param {string} [modelId] - Optional model ID
|
||||
* @returns {Object|null} The current account or null if unavailable/rate-limited
|
||||
*/
|
||||
getCurrentStickyAccount() {
|
||||
const { account, newIndex } = getSticky(this.#accounts, this.#currentIndex, () => this.saveToDisk());
|
||||
getCurrentStickyAccount(modelId = null) {
|
||||
const { account, newIndex } = getSticky(this.#accounts, this.#currentIndex, () => this.saveToDisk(), modelId);
|
||||
this.#currentIndex = newIndex;
|
||||
return account;
|
||||
}
|
||||
@@ -149,10 +153,11 @@ export class AccountManager {
|
||||
/**
|
||||
* Check if we should wait for the current account's rate limit to reset.
|
||||
* Used for sticky account selection - wait if rate limit is short (≤ threshold).
|
||||
* @param {string} [modelId] - Optional model ID
|
||||
* @returns {{shouldWait: boolean, waitMs: number, account: Object|null}}
|
||||
*/
|
||||
shouldWaitForCurrentAccount() {
|
||||
return shouldWait(this.#accounts, this.#currentIndex);
|
||||
shouldWaitForCurrentAccount(modelId = null) {
|
||||
return shouldWait(this.#accounts, this.#currentIndex, modelId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,10 +165,11 @@ export class AccountManager {
|
||||
* Prefers the current account for cache continuity, only switches when:
|
||||
* - Current account is rate-limited for > 2 minutes
|
||||
* - Current account is invalid
|
||||
* @param {string} [modelId] - Optional model ID
|
||||
* @returns {{account: Object|null, waitMs: number}} Account to use and optional wait time
|
||||
*/
|
||||
pickStickyAccount() {
|
||||
const { account, waitMs, newIndex } = selectSticky(this.#accounts, this.#currentIndex, () => this.saveToDisk());
|
||||
pickStickyAccount(modelId = null) {
|
||||
const { account, waitMs, newIndex } = selectSticky(this.#accounts, this.#currentIndex, () => this.saveToDisk(), modelId);
|
||||
this.#currentIndex = newIndex;
|
||||
return { account, waitMs };
|
||||
}
|
||||
@@ -172,9 +178,10 @@ export class AccountManager {
|
||||
* Mark an account as rate-limited
|
||||
* @param {string} email - Email of the account to mark
|
||||
* @param {number|null} resetMs - Time in ms until rate limit resets (optional)
|
||||
* @param {string} [modelId] - Optional model ID to mark specific limit
|
||||
*/
|
||||
markRateLimited(email, resetMs = null) {
|
||||
markLimited(this.#accounts, email, resetMs, this.#settings);
|
||||
markRateLimited(email, resetMs = null, modelId = null) {
|
||||
markLimited(this.#accounts, email, resetMs, this.#settings, modelId);
|
||||
this.saveToDisk();
|
||||
}
|
||||
|
||||
@@ -190,10 +197,11 @@ export class AccountManager {
|
||||
|
||||
/**
|
||||
* Get the minimum wait time until any account becomes available
|
||||
* @param {string} [modelId] - Optional model ID
|
||||
* @returns {number} Wait time in milliseconds
|
||||
*/
|
||||
getMinWaitTimeMs() {
|
||||
return getMinWait(this.#accounts);
|
||||
getMinWaitTimeMs(modelId = null) {
|
||||
return getMinWait(this.#accounts, modelId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,9 +259,16 @@ export class AccountManager {
|
||||
*/
|
||||
getStatus() {
|
||||
const available = this.getAvailableAccounts();
|
||||
const rateLimited = this.#accounts.filter(a => a.isRateLimited);
|
||||
const invalid = this.getInvalidAccounts();
|
||||
|
||||
// Count accounts that have any active model-specific rate limits
|
||||
const rateLimited = this.#accounts.filter(a => {
|
||||
if (!a.modelRateLimits) return false;
|
||||
return Object.values(a.modelRateLimits).some(
|
||||
limit => limit.isRateLimited && limit.resetTime > Date.now()
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
total: this.#accounts.length,
|
||||
available: available.length,
|
||||
@@ -263,8 +278,7 @@ export class AccountManager {
|
||||
accounts: this.#accounts.map(a => ({
|
||||
email: a.email,
|
||||
source: a.source,
|
||||
isRateLimited: a.isRateLimited,
|
||||
rateLimitResetTime: a.rateLimitResetTime,
|
||||
modelRateLimits: a.modelRateLimits || {},
|
||||
isInvalid: a.isInvalid || false,
|
||||
invalidReason: a.invalidReason || null,
|
||||
lastUsed: a.lastUsed
|
||||
|
||||
Reference in New Issue
Block a user