How to Add a New AI Provider to Bella OpenAPI by Implementing IProtocolAdaptor

To add a new AI provider to Bella OpenAPI, implement the IProtocolAdaptor interface (or a specialized sub-interface like CompletionAdaptor), create a property DTO extending IProtocolProperty, and annotate the class with @Component so Spring auto-registers it with the AdaptorManager.

The lianjiatech/bella-openapi repository uses a pluggable adapter pattern to integrate diverse AI services. By implementing the generic IProtocolAdaptor interface, you can route requests to any external LLM or embedding service without modifying core controller logic. This guide walks through the concrete implementation steps using actual source paths from the codebase.

Understanding the Bella OpenAPI Adapter Architecture

Bella OpenAPI delegates all provider-specific logic to implementations of IProtocolAdaptor. At startup, BellaAutoConf collects every Spring bean implementing this interface and registers them with the singleton AdaptorManager. When a request arrives, EndpointService looks up the appropriate adaptor by endpoint and protocol, then delegates execution.

The framework provides specialized sub-interfaces for common tasks:

  • CompletionAdaptor<T> for chat and text completion
  • EmbeddingAdaptor<T> for vector embeddings

Step-by-Step Implementation Guide

1. Create a Property DTO

Define a class that implements IProtocolProperty (or a specific variant like CompletionProperty) to hold provider-specific configuration such as API keys, model IDs, and base URLs.

package com.ke.bella.openapi.protocol.myprovider;

import com.ke.bella.openapi.protocol.IProtocolProperty;

public class MyProviderCompletionProperty implements IProtocolProperty {
    public String apiKey;
    public String model;
    public String baseUrl;
}

Store this file at api/sdk/src/main/java/com/ke/bella/openapi/protocol/myprovider/MyProviderCompletionProperty.java.

2. Implement the Adaptor Interface

Create a class that implements the appropriate sub-interface. For chat completions, extend CompletionAdaptor<T> where T is your property class. Implement the completion() and streamCompletion() methods to translate between Bella's request/response format and the provider's API.

package com.ke.bella.openapi.protocol.myprovider;

import com.ke.bella.openapi.protocol.Callbacks;
import com.ke.bella.openapi.protocol.completion.CompletionAdaptor;
import com.ke.bella.openapi.protocol.completion.CompletionRequest;
import com.ke.bella.openapi.protocol.completion.CompletionResponse;
import okhttp3.*;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
public class MyProviderCompletionAdaptor implements CompletionAdaptor<MyProviderCompletionProperty> {

    private final OkHttpClient http = new OkHttpClient();

    @Override
    public CompletionResponse completion(CompletionRequest request,
                                        String url,
                                        MyProviderCompletionProperty property) {
        Request.Builder builder = authorizationRequestBuilder(property);
        builder.url(property.baseUrl != null ? property.baseUrl : "https://api.myprovider.com/v1/chat/completions")
               .post(RequestBody.create(MediaType.parse("application/json"),
                        request.toJson(property.model)));

        try (Response resp = http.newCall(builder.build()).execute()) {
            if (!resp.isSuccessful()) {
                throw new IOException("Unexpected code " + resp);
            }
            return CompletionResponse.fromJson(resp.body().string());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void streamCompletion(CompletionRequest request,
                                 String url,
                                 MyProviderCompletionProperty property,
                                 Callbacks.StreamCompletionCallback callback) {
        // Implement Server-Sent Events or WebSocket streaming logic here
        // Call clearLargeData(...) for huge responses to prevent memory issues
    }

    @Override
    public String endpoint() {
        return "/v1/chat/completions";
    }

    @Override
    public String getDescription() {
        return "Adapter for MyProvider chat completion API";
    }

    @Override
    public Class<?> getPropertyClass() {
        return MyProviderCompletionProperty.class;
    }
}

Place this implementation at api/server/src/main/java/com/ke/bella/openapi/protocol/myprovider/MyProviderCompletionAdaptor.java.

Key implementation details:

  • Use the inherited authorizationRequestBuilder() method from IProtocolAdaptor to construct the Authorization header.
  • The endpoint() method must return the exact path used by the frontend (e.g., /v1/chat/completions).
  • The @Component annotation triggers automatic discovery—no manual registration required.

3. Configure Provider Credentials

Add provider-specific settings to application.yml or externalize them via environment variables. The adaptor receives these values through the property object passed in the request.

myprovider:
  api-key: ${MY_PROVIDER_API_KEY}
  base-url: https://api.myprovider.com
  model: gpt-4-my

4. (Optional) Register Cost and Model Metadata

If your provider uses custom pricing or model capabilities, implement IPriceInfo for billing integration with CostCounter, or IModelFeatures/IModelProperties to expose model-specific behaviors.

5. Test the Adaptor

Use the mock network utilities provided in the SDK to write isolated unit tests. The MockNetworkIO class stubs HTTP interactions without external calls.

package com.ke.bella.openapi.protocol.myprovider;

import com.ke.bella.openapi.protocol.completion.CompletionRequest;
import com.ke.bella.openapi.protocol.completion.CompletionResponse;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class MyProviderCompletionAdaptorTest {

    @Test
    void testCompletion() {
        MyProviderCompletionAdaptor adaptor = new MyProviderCompletionAdaptor();
        CompletionRequest req = new CompletionRequest();
        MyProviderCompletionProperty prop = new MyProviderCompletionProperty();
        prop.apiKey = "test-key";
        prop.model = "gpt-4-my";
        prop.baseUrl = "http://localhost:8080/mock";

        CompletionResponse resp = adaptor.completion(req, null, prop);
        assertNotNull(resp);
    }
}

Run the test suite with:

mvn test -pl server -Dtest=MyProviderCompletionAdaptorTest

Critical Source Files for Reference

  • Core Interface: IProtocolAdaptor — Base contract all adaptors must implement.
  • Chat Completion Sub-interface: CompletionAdaptor — Specialized contract for LLM chat endpoints.
  • Central Registry: AdaptorManager — Singleton that maps endpoints to adaptor instances.
  • Auto-Registration: BellaAutoConf — Spring configuration that injects all IProtocolAdaptor beans into the manager.
  • Request Routing: EndpointService — Service layer that resolves adaptors at runtime.
  • Reference Implementation: OpenAiAdaptor — Working example of a production provider integration.

Summary

  • Implement IProtocolAdaptor (or CompletionAdaptor<T>) to handle provider-specific protocol translation.
  • Create a property class extending IProtocolProperty to encapsulate API keys, models, and endpoints.
  • Annotate with @Component to enable Spring's auto-discovery and registration via BellaAutoConf.
  • Override endpoint() to define the URL path that routes requests to your adaptor.
  • Use MockNetworkIO for unit testing HTTP interactions without live network calls.

Once deployed, Bella OpenAPI automatically routes matching requests to your implementation through the AdaptorManager, enabling immediate integration with the new AI provider.

Frequently Asked Questions

What is the difference between IProtocolAdaptor and CompletionAdaptor?

IProtocolAdaptor is the generic base interface for all protocol adapters in Bella OpenAPI. CompletionAdaptor<T> is a specialized sub-interface specifically for chat completion and text generation endpoints. When adding a new LLM provider, you should implement CompletionAdaptor<T> rather than the base interface to inherit default methods like authorizationRequestBuilder() and ensure type safety with your property class.

How does Bella OpenAPI discover my new adaptor without explicit configuration?

The framework uses Spring's dependency injection and component scanning. According to BellaAutoConf, any bean implementing IProtocolAdaptor is automatically injected into the adaptorManager() method, which registers each instance under the endpoint returned by its endpoint() method. Simply adding @Component to your class completes the registration.

Can I add multiple endpoints for the same provider?

Yes. Implement separate adaptor classes for each endpoint (for example, one for /v1/chat/completions and another for /v1/embeddings), ensuring each implements the appropriate sub-interface (CompletionAdaptor vs EmbeddingAdaptor). Each class should return a unique endpoint string and be annotated with @Component. The AdaptorManager stores both registrations independently.

How do I handle streaming responses from my AI provider?

Implement the streamCompletion() method in your CompletionAdaptor. Use OkHttp's WebSocket or Server-Sent Events (SSE) to consume the provider's streaming API, and invoke the provided Callbacks.StreamCompletionCallback for each chunk. If processing large payloads, call clearLargeData() on the response object to prevent memory exhaustion, as indicated in the interface contracts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →