Skip to content

Custom Provider

Build your own AI provider adapter to connect DevDiff to any LLM API — Azure OpenAI, AWS Bedrock, Google Gemini, a self-hosted model, or any OpenAI-compatible endpoint.


OpenAI-Compatible Endpoints

If your provider is OpenAI-compatible (uses the same API format), use the openai-compatible adapter:

javascript
// .devdiff.config.js
export default {
  ai: {
    providers: [
      {
        name: "azure-openai",
        url: "openai-compatible://your-deployment-name",
        baseURL: "https://your-resource.openai.azure.com/openai/deployments/",
        apiKey: process.env.AZURE_OPENAI_KEY,
        apiVersion: "2024-02-15-preview",
        priority: 1,
      },
    ],
  },
};

Works with:

  • Azure OpenAI Service
  • AWS Bedrock (via OpenAI-compatible proxy)
  • LM Studio (http://localhost:1234/v1)
  • LocalAI (http://localhost:8080/v1)
  • vLLM servers
  • Any OpenAI-compatible API

LM Studio Example

LM Studio runs models locally with an OpenAI-compatible API:

javascript
export default {
  ai: {
    providers: [
      {
        name: "lmstudio",
        url: "openai-compatible://local-model",
        baseURL: "http://localhost:1234/v1",
        apiKey: "lm-studio", // LM Studio accepts any key
        priority: 1,
      },
    ],
  },
};

Azure OpenAI Example

bash
# Set environment variables
export AZURE_OPENAI_KEY="your-azure-key"
export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com"
javascript
// .devdiff.config.js
export default {
  ai: {
    providers: [
      {
        name: "azure-gpt4",
        url: "openai-compatible://gpt-4o-deployment",
        baseURL: `${process.env.AZURE_OPENAI_ENDPOINT}/openai/deployments/`,
        apiKey: process.env.AZURE_OPENAI_KEY,
        apiVersion: "2024-02-15-preview",
        priority: 1,
      },
    ],
  },
};

Writing a Full Custom Provider

For providers that don't follow the OpenAI API format, you can write a full adapter:

javascript
// my-provider.js
export default {
  name: "my-custom-llm",

  /**
   * Called once when DevDiff starts
   */
  async initialize(config) {
    this.baseURL = config.baseURL || "http://localhost:8080";
    this.model = config.model || "default";
  },

  /**
   * Called for each diff analysis request
   * @param {string} prompt - The formatted diff + instruction prompt
   * @returns {string} - The AI-generated changelog text
   */
  async generate(prompt) {
    const response = await fetch(`${this.baseURL}/generate`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        model: this.model,
        prompt: prompt,
        stream: false,
      }),
    });

    if (!response.ok) {
      throw new Error(
        `Provider error: ${response.status} ${response.statusText}`,
      );
    }

    const data = await response.json();
    return data.response || data.content || data.text;
  },

  /**
   * Optional: health check
   * @returns {boolean}
   */
  async isAvailable() {
    try {
      const res = await fetch(`${this.baseURL}/health`);
      return res.ok;
    } catch {
      return false;
    }
  },
};

Register it in your config:

javascript
// .devdiff.config.js
import myProvider from "./my-provider.js";

export default {
  ai: {
    providers: [
      {
        name: "my-custom",
        adapter: myProvider,
        baseURL: "http://my-llm-server:8080",
        model: "my-model-name",
        priority: 1,
      },
    ],
  },
};

Google Gemini Example

bash
export GEMINI_API_KEY="your-gemini-key"
javascript
// gemini-provider.js
export default {
  name: "gemini",

  async initialize(config) {
    this.apiKey = config.apiKey || process.env.GEMINI_API_KEY;
    this.model = config.model || "gemini-1.5-flash";
  },

  async generate(prompt) {
    const response = await fetch(
      `https://generativelanguage.googleapis.com/v1beta/models/${this.model}:generateContent?key=${this.apiKey}`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          contents: [{ parts: [{ text: prompt }] }],
        }),
      },
    );
    const data = await response.json();
    return data.candidates[0].content.parts[0].text;
  },
};

Provider Interface Reference

typescript
interface DevDiffProvider {
  name: string;

  // Required
  initialize(config: ProviderConfig): Promise<void>;
  generate(prompt: string): Promise<string>;

  // Optional
  isAvailable?(): Promise<boolean>;
  onError?(error: Error): Promise<void>;
}

interface ProviderConfig {
  apiKey?: string;
  baseURL?: string;
  model?: string;
  timeout?: number;
  maxTokens?: number;
  [key: string]: unknown;
}