Skip to content

Custom Providers ​

All providers in Meloqui implement the ProviderPlugin interface, including the built-in OpenAI, Anthropic, Google, and Ollama providers. You can create your own custom providers following the same pattern.

The ProviderPlugin Interface ​

typescript
import { ProviderPlugin, ProviderCapabilities } from 'meloqui';

interface ProviderPlugin {
  readonly name: string;
  readonly capabilities: ProviderCapabilities;

  chat(messages: Message[], options?: ChatOptions): Promise<ChatResponse>;
  stream(messages: Message[], options?: ChatOptions): AsyncIterableIterator<StreamChunk>;
  chatWithTools?(messages: Message[], tools: Record<string, CoreTool>, options?: ChatOptions): Promise<ChatResponse>;
  streamWithTools?(messages: Message[], tools: Record<string, CoreTool>, options?: ChatOptions): AsyncIterableIterator<StreamChunk>;
}

interface ProviderCapabilities {
  chat: boolean;
  streaming: boolean;
  toolCalling: boolean;
  vision: boolean;
  audio: boolean;
}

Creating a Custom Provider ​

typescript
import { ChatClient, ProviderPlugin, ProviderCapabilities } from 'meloqui';

class MyCustomProvider implements ProviderPlugin {
  readonly name = 'my-custom-llm';
  readonly capabilities: ProviderCapabilities = {
    chat: true,
    streaming: true,
    toolCalling: false,
    vision: false,
    audio: false
  };

  async chat(messages, options) {
    // Call your custom API here
    return { content: '...', role: 'assistant' };
  }

  async *stream(messages, options) {
    // Implement streaming
    yield { content: '...', role: 'assistant' };
  }
}

const client = new ChatClient({
  provider: new MyCustomProvider(),
  model: 'custom-model-v1'
});

Configuration Interface ​

When registering a custom provider, ChatClient will pass a configuration object to your provider's constructor. To support all features, your provider's config interface should include:

typescript
import { ILogger, logger as defaultLogger } from 'meloqui';

interface MyProviderConfig {
  apiKey: string;
  model: string;
  baseUrl?: string;
  /** Optional logger for debug/error output */
  logger?: ILogger;
}

class MyCustomProvider implements ProviderPlugin {
  private readonly logger: ILogger;

  constructor(config: MyProviderConfig) {
    // Fall back to default logger if none provided
    this.logger = config.logger ?? defaultLogger;
  }

  async chat(messages, options) {
    this.logger.debug('chat request', { provider: this.name, model: this.model });
    // ... implementation
  }
}

The logger field enables debug logging to flow from ChatClient through to your provider. If not provided, you can fall back to the default logger or skip logging entirely.

Capabilities ​

Declare your provider's capabilities honestly. The ChatClient uses these to:

  • Validate operations before attempting them (e.g., throwing CapabilityError if tool calling is attempted on a provider that doesn't support it)
  • Enable/disable features in the client
CapabilityDescription
chatBasic chat completion
streamingStreaming responses
toolCallingFunction/tool calling
visionImage input support
audioAudio input/output

Released under the MIT License.