@holokai/sdk
v1.2.2
Published
Common SDK for Holo plugin system - contracts, types, and validators
Readme
@holokai/sdk
SDK for Holo plugin development. Provides base classes, utilities, and types for building provider plugins.
Installation
npm install @holokai/sdkPlugins also need the types package:
npm install @holokai/typesOverview
The SDK provides:
BasePlugin— lifecycle management (initialize, destroy, state machine)BaseProvider— provider request processing and auditingBaseAuditor— request/response audit record constructionBaseTranslator— Holo universal format translationBaseWireAdapter— provider events to HTTP wire chunks- Core utilities (
ClassLogger,pickDefined,AsyncEventQueue, etc.) - Notification factories and stores
Package Exports
import { BasePlugin } from '@holokai/sdk/plugin';
import { BaseProvider, BaseAuditor, BaseTranslator, BaseWireAdapter } from '@holokai/sdk/provider';
import { ClassLogger, pickDefined, stringifyError } from '@holokai/sdk';
import { HoloRequestDefaults } from '@holokai/sdk/holo';
import { NotificationServiceToken, NotificationEventFactory } from '@holokai/sdk/notification';Building a Provider Plugin
Directory Structure
plugins/holo-provider-{name}/
├── src/
│ ├── index.ts # Default export of plugin instance
│ ├── manifest.ts # Plugin manifest definition
│ ├── plugin.ts # IProviderPlugin implementation
│ ├── {name}.provider.ts # IProvider implementation
│ ├── {name}.auditor.ts # IAuditor implementation
│ ├── {name}.translator.ts # IProviderTranslator implementation
│ ├── {name}.wire.adapter.ts # IWireAdapter implementation
│ └── types.ts # Provider-specific types
├── package.json
├── tsconfig.json
└── README.mdPlugin Implementation
// plugin.ts
import { BasePlugin } from '@holokai/sdk/plugin';
import type { IProviderPlugin, PluginContext } from '@holokai/types/plugin';
import type { IProvider, IWireAdapter, ProviderCapabilities, WireAdapterParams } from '@holokai/types/provider';
import type { RouteTree } from '@holokai/types/routing';
import { RouteHandler } from '@holokai/types/routing';
import { ProtocolCapability } from '@holokai/types/entities';
import { manifest } from './manifest';
import { MyProvider } from './my.provider';
import { MyWireAdapter } from './my.wire.adapter';
import { MyTranslator } from './my.translator';
export const MyProtocols = {
CHAT: 'my.chat',
MODELS: 'my.models'
} as const;
export class MyProviderPlugin extends BasePlugin implements IProviderPlugin {
manifest = manifest;
translator = MyTranslator.instance();
protocols = MyProtocols;
defaultProtocol = MyProtocols.CHAT;
async createProvider(id: string, name: string, config: any): Promise<IProvider> {
return new MyProvider(this, config);
}
async createWireAdapter(params: WireAdapterParams): Promise<IWireAdapter> {
return new MyWireAdapter(params.requestId, params.isStreaming);
}
getCapabilities(): ProviderCapabilities {
return { streaming: true, tools: true, vision: false, functionCalling: true, maxTokens: 128000 };
}
getRoutes(): RouteTree {
return {
v1: {
chat: {
method: 'POST',
handler: RouteHandler.REQUEST,
protocol: { name: MyProtocols.CHAT, capability: ProtocolCapability.CHAT }
},
models: {
method: 'GET',
handler: RouteHandler.MODELS,
protocol: { name: MyProtocols.MODELS, capability: ProtocolCapability.MODELS }
}
}
};
}
protected onInitialize(_context: PluginContext): Promise<void> {
return Promise.resolve();
}
protected onDestroy(): Promise<void> {
return Promise.resolve();
}
}Protocols and Capabilities
Each route declares a protocol (wire format identifier) and capability (what kind of request it handles):
| Capability | Description |
|-----------|-------------|
| ProtocolCapability.CHAT | Conversational LLM requests |
| ProtocolCapability.GENERATE | Text generation (non-chat) |
| ProtocolCapability.EMBED | Embedding generation |
| ProtocolCapability.MODELS | Model listing |
Protocols are registered in the database at startup. Each protocol is tied to a specific plugin version.
Auditor Implementation
The auditor transforms worker requests/responses into ProviderRequest/ProviderResponse audit records:
import { BaseAuditor } from '@holokai/sdk/provider';
import type { HoloWorkerRequest } from '@holokai/types/worker';
import type { ProviderRequest } from '@holokai/types/entities';
export class MyAuditor extends BaseAuditor {
readonly provider = 'my-provider';
protected toHoloRequest(workerRequest: HoloWorkerRequest, req: Omit<ProviderRequest, 'id'>): void {
req.access_model = workerRequest.payload.model;
req.metadata.user_prompt = workerRequest.payload.messages?.at(-1)?.content;
}
protected mapProviderPayload(workerRequest: HoloWorkerRequest, req: Omit<ProviderRequest, 'id'>): void {
const { temperature, max_tokens } = workerRequest.payload;
req.metadata.options = { temperature, max_tokens };
}
protected async createProviderEnvelope(payload: any) {
return { access_model: payload.model || 'unknown' };
}
}Audit records use:
- Real columns for queryable fields:
access_model,application_id,provider_id,protocol_id,user_id,client_identifier,thread_id, token counts, timing, cost metadataJSONB for extensible data:user_prompt,system_prompt,options,raw_request,headers,response_raw,usage_raw, etc.
Manifest
// manifest.ts
import type { PluginManifest } from '@holokai/types/plugin';
import { PluginType } from '@holokai/types/plugin';
export const manifest: PluginManifest = {
name: '@holokai/holo-provider-my',
version: '1.0.0',
pluginType: PluginType.PROVIDER,
family: 'MY_PROVIDER',
description: 'My custom provider plugin'
};Default Export
// index.ts
import { MyProviderPlugin } from './plugin';
export default new MyProviderPlugin();Reference Implementations
| Plugin | Key Features | |--------|-------------| | OpenAI | Dual protocols (completions + responses), dual wire adapters, tool calling | | Claude | 6-event streaming lifecycle, content blocks, extended thinking, prompt caching | | Ollama | Chat + generate protocols, local deployment, passthrough default handler |
License
MIT
