Custom Provider
Eklesia's Provider abstraction lets you connect agents to any AI backend. Create a custom provider to integrate models that don't follow the OpenAI API format.
Extending the Base Provider
typescript
import { Provider } from "eklesia";
import type { Message, ChatCompletionResponse } from "eklesia";
class AnthropicProvider extends Provider {
private apiKey: string;
private model: string;
constructor(
model: string,
apiKey: string,
temperature?: number,
max_tokens?: number,
) {
super(temperature, max_tokens);
this.model = model;
this.apiKey = apiKey;
}
async query(messages: Array<Message>): Promise<ChatCompletionResponse> {
// Convert messages to Anthropic format
const systemMessage = messages.find((m) => m.role === "system");
const nonSystemMessages = messages
.filter((m) => m.role !== "system")
.map((m) => ({
role: m.role === "assistant" ? "assistant" : "user",
content: m.content,
}));
const res = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": this.apiKey,
"anthropic-version": "2023-06-01",
},
body: JSON.stringify({
model: this.model,
max_tokens: this.max_tokens,
system: systemMessage?.content ?? "",
messages: nonSystemMessages,
}),
});
const data = await res.json();
// Convert Anthropic response to OpenAI format
return {
id: data.id,
object: "chat.completion",
created: Date.now(),
model: this.model,
choices: [
{
index: 0,
finish_reason: data.stop_reason ?? "stop",
message: {
role: "assistant",
content: data.content?.[0]?.text ?? "",
},
},
],
};
}
}The ChatCompletionResponse Interface
Your provider's query() method must return data matching this interface:
typescript
interface ChatCompletionResponse {
id: string;
object: "chat.completion";
created: number;
model: string;
choices: Array<{
index: number;
finish_reason: string | null;
message: {
role: "system" | "user" | "assistant" | "tool";
content: string | null;
};
}>;
}The agent reads the response from choices[0].message.content.
Example: Static Provider (for Testing)
A provider that returns predefined responses, useful for testing:
typescript
class StaticProvider extends Provider {
private responses: string[];
private index = 0;
constructor(responses: string[]) {
super();
this.responses = responses;
}
async query(_messages: Array<Message>): Promise<ChatCompletionResponse> {
const content = this.responses[this.index % this.responses.length];
this.index++;
return {
id: `static-${this.index}`,
object: "chat.completion",
created: Date.now(),
model: "static",
choices: [
{
index: 0,
finish_reason: "stop",
message: { role: "assistant", content },
},
],
};
}
}Usage
typescript
import { Agent, Arena, Orchestrator, ConversationEnvironment } from "eklesia";
const provider = new AnthropicProvider(
"claude-sonnet-4-20250514",
process.env.ANTHROPIC_API_KEY!,
);
const agent = new Agent(
"Claude",
"You are a thoughtful conversationalist.",
provider,
);
const environment = new ConversationEnvironment("A philosophical discussion.");
const orchestrator = new Orchestrator(environment);
const arena = new Arena([agent], orchestrator, environment);
await arena.run(10);Key Points
- Override
query()to implement your AI backend integration - Always return a
ChatCompletionResponse, the agent expectschoices[0].message.content - Use
this.temperatureandthis.max_tokensfrom the base class - The
messagesparameter uses the standard{ role, content }format