Преглед на файлове

fix(session): clear stale provider binding on model mismatch

When a session switches models mid-conversation, the previously bound
provider may not support the new model. Due to SET NX semantics, the
stale binding would persist indefinitely, causing repeated provider
rejection without recovery. Clear the binding on model mismatch so a
new SET NX can rebind to a compatible provider.
ding113 преди 3 дни
родител
ревизия
b83ab2af1f
променени са 2 файла, в които са добавени 27 реда и са изтрити 0 реда
  1. 12 0
      src/app/v1/_lib/proxy/provider-selector.ts
  2. 15 0
      src/lib/session-manager.ts

+ 12 - 0
src/app/v1/_lib/proxy/provider-selector.ts

@@ -558,6 +558,18 @@ export class ProxyProviderResolver {
         requestedModel,
         allowedModels: provider.allowedModels,
       });
+
+      // 清除过时绑定,避免 SET NX 死锁
+      // 当 session 内请求模型发生变化时,旧绑定已无意义,
+      // 清除后新的成功请求可通过 SET NX 重新绑定匹配的 provider
+      await SessionManager.clearSessionProvider(session.sessionId);
+      logger.info("ProviderSelector: Cleared stale provider binding (model mismatch)", {
+        sessionId: session.sessionId,
+        staleProviderId: provider.id,
+        staleProviderName: provider.name,
+        requestedModel,
+      });
+
       return null;
     }
 

+ 15 - 0
src/lib/session-manager.ts

@@ -556,6 +556,21 @@ export class SessionManager {
     return null;
   }
 
+  /**
+   * 清除 session 绑定的 provider(用于跨模型 session 绑定过时时)
+   */
+  static async clearSessionProvider(sessionId: string): Promise<void> {
+    const redis = getRedisClient();
+    if (!redis || redis.status !== "ready") return;
+
+    try {
+      await redis.del(`session:${sessionId}:provider`);
+      logger.trace("SessionManager: Cleared session provider binding", { sessionId });
+    } catch (error) {
+      logger.error("SessionManager: Failed to clear session provider", { error, sessionId });
+    }
+  }
+
   /**
    * 获取当前绑定供应商的优先级
    *