@redhat-cloud-services/ai-client-common
Common interfaces and utilities for AI client packages in the Red Hat Cloud Services ecosystem.
Features
- Standardized AI Client Interface - Common
IAIClient
interface for all AI services
- Conversation Management - Standard conversation interface with locking support
- Dependency Injection - Interfaces for custom fetch implementations and streaming handlers
- TypeScript Support - Comprehensive type definitions for AI client development
- Error Handling - Base error classes with validation error support
- Streaming Support - Generic streaming handler interface for real-time responses
- Zero Dependencies - Pure TypeScript interfaces with no runtime dependencies
Installation
npm install @redhat-cloud-services/ai-client-common
Core Interfaces
IAIClient Interface
All AI clients in this workspace implement the IAIClient
interface:
import { IAIClient, ClientInitLimitation, IInitErrorResponse } from '@redhat-cloud-services/ai-client-common';
declare class IAIClient<AP extends Record<string, unknown> = Record<string, unknown>, TChunk = unknown> {
constructor(config: IBaseClientConfig<TChunk>);
init(): Promise<{
conversations: IConversation[];
limitation?: ClientInitLimitation;
error?: IInitErrorResponse;
}>;
sendMessage<T extends Record<string, unknown> = Record<string, unknown>>(
conversationId: string,
message: string,
options?: ISendMessageOptions<T>
): Promise<IMessageResponse<AP>>;
sendMessage<
T extends Record<string, unknown> = Record<string, unknown>,
R extends Record<string, unknown> = Record<string, unknown>
>(
conversationId: string,
message: string,
options?: ISendMessageOptions<T, R>
): Promise<IMessageResponse<AP>>;
getDefaultStreamingHandler<TChunk = unknown>(): IStreamingHandler<TChunk> | undefined;
getConversationHistory(conversationId: string, options?: IRequestOptions): Promise<IConversationHistoryResponse<AP>>;
healthCheck(options?: IRequestOptions): Promise<unknown>;
getServiceStatus?(options?: IRequestOptions): Promise<unknown>;
createNewConversation(): Promise<IConversation>;
}
Dependency Injection
Custom Fetch Implementation
Important: Do NOT set 'Content-Type'
headers in your fetchFunction - AI clients manage these internally based on endpoint requirements.
import { IFetchFunction } from '@redhat-cloud-services/ai-client-common';
const customFetch: IFetchFunction = async (input, init) => {
const token = await getAuthToken();
return fetch(input, {
...init,
headers: {
...init?.headers,
'Authorization': `Bearer ${token}`,
},
});
};
Base Client Configuration
import { IBaseClientConfig } from '@redhat-cloud-services/ai-client-common';
const config: IBaseClientConfig = {
baseUrl: 'https://your-ai-service.com',
fetchFunction: customFetch,
defaultStreamingHandler: new CustomStreamingHandler()
};
Lazy Initialization (Default Behavior)
AI clients now use lazy initialization by default. The init()
method no longer auto-creates conversations - instead, conversations are created automatically when needed (e.g., on first message send).
Default Behavior:
- Client
init()
only loads existing conversations
- No conversations are auto-created during initialization
- Conversations are created automatically on first
sendMessage()
call
- Provides optimal performance and seamless user experience
Key Changes:
- Removed:
ClientInitOptions
interface (no longer needed)
- Removed:
getInitOptions()
method from IAIClient
interface
- Removed:
initialConversationId
from init()
return type
- New: Automatic conversation creation on first message send
Client Initialization Responses
The init()
method returns additional information about client limitations and errors:
import { ClientInitLimitation, IInitErrorResponse } from '@redhat-cloud-services/ai-client-common';
type ClientInitLimitation = {
reason: string;
detail?: string;
};
interface IInitErrorResponse {
message: string;
status: number;
}
function isInitErrorResponse(obj: unknown): obj is IInitErrorResponse;
Streaming Support
The streaming interface has been updated to standardize chunk handling across all AI clients. The afterChunk
callback now receives an IStreamChunk
object with standardized structure.
IStreamChunk Interface
import { IStreamChunk } from '@redhat-cloud-services/ai-client-common';
interface IStreamChunk<T extends Record<string, unknown> = Record<string, unknown>> {
answer: string;
messageId: string;
additionalAttributes: T;
}
Implementing a Custom Streaming Handler
import { IStreamingHandler, AfterChunkCallback, IStreamChunk } from '@redhat-cloud-services/ai-client-common';
class CustomStreamingHandler<TChunk = unknown> implements IStreamingHandler<TChunk> {
onChunk(chunk: TChunk, afterChunk?: AfterChunkCallback): void {
console.log('Received chunk:', chunk);
if (afterChunk) {
afterChunk({
answer: extractAnswer(chunk),
additionalAttributes: extractAttributes(chunk)
});
}
}
onStart?(conversationId?: string, messageId?: string): void {
console.log('Stream started', { conversationId, messageId });
}
onComplete?(finalChunk: TChunk): void {
console.log('Stream completed:', finalChunk);
}
onError?(error: Error): void {
console.error('Stream error:', error);
}
onAbort?(): void {
console.log('Stream aborted');
}
}
Send Message Options
import { ISendMessageOptions, IStreamChunk } from '@redhat-cloud-services/ai-client-common';
const streamingOptions: ISendMessageOptions = {
stream: true,
headers: { 'Custom-Header': 'value' },
signal: abortController.signal,
afterChunk: (chunk: IStreamChunk) => {
console.log('Answer:', chunk.answer);
console.log('Additional data:', chunk.additionalAttributes);
updateUI(chunk.answer);
}
};
const optionsWithPayload: ISendMessageOptions<AdditionalProps, CustomPayload> = {
stream: false,
headers: { 'X-Custom': 'value' },
requestPayload: {
customData: 'value',
options: { setting: true }
}
};
Request Payload Support
The ISendMessageOptions
interface supports method overloading to enable client-specific request payloads:
export interface ISendMessageOptions<
T extends Record<string, unknown> = Record<string, unknown>,
R extends Record<string, unknown> = never
> extends IRequestOptions {
stream?: boolean;
afterChunk?: AfterChunkCallback<T>;
requestPayload?: R extends never ? never : R;
}
interface MyClientPayload {
context?: { systemInfo: string };
skipCache?: boolean;
}
const response = await client.sendMessage(
'conversation-id',
'message',
{
requestPayload: {
context: { systemInfo: 'linux' },
skipCache: true
}
}
);
Error Handling
Base Error Classes
import {
AIClientError,
AIClientValidationError
} from '@redhat-cloud-services/ai-client-common';
try {
const response = await client.sendMessage(conversationId, message);
} catch (error) {
if (error instanceof AIClientValidationError) {
console.error('Validation errors:', error.validationErrors);
error.validationErrors.forEach(validationError => {
console.log(`Field: ${validationError.loc.join('.')}`);
console.log(`Message: ${validationError.msg}`);
console.log(`Type: ${validationError.type}`);
});
} else if (error instanceof AIClientError) {
console.error(`API Error ${error.status}: ${error.message}`);
console.error('Response data:', error.data);
} else {
console.error('Unexpected error:', error);
}
}
Type Definitions
Request and Response Types
import {
IRequestOptions,
IMessageResponse,
IConversationHistoryResponse,
IConversation
} from '@redhat-cloud-services/ai-client-common';
const options: IRequestOptions = {
headers: { 'Custom-Header': 'value' },
signal: new AbortController().signal
};
interface IMessageResponse<AP = Record<string, unknown>> {
messageId: string;
answer: string;
conversationId: string;
date?: Date;
additionalAttributes?: AP;
}
interface IConversation {
id: string;
title: string;
locked: boolean;
createdAt: Date;
}
Compatible Packages
This package provides the foundation for:
Building
Run nx build ai-client-common
to build the library.
Running unit tests
Run nx test ai-client-common
to execute the unit tests via Jest.
Development
This package follows the workspace standards:
- Strict TypeScript with no
any
types
- Zero runtime dependencies
- Comprehensive interface definitions