Skip to content

AI provider interface contract violations causing runtime failures #12

@aaron-seq

Description

@aaron-seq

Problem Description

The AI provider orchestrator expects all provider implementations to support a consistent interface with methods for translation and sentiment analysis. However, several providers only implement stub methods for generateSummary and answerQuestion, leading to runtime errors when the orchestrator attempts to call missing methods.

Current Provider Support Matrix

Provider generateSummary answerQuestion translateText analyzeSentiment
OpenAI ✅ Full ✅ Full ✅ Full ✅ Full
ChromeAI ⚠️ Stub ⚠️ Stub ❌ Missing ❌ Missing
Anthropic ⚠️ Stub ⚠️ Stub ❌ Missing ❌ Missing
Gemini ⚠️ Stub ⚠️ Stub ❌ Missing ❌ Missing
Cohere ⚠️ Stub ⚠️ Stub ❌ Missing ❌ Missing

Technical Impact

Orchestrator Method Calls

In core/ai-provider-orchestrator.js, the orchestrator calls:

const selectedProvider = this.loadBalancer.selectProvider(availableProviders);

However, when background.js attempts operations like:

const translation = await aiProvider.translateText(content, options);
const sentiment = await aiProvider.analyzeSentiment(text, options);

These calls will fail with TypeError: aiProvider.translateText is not a function for non-OpenAI providers.

Root Cause Analysis

Missing Interface Definition

The codebase lacks a formal interface or abstract base class defining the required provider contract. Each provider should implement:

interface AIProvider {
  name: string;
  displayName: string;
  
  // Core methods
  isAvailable(): Promise<boolean>;
  
  // Generation methods
  generateSummary(content: string, options: SummaryOptions): Promise<SummaryResult>;
  answerQuestion(question: string, options: QuestionOptions): Promise<AnswerResult>;
  
  // Language methods
  translateText(text: string, options: TranslationOptions): Promise<TranslationResult>;
  
  // Analysis methods
  analyzeSentiment(text: string, options: AnalysisOptions): Promise<SentimentResult>;
  extractKeywords(text: string, options: ExtractionOptions): Promise<KeywordResult>;
  generateTags(content: string, options: TagOptions): Promise<TagResult>;
}

Provider-Specific Issues

ChromeAI Provider (providers/chrome-ai-provider.js)

  • Lines 14-17: generateSummary returns plain string instead of structured object
  • Lines 23-29: answerQuestion returns object but with hardcoded stub values
  • Missing Methods: translateText, analyzeSentiment, extractKeywords, generateTags

Anthropic Provider (providers/anthropic-provider.js)

  • Only contains stub class structure
  • All methods missing or returning stub values
  • No API integration implemented

Gemini Provider (providers/gemini-provider.js)

  • Only contains stub class structure
  • All methods missing or returning stub values
  • No API integration implemented

Cohere Provider (providers/cohere-provider.js)

  • Only contains stub class structure
  • All methods missing or returning stub values
  • No API integration implemented

Recommended Solutions

Solution 1: Abstract Base Class (Preferred)

Create providers/base-provider.js:

export class BaseAIProvider {
  constructor(name, displayName) {
    if (this.constructor === BaseAIProvider) {
      throw new Error('Cannot instantiate abstract class');
    }
    this.name = name;
    this.displayName = displayName;
  }

  async isAvailable() {
    throw new Error('Method isAvailable must be implemented');
  }

  async generateSummary(content, options) {
    throw new Error('Method generateSummary must be implemented');
  }

  async answerQuestion(question, options) {
    throw new Error('Method answerQuestion must be implemented');
  }

  async translateText(text, options) {
    throw new Error('Method translateText must be implemented');
  }

  async analyzeSentiment(text, options) {
    throw new Error('Method analyzeSentiment must be implemented');
  }

  // Optional methods with default implementations
  async extractKeywords(text, options) {
    // Default implementation using generateSummary
    const summary = await this.generateSummary(text, {
      type: 'key-points',
      ...options
    });
    return this.parseKeywordsFromSummary(summary);
  }

  parseKeywordsFromSummary(summary) {
    // Basic keyword extraction logic
    return [];
  }
}

Solution 2: Provider Capability Detection

Modify AIProviderOrchestrator to detect and respect provider capabilities:

async getOptimalProvider(task = 'general') {
  const availableProviders = Array.from(this.providers.values())
    .filter(provider => {
      // Check if provider is healthy
      if (!this.isProviderHealthy(provider.name)) return false;
      
      // Check if provider supports the requested task
      const taskMethodMap = {
        'translation': 'translateText',
        'sentiment-analysis': 'analyzeSentiment',
        'summarization': 'generateSummary',
        'question-answering': 'answerQuestion'
      };
      
      const requiredMethod = taskMethodMap[task];
      if (requiredMethod && typeof provider[requiredMethod] !== 'function') {
        return false;
      }
      
      return true;
    })
    .sort((a, b) => this.calculateProviderScore(b, task) - this.calculateProviderScore(a, task));

  if (availableProviders.length === 0) {
    throw new Error(`No providers available for task: ${task}`);
  }

  return this.loadBalancer.selectProvider(availableProviders);
}

Solution 3: Fallback Implementation

Implement fallback methods in stub providers using basic prompt engineering:

// For ChromeAI Provider
async translateText(text, options) {
  const { targetLanguage = 'English', sourceLanguage = 'auto-detect' } = options;
  const prompt = `Translate the following text from ${sourceLanguage} to ${targetLanguage}:\n\n${text}`;
  
  // Use the built-in Chrome AI with custom prompt
  const result = await this.generateSummary(prompt, {
    type: 'tldr',
    length: 'medium'
  });
  
  return {
    text: result,
    detectedLanguage: sourceLanguage,
    confidence: 0.8
  };
}

async analyzeSentiment(text, options) {
  const prompt = `Analyze the sentiment of this text as positive, negative, or neutral. Provide confidence score:\n\n${text}`;
  
  const result = await this.generateSummary(prompt, {
    type: 'key-points',
    length: 'short'
  });
  
  // Parse result for sentiment indicators
  const sentiment = this.parseSentimentFromResponse(result);
  
  return {
    sentiment: sentiment.type,
    confidence: sentiment.confidence,
    reasoning: result
  };
}

parseSentimentFromResponse(response) {
  const lowerResponse = response.toLowerCase();
  if (lowerResponse.includes('positive')) {
    return { type: 'positive', confidence: 0.85 };
  } else if (lowerResponse.includes('negative')) {
    return { type: 'negative', confidence: 0.85 };
  }
  return { type: 'neutral', confidence: 0.7 };
}

Implementation Priority

  1. High Priority: Implement Solution 2 (capability detection) to prevent runtime errors
  2. Medium Priority: Implement Solution 1 (base class) for long-term maintainability
  3. Medium Priority: Implement Solution 3 for ChromeAI provider (most commonly available)
  4. Low Priority: Full implementations for Anthropic, Gemini, and Cohere providers

Testing Requirements

  • Test orchestrator with task-specific provider selection
  • Verify graceful degradation when no provider supports a task
  • Test ChromeAI fallback implementations with various text inputs
  • Validate error messages are user-friendly when providers lack capabilities
  • Test provider switching when primary provider fails

Breaking Changes

Implementing Solution 1 will require all provider classes to extend BaseAIProvider, which may break existing code that directly instantiates providers without proper inheritance.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions