Przeglądaj źródła

add more information for 403 errors

alexchenzl 4 miesięcy temu
rodzic
commit
c872963daf

+ 70 - 0
chrome-extension/src/background/agent/agents/errors.ts

@@ -28,3 +28,73 @@ export class ChatModelAuthError extends Error {
     return `${this.name}: ${this.message}${this.cause ? ` (Caused by: ${this.cause})` : ''}`;
   }
 }
+
+export class ChatModelForbiddenError extends Error {
+  constructor(
+    message: string,
+    public readonly cause?: unknown,
+  ) {
+    super(message);
+    this.name = 'ChatModelForbiddenError';
+
+    if (Error.captureStackTrace) {
+      Error.captureStackTrace(this, ChatModelForbiddenError);
+    }
+  }
+
+  /**
+   * Returns a string representation of the error
+   */
+  toString(): string {
+    return `${this.name}: ${this.message}${this.cause ? ` (Caused by: ${this.cause})` : ''}`;
+  }
+}
+
+export const LLM_FORBIDDEN_ERROR_MESSAGE =
+  'Access denied (403 Forbidden). Please check:\n\n1. Your API key has the required permissions\n\n2. For Ollama: Set OLLAMA_ORIGINS=chrome-extension://* \nsee https://github.com/ollama/ollama/blob/main/docs/faq.md';
+
+/**
+ * Checks if an error is related to API authentication
+ *
+ * @param error - The error to check
+ * @returns boolean indicating if it's an authentication error
+ */
+export function isAuthenticationError(error: unknown): boolean {
+  if (!(error instanceof Error)) return false;
+
+  // Get the error message
+  const errorMessage = error.message || '';
+
+  // Get error name - sometimes error.name just returns "Error" for custom errors
+  let errorName = error.name || '';
+
+  // Try to extract the constructor name, which often contains the actual error type
+  // This works better than error.name for many custom errors
+  const constructorName = error.constructor?.name;
+  if (constructorName && constructorName !== 'Error') {
+    errorName = constructorName;
+  }
+
+  // Check if the error name indicates an authentication error
+  if (errorName === 'AuthenticationError') {
+    return true;
+  }
+
+  // Fallback: check the message for authentication-related indicators
+  return (
+    errorMessage.toLowerCase().includes('authentication') ||
+    errorMessage.includes(' 401') ||
+    errorMessage.toLowerCase().includes('api key')
+  );
+}
+
+/**
+ * Checks if an error is related 403 Forbidden
+ *
+ * @param error - The error to check
+ * @returns boolean indicating if it's an 403 Forbidden error
+ */
+export function isForbiddenError(error: unknown): boolean {
+  if (!(error instanceof Error)) return false;
+  return error.message.includes(' 403') && error.message.includes('Forbidden');
+}

+ 10 - 2
chrome-extension/src/background/agent/agents/navigator.ts

@@ -7,8 +7,13 @@ import { buildDynamicActionSchema } from '../actions/builder';
 import { agentBrainSchema } from '../types';
 import { type BaseMessage, HumanMessage } from '@langchain/core/messages';
 import { Actors, ExecutionState } from '../event/types';
-import { isAuthenticationError } from '@src/background/utils';
-import { ChatModelAuthError } from './errors';
+import {
+  ChatModelAuthError,
+  ChatModelForbiddenError,
+  isAuthenticationError,
+  isForbiddenError,
+  LLM_FORBIDDEN_ERROR_MESSAGE,
+} from './errors';
 import { jsonNavigatorOutputSchema } from '../actions/json_schema';
 import { geminiNavigatorOutputSchema } from '../actions/json_gemini';
 import { calcBranchPathHashSet } from '@src/background/dom/views';
