Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/manusapis/Agix/llms.txt

Use this file to discover all available pages before exploring further.

Agix abstracts every AI model behind the IAIProvider interface. Adding support for a new provider — whether it is Google Gemini, a self-hosted Ollama instance, or any other model API — involves four focused changes spread across four files. No call sites in the React UI or service layer need to change.

The IAIProvider contract

Every provider must satisfy the following interface, defined in src/services/ai/base.provider.ts:
export interface IAIProvider {
  readonly id: AIProviderConfig["id"];
  readonly name: string;

  isConfigured(): boolean;
  chat(request: ChatRequest): Promise<ChatResponse>;
}
isConfigured() is already implemented by BaseAIProvider — it returns true when both apiKey and baseUrl are non-empty strings. Concrete providers only need to implement chat() along with the id and name getters.
Always call isConfigured() before calling chat(). The ChatPanel component does this automatically, but if you invoke a provider directly from another service, an unconfigured provider will produce an authorization error from the remote API rather than a clear local message.

Steps

1
Extend the ProviderId union type
2
Open src/types/ai.types.ts and add your new identifier to the ProviderId union. This is the canonical string key used everywhere — in the config registry, the factory switch, and persisted settings.
3
// src/types/ai.types.ts
export type ProviderId = "openai" | "openrouter" | "claude" | "custom" | "gemini";
4
TypeScript’s exhaustiveness checking will now flag any switch statement on ProviderId that does not handle the new case, which makes it easy to find every place that needs updating.
5
Implement IAIProvider
6
Create a new file at src/services/ai/gemini.provider.ts. Extend BaseAIProvider so you inherit the constructor, the config field, and the default isConfigured() implementation, then implement id, name, and chat().
7
// src/services/ai/gemini.provider.ts
import { BaseAIProvider } from "./base.provider";
import type { ChatRequest, ChatResponse } from "@/types/ai.types";

export class GeminiProvider extends BaseAIProvider {
  get id() {
    return this.config.id;
  }

  get name() {
    return this.config.name;
  }

  async chat(request: ChatRequest): Promise<ChatResponse> {
    const res = await fetch(`${this.config.baseUrl}/generateContent`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${this.config.apiKey}`,
      },
      body: JSON.stringify({ contents: request.messages }),
    });

    if (!res.ok) {
      throw new Error(`Gemini request failed: ${res.status} ${await res.text()}`);
    }

    const data = await res.json();
    return {
      provider: this.id,
      model: request.model,
      content: data.candidates[0].content.parts[0].text,
      finishReason: data.candidates[0].finishReason,
    };
  }
}
8
The ChatResponse shape — provider, model, content, and finishReason — is required. The optional usage field can be populated if the API returns token counts.
9
Register in provider.factory.ts
10
Open src/services/ai/provider.factory.ts and add an import and a case to the createProvider() switch:
11
// src/services/ai/provider.factory.ts
import { GeminiProvider } from "./gemini.provider";

export function createProvider(config: AIProviderConfig): IAIProvider {
  switch (config.id) {
    case "openai":
    case "openrouter":
    case "custom":
      return new OpenAIProvider(config);
    case "claude":
      return new ClaudeProvider(config);
    case "gemini":
      return new GeminiProvider(config);
    default:
      throw new Error(`Unknown AI provider: ${(config as { id: ProviderId }).id}`);
  }
}
12
This is the only place that needs to know which class backs which provider ID. All other code goes through IAIProvider.
13
Add to DEFAULT_PROVIDERS in providers.config.ts
14
Open src/config/providers.config.ts and add an entry for the new provider. The apiKey is always left blank here — keys are entered at runtime via the sidebar and persisted by settingsStore.
15
// src/config/providers.config.ts
export const DEFAULT_PROVIDERS: Record<ProviderId, AIProviderConfig> = {
  // … existing entries …
  gemini: {
    id: "gemini",
    name: "Google Gemini",
    apiKey: "",
    baseUrl: "https://generativelanguage.googleapis.com/v1beta",
    defaultModel: "gemini-1.5-flash",
    enabled: true,
  },
};
16
Once this entry exists, getProviderConfig("gemini") returns the defaults, and the ProviderSelector component automatically surfaces the new provider in the sidebar dropdown.

Existing providers for reference

The two shipped providers illustrate the two main API shapes you are likely to encounter:
// Covers OpenAI, OpenRouter, and any /chat/completions-compatible endpoint.
async chat(request: ChatRequest): Promise<ChatResponse> {
  const res = await fetch(`${this.config.baseUrl}/chat/completions`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${this.config.apiKey}`,
    },
    body: JSON.stringify({
      model: request.model,
      messages: request.messages,
      temperature: request.temperature ?? 0.7,
      max_tokens: request.maxTokens,
      stream: false,
    }),
  });

  if (!res.ok) {
    throw new Error(`OpenAI request failed: ${res.status} ${await res.text()}`);
  }

  const data = await res.json();
  const choice = data.choices?.[0];
  return {
    provider: this.id,
    model: request.model,
    content: choice?.message?.content ?? "",
    finishReason: choice?.finish_reason,
    usage: data.usage
      ? {
          promptTokens: data.usage.prompt_tokens,
          completionTokens: data.usage.completion_tokens,
          totalTokens: data.usage.total_tokens,
        }
      : undefined,
  };
}

Files changed summary

FileChange
src/types/ai.types.tsAdd the new ID to the ProviderId union
src/services/ai/<name>.provider.tsNew file — implements IAIProvider
src/services/ai/provider.factory.tsOne new case in createProvider()
src/config/providers.config.tsOne new entry in DEFAULT_PROVIDERS

Build docs developers (and LLMs) love