From 0e42ca5ca2aa8ff06b216ba73ea53e1e99234f74 Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 12 Feb 2026 13:09:38 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20improve=20GitHub=20Copilo?= =?UTF-8?q?t=20auth=20retry=20logic=20(#12250)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 fix: improve GitHub Copilot auth retry logic Simplify auth refresh tracking from counter to boolean flag and clear cached bearer token on 401 to ensure fresh token exchange. * 🔧 fix: update package.json formatting and import statements in GitHub Copilot provider Signed-off-by: Innei * 🐛 fix: refine GitHub Copilot auth refresh logic to check for exchange credential Update the 401 error handling to refresh the token only if an exchange credential is available, improving the authentication flow. Signed-off-by: Innei --------- Signed-off-by: Innei --- package.json | 12 ++++++------ .../src/providers/githubCopilot/index.ts | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 2f1c2a52b9..1d440bff2b 100644 --- a/package.json +++ b/package.json @@ -132,18 +132,18 @@ "prettier --write --no-error-on-unmatched-pattern" ], "*.{mjs,cjs}": [ - "prettier --write", - "eslint --fix" + "eslint --fix", + "prettier --write" ], "*.{js,jsx}": [ - "prettier --write", + "eslint --fix", "stylelint --fix", - "eslint --fix" + "prettier --write" ], "*.{ts,tsx}": [ - "prettier --parser=typescript --write", "stylelint --fix", - "eslint --fix" + "eslint --fix", + "prettier --parser=typescript --write" ] }, "overrides": { diff --git a/packages/model-runtime/src/providers/githubCopilot/index.ts b/packages/model-runtime/src/providers/githubCopilot/index.ts index d9ff855a51..6c34459727 100644 --- a/packages/model-runtime/src/providers/githubCopilot/index.ts +++ b/packages/model-runtime/src/providers/githubCopilot/index.ts @@ -1,11 +1,11 @@ -import type { ChatModelCard } from '@lobechat/types'; +import { type ChatModelCard } from '@lobechat/types'; import { ModelProvider } from 'model-bank'; import OpenAI from 'openai'; -import type { LobeRuntimeAI } from '../../core/BaseAI'; +import { type LobeRuntimeAI } from '../../core/BaseAI'; import { pruneReasoningPayload } from '../../core/contextBuilders/openai'; import { OpenAIStream } from '../../core/streams'; -import type { ChatMethodOptions, ChatStreamPayload } from '../../types'; +import { type ChatMethodOptions, type ChatStreamPayload } from '../../types'; import { AgentRuntimeErrorType } from '../../types/error'; import { AgentRuntimeError } from '../../utils/createError'; import { StreamingResponse } from '../../utils/response'; @@ -14,7 +14,6 @@ const COPILOT_BASE_URL = 'https://api.githubcopilot.com'; const TOKEN_EXCHANGE_URL = 'https://api.github.com/copilot_internal/v2/token'; const MAX_TOTAL_ATTEMPTS = 5; -const MAX_AUTH_REFRESH = 1; const MAX_RATE_LIMIT_RETRIES = 3; interface CachedToken { @@ -227,7 +226,7 @@ export class LobeGithubCopilotAI implements LobeRuntimeAI { private async executeWithRetry(fn: () => Promise): Promise { let totalAttempts = 0; - let authRefreshAttempts = 0; + let hasRefreshedAuth = false; let rateLimitAttempts = 0; while (totalAttempts < MAX_TOTAL_ATTEMPTS) { @@ -238,9 +237,10 @@ export class LobeGithubCopilotAI implements LobeRuntimeAI { } catch (error: any) { const status = error?.status ?? error?.error?.status ?? error?.response?.status; - // 401: Refresh token once - if (status === 401 && authRefreshAttempts < MAX_AUTH_REFRESH) { - authRefreshAttempts++; + // 401: Refresh token once (only if we have an exchange credential to fall back to) + if (status === 401 && !hasRefreshedAuth && this.githubToken) { + hasRefreshedAuth = true; + this.cachedBearerToken = undefined; tokenManager.invalidate(this.githubToken); continue; }