@@ -168,6 +173,9 @@ export class NavigatorAgent extends BaseAgent<z.ZodType, NavigatorResult> {
       if (isAuthenticationError(error)) {
         throw new ChatModelAuthError('Navigator API Authentication failed. Please verify your API key', error);
       }
+      if (isForbiddenError(error)) {
+        throw new ChatModelForbiddenError(LLM_FORBIDDEN_ERROR_MESSAGE, error);
+      }
 
       const errorMessage = error instanceof Error ? error.message : String(error);
       const errorString = `Navigation failed: ${errorMessage}`;

+ 10 - 2
chrome-extension/src/background/agent/agents/planner.ts

@@ -4,8 +4,13 @@ import { z } from 'zod';
 import type { AgentOutput } from '../types';
 import { HumanMessage } from '@langchain/core/messages';
 import { Actors, ExecutionState } from '../event/types';
-import { isAuthenticationError } from '@src/background/utils';
-import { ChatModelAuthError } from './errors';
+import {
+  ChatModelAuthError,
+  ChatModelForbiddenError,
+  isAuthenticationError,
+  isForbiddenError,
+  LLM_FORBIDDEN_ERROR_MESSAGE,
+} from './errors';
 const logger = createLogger('PlannerAgent');
 
 // Define Zod schema for planner output
@@ -81,6 +86,9 @@ export class PlannerAgent extends BaseAgent<typeof plannerOutputSchema, PlannerO
       if (isAuthenticationError(error)) {
         throw new ChatModelAuthError('Planner API Authentication failed. Please verify your API key', error);
       }
+      if (isForbiddenError(error)) {
+        throw new ChatModelForbiddenError(LLM_FORBIDDEN_ERROR_MESSAGE, error);
+      }
       const errorMessage = error instanceof Error ? error.message : String(error);
       this.context.emitEvent(Actors.PLANNER, ExecutionState.STEP_FAIL, `Planning failed: ${errorMessage}`);
       return {

+ 10 - 2
chrome-extension/src/background/agent/agents/validator.ts

@@ -4,8 +4,13 @@ import { z } from 'zod';
 import { ActionResult, type AgentOutput } from '../types';
 import { Actors, ExecutionState } from '../event/types';
 import { HumanMessage } from '@langchain/core/messages';
-import { isAuthenticationError } from '@src/background/utils';
-import { ChatModelAuthError } from './errors';
+import {
+  ChatModelAuthError,
+  ChatModelForbiddenError,
+  isAuthenticationError,
+  isForbiddenError,
+  LLM_FORBIDDEN_ERROR_MESSAGE,
+} from './errors';
 const logger = createLogger('ValidatorAgent');
 
 // Define Zod schema for validator output
@@ -81,6 +86,9 @@ export class ValidatorAgent extends BaseAgent<typeof validatorOutputSchema, Vali
       if (isAuthenticationError(error)) {
         throw new ChatModelAuthError('Validator API Authentication failed. Please verify your API key', error);
       }
+      if (isForbiddenError(error)) {
+        throw new ChatModelForbiddenError(LLM_FORBIDDEN_ERROR_MESSAGE, error);
+      }
 
       const errorMessage = error instanceof Error ? error.message : String(error);
       logger.error(`Validation failed: ${errorMessage}`);

+ 2 - 2
chrome-extension/src/background/agent/executor.ts

@@ -12,7 +12,7 @@ import type BrowserContext from '../browser/context';
 import { ActionBuilder } from './actions/builder';
 import { EventManager } from './event/manager';
 import { Actors, type EventCallback, EventType, ExecutionState } from './event/types';
-import { ChatModelAuthError } from './agents/errors';
+import { ChatModelAuthError, ChatModelForbiddenError } from './agents/errors';
 const logger = createLogger('Executor');
 
 export interface ExecutorExtraArgs {
@@ -223,7 +223,7 @@ export class Executor {
         return true;
       }
     } catch (error) {
-      if (error instanceof ChatModelAuthError) {
+      if (error instanceof ChatModelAuthError || error instanceof ChatModelForbiddenError) {
         throw error;
       }
       context.consecutiveFailures++;

+ 0 - 35
chrome-extension/src/background/utils.ts

@@ -17,38 +17,3 @@ export function getCurrentTimestampStr(): string {
     })
     .replace(',', '');
 }
-
-/**
- * Checks if an error is related to API authentication
- *
- * @param error - The error to check
- * @returns boolean indicating if it's an authentication error
- */
-export function isAuthenticationError(error: unknown): boolean {
-  if (!(error instanceof Error)) return false;
-
-  // Get the error message
-  const errorMessage = error.message || '';
-
-  // Get error name - sometimes error.name just returns "Error" for custom errors
-  let errorName = error.name || '';
-
-  // Try to extract the constructor name, which often contains the actual error type
-  // This works better than error.name for many custom errors
-  const constructorName = error.constructor?.name;
-  if (constructorName && constructorName !== 'Error') {
-    errorName = constructorName;
-  }
-
-  // Check if the error name indicates an authentication error
-  if (errorName === 'AuthenticationError') {
-    return true;
-  }
-
-  // Fallback: check the message for authentication-related indicators
-  return (
-    errorMessage.toLowerCase().includes('authentication') ||
-    errorMessage.includes('401') ||
-    errorMessage.toLowerCase().includes('api key')
-  );
-}