NX monorepo for AI-related web client libraries, starting with the Intelligent Front Door (IFD) TypeScript client and OpenShift Lightspeed client. This workspace provides a complete ecosystem for building AI-powered web clients.
For AI Agents: This workspace includes comprehensive AI agent context files in the
ai-rules/directory. Start withai-rules/AI_CONTEXT.mdfor workspace overview and guidance.
Intelligent Front Door (IFD) API TypeScript client with streaming support.
- Features: Full IFD API coverage, dependency injection, streaming handlers
- Status: Production ready
- Usage:
npm install @redhat-cloud-services/arh-client
OpenShift Lightspeed API TypeScript client with comprehensive feature support.
- Features: Complete Lightspeed API, streaming, health checks, feedback
- Status: Production ready
- Usage:
npm install @redhat-cloud-services/lightspeed-client
Ansible Lightspeed API TypeScript client with dependency injection support.
- Features: Complete Ansible Lightspeed API, streaming, conversation management, health checks
- Status: Production ready
- Usage:
npm install @redhat-cloud-services/ansible-lightspeed
Ansible Assisted Installer (AAI) API TypeScript client with Server-Sent Events streaming support.
- Features: Full AAI API coverage, SSE streaming, temporary conversations, health checks
- Status: Production ready
- Usage:
npm install @redhat-cloud-services/aai-client
RHEL Lightspeed API TypeScript client for Red Hat Enterprise Linux assistance.
- Features: RHEL-specific AI assistance, conversation management, health checks
- Status: Production ready
- Usage:
npm install @redhat-cloud-services/rhel-lightspeed-client
Common interfaces and utilities for all AI client packages.
- Features: Standardized
IAIClientinterface, dependency injection interfaces, error classes - Status: Foundation package
- Usage:
npm install @redhat-cloud-services/ai-client-common
Framework-agnostic state management for AI conversations with lazy initialization and comprehensive conversation management.
- Features: Lazy initialization, automatic conversation creation, multi-conversation support, event-driven architecture, message flow control, conversation history, streaming integration
- Status: Production ready
- Usage:
npm install @redhat-cloud-services/ai-client-state
React hooks and context provider for seamless AI state management integration.
- Features: Complete hook ecosystem (useMessages, useSendMessage, useConversations, etc.), React Context provider, streaming support, TypeScript support
- Status: Production ready
- Usage:
npm install @redhat-cloud-services/ai-react-state
Integration test application for validating package interoperability.
- Features: Cross-package testing, live server integration, mocked responses
- Status: Development tool
- Usage:
npx nx test client-integration-tests
React test application for AI client UI component validation.
- Features: AI chatbot components, React hook examples, UI integration patterns
- Status: Development tool
- Usage:
npx nx serve react-integration-tests
End-to-end tests for the React integration test application.
- Features: Cypress e2e testing, UI behavior validation
- Status: Development tool
- Usage:
npx nx e2e react-integration-tests-e2e
import { IFDClient } from '@redhat-cloud-services/arh-client';
const client = new IFDClient({
baseUrl: 'https://your-api.com',
fetchFunction: (input, init) => fetch(input, init)
});
await client.init();
const conversation = await client.createNewConversation();
const response = await client.sendMessage(conversation.id, 'Hello AI!');
// Streaming usage (requires handleChunk callback)
await client.sendMessage(conversation.id, 'Tell me about containers', {
stream: true,
handleChunk: (response) => {
console.log('Streaming response:', response.answer);
}
});import { createClientStateManager } from '@redhat-cloud-services/ai-client-state';
import { IFDClient } from '@redhat-cloud-services/arh-client';
const client = new IFDClient({
baseUrl: 'https://your-api.com',
fetchFunction: (input, init) => fetch(input, init)
});
const stateManager = createClientStateManager(client);
await stateManager.init();
// LAZY INITIALIZATION: First sendMessage auto-creates conversation
await stateManager.sendMessage('Hello!'); // Auto-promotes temporary conversation
// Streaming usage
await stateManager.sendMessage('Explain Kubernetes', { stream: true });
// Conversation management
const conversations = stateManager.getConversations();
await stateManager.setActiveConversationId('conversation-123');
const newConversation = await stateManager.createNewConversation();import { createClientStateManager, Events } from '@redhat-cloud-services/ai-client-state';
const stateManager = createClientStateManager(client);
await stateManager.init(); // No longer auto-creates conversations
// Create and manage multiple conversations
const conv1 = await stateManager.createNewConversation();
const conv2 = await stateManager.createNewConversation();
// Switch between conversations
await stateManager.setActiveConversationId(conv1.id);
await stateManager.sendMessage('Hello in conversation 1');
await stateManager.setActiveConversationId(conv2.id);
await stateManager.sendMessage('Hello in conversation 2');
// Get all conversations
const allConversations = stateManager.getConversations();
console.log(`Total conversations: ${allConversations.length}`);
// Get messages from active conversation
const messages = stateManager.getActiveConversationMessages();
// Subscribe to state changes
const unsubscribe = stateManager.subscribe(Events.MESSAGE, () => {
console.log('Messages updated:', stateManager.getActiveConversationMessages());
});import React from 'react';
import {
AIStateProvider,
useSendMessage,
useMessages,
useConversations,
useCreateNewConversation,
useSetActiveConversation,
useActiveConversation
} from '@redhat-cloud-services/ai-react-state';
function ChatApp() {
const sendMessage = useSendMessage();
const messages = useMessages();
const conversations = useConversations();
const createNewConversation = useCreateNewConversation();
const setActiveConversation = useSetActiveConversation();
const activeConversation = useActiveConversation();
const handleNewConversation = async () => {
const newConv = await createNewConversation();
await setActiveConversation(newConv.id);
};
return (
<div>
<div>
<h3>Active: {activeConversation?.title || 'None'}</h3>
{activeConversation?.locked && <p>π This conversation is locked</p>}
<button onClick={handleNewConversation}>New Conversation</button>
<select
value={activeConversation?.id || ''}
onChange={(e) => setActiveConversation(e.target.value)}
>
{conversations.map(conv => (
<option key={conv.id} value={conv.id}>
{conv.title} {conv.locked ? 'π' : ''}
</option>
))}
</select>
</div>
<div>
{messages.map(msg => (
<div key={msg.id}>{msg.role}: {msg.answer}</div>
))}
</div>
<button
onClick={() => sendMessage('Hello AI!')}
disabled={activeConversation?.locked}
>
Send Message
</button>
<button
onClick={() => sendMessage('Stream response', { stream: true })}
disabled={activeConversation?.locked}
>
Send Streaming
</button>
</div>
);
}import React from 'react';
import { AIStateProvider, useSendMessage, useMessages } from '@redhat-cloud-services/ai-react-state';
import { createClientStateManager } from '@redhat-cloud-services/ai-client-state';
import { IFDClient } from '@redhat-cloud-services/arh-client';
// Initialize state manager outside React scope
const client = new IFDClient({
baseUrl: 'https://your-api.com',
fetchFunction: (input, init) => fetch(input, init)
});
const stateManager = createClientStateManager(client);
// Initialize immediately when module loads (no longer auto-creates conversations)
stateManager.init();
function App() {
return (
<AIStateProvider stateManager={stateManager}>
<ChatInterface />
</AIStateProvider>
);
}
function ChatInterface() {
const sendMessage = useSendMessage();
const messages = useMessages();
const handleSend = () => {
sendMessage('Hello AI!');
};
const handleStreamingSend = () => {
sendMessage('Tell me about containers', { stream: true });
};
return (
<div>
{messages.map(msg => <div key={msg.id}>{msg.answer}</div>)}
<button onClick={handleSend}>Send</button>
<button onClick={handleStreamingSend}>Send Streaming</button>
</div>
);
}import React, { useMemo } from 'react';
import { AIStateProvider, useSendMessage, useMessages } from '@redhat-cloud-services/ai-react-state';
import { createClientStateManager } from '@redhat-cloud-services/ai-client-state';
import { IFDClient } from '@redhat-cloud-services/arh-client';
function App() {
const stateManager = useMemo(() => {
// Create client with proper fetchFunction
const client = new IFDClient({
baseUrl: 'https://your-api.com',
fetchFunction: (input, init) => fetch(input, init)
});
// Create state manager (init will be called by provider)
const manager = createClientStateManager(client);
// Initialize async - once resolved, the client is ready (no longer auto-creates conversations)
manager.init();
return manager;
}, []);
return (
<AIStateProvider stateManager={stateManager}>
<ChatInterface />
</AIStateProvider>
);
}
function ChatInterface() {
const sendMessage = useSendMessage();
const messages = useMessages();
const handleSend = () => {
sendMessage('Hello AI!');
};
const handleStreamingSend = () => {
sendMessage('Tell me about containers', { stream: true });
};
return (
<div>
{messages.map(msg => <div key={msg.id}>{msg.answer}</div>)}
<button onClick={handleSend}>Send</button>
<button onClick={handleStreamingSend}>Send Streaming</button>
</div>
);
}| Layer | Package | Purpose |
|---|---|---|
| Application | Your React/JS App | Frontend application using AI capabilities |
| React Hooks | ai-react-state |
React hooks: useMessages, useSendMessage, useConversations, useActiveConversation |
| State Management | ai-client-state |
Multi-conversation management, event-driven updates, streaming integration |
| AI Clients | arh-client |
Intelligent Front Door (IFD) API client |
lightspeed-client |
OpenShift Lightspeed API client | |
ansible-lightspeed |
Ansible Lightspeed API client | |
aai-client |
Ansible Assisted Installer (AAI) API client | |
rhel-lightspeed-client |
RHEL Lightspeed API client | |
| Foundation | ai-client-common |
IAIClient interface, error classes, shared types |
- π Interoperability: All packages implement common interfaces
- π― Dependency Injection: Testable and configurable clients
- π‘ Streaming Support: Real-time AI responses with custom handlers
- π¬ Conversation Management: Lazy initialization with automatic conversation creation, multi-conversation support with history and state persistence
- βοΈ React Ready: Complete hook ecosystem and context providers
- π¦ Modular: Use only what you need, from raw clients to full React integration
- π Type Safe: Comprehensive TypeScript coverage across all packages
- π§ͺ Well Tested: Extensive integration tests and cross-package validation
ai-client-common (foundation)
βββ arh-client
βββ lightspeed-client
βββ ansible-lightspeed
βββ aai-client
βββ rhel-lightspeed-client
βββ ai-client-state
β βββ ai-react-state
βββ [your-custom-client]
ai-web-clients/
βββ packages/ # All packages/applications go here
βββ .github/workflows/ # GitHub Actions workflows
βββ .husky/ # Git hooks (modern Husky v9+)
βββ nx.json # NX configuration
βββ tsconfig.json # TypeScript configuration
βββ jest.preset.js # Jest configuration
βββ jest.setup.js # Jest setup file
βββ .eslintrc.json # ESLint configuration
βββ commitlint.config.js # Commitlint configuration
βββ .releaserc.json # Semantic release configuration
βββ package.json # Root package.json
npm run build- Build all packagesnpm run test- Run all testsnpm run test:watch- Run tests in watch modenpm run test:coverage- Run tests with coveragenpm run lint- Lint all packagesnpm run lint:fix- Fix linting issuesnpm run e2e- Run e2e testsnpm run serve- Serve applicationsnpm run graph- View dependency graph
npm run affected:build- Build only affected packagesnpm run affected:test- Test only affected packagesnpm run affected:lint- Lint only affected packagesnpm run affected:e2e- E2e test only affected packages
npm run version- Version all packages based on conventional commitsnpm run version:dry-run- Preview version changes without applying themnpm run release- Release all packages (runs in CI)npm run release:dry-run- Preview release without applying changes
- NX: Monorepo management and build system
- TypeScript: Type-safe JavaScript
- React: UI framework
- Jest: Unit testing
- Cypress: End-to-end testing
- ESLint: Code linting
- @jscutlery/semver: Semantic versioning and releases
- Conventional Commits: Commit message format for automated versioning
- Husky v9+: Modern git hooks for code quality
-
Install dependencies:
npm install
-
Create a new package:
nx generate @nx/react:application my-app # or nx generate @nx/react:library my-lib -
Run the application:
nx serve my-app
All packages should be created in the packages/ directory. NX will automatically detect and configure packages placed there.
The repository uses Husky v9+ for git hooks:
- pre-commit: Runs linting and tests before allowing commits
- commit-msg: Validates commit messages follow conventional commit format
These hooks ensure code quality and consistent commit messages for automated versioning.
This monorepo uses semantic versioning with automated releases:
Follow Conventional Commits:
type(scope): description
[optional body]
[optional footer]
Types:
feat: New feature (minor version bump)fix: Bug fix (patch version bump)BREAKING CHANGE: Breaking change (major version bump)chore,docs,style,refactor,test: No version bump
When package B (dependency) is updated, package A (dependent) will automatically:
- Update its dependency on package B to the new version
- Receive its own version bump
- Trigger a new release
- Automated: Releases happen automatically via GitHub Actions on pushes to
main - Manual: Run
npm run releaselocally (requires proper git setup) - Preview: Use
npm run version:dry-runto preview changes
# Generate the library
nx generate @nx/react:library my-lib
# The library will automatically inherit versioning configuration
# Start making commits with conventional commit messages
git add .
git commit -m "feat(my-lib): add new awesome feature"
# Version and release
npm run release# Create two libraries
nx generate @nx/react:library shared-utils
nx generate @nx/react:library my-app
# In my-app, add dependency to shared-utils
# When shared-utils gets updated, my-app will automatically bump its version