Architecture Overview¶
This document describes the architecture and design decisions of the Jodit AI Adapter service.
System Overview¶
graph TD
subgraph Client Side
JoditEditor[Jodit Editor<br/>AI Plugin]
end
JoditEditor -->|HTTPS<br/>API Key in header| CORS
subgraph Adapter Service
CORS[CORS Middleware] --> Auth[Auth Middleware] --> Handler[Request Handler]
Handler --> Factory[Adapter Factory]
Factory --> OpenAI[OpenAI Adapter<br/>Vercel AI SDK]
Factory --> DeepSeek[DeepSeek Adapter]
Factory --> Other[...]
end
OpenAI -->|HTTPS| Providers
DeepSeek -->|HTTPS| Providers
Other -->|HTTPS| Providers
subgraph External AI Providers
Providers[" "]:::hidden
P1[OpenAI]
P2[DeepSeek]
P3[Anthropic]
P4[Google]
end
classDef hidden display:none
Core Components¶
1. Middlewares¶
CORS Middleware (src/middlewares/cors.ts)¶
- Handles Cross-Origin Resource Sharing
- Configurable allowed origins
- Supports wildcards, specific domains, and RegExp patterns
Auth Middleware (src/middlewares/auth.ts)¶
- Validates API key format (36 chars, UUID format: A-F0-9-)
- Extracts API key from
Authorizationorx-api-keyheader - Validates referer if required
- Calls custom authentication callback
- Stores user ID in request object
2. Adapters¶
Base Adapter (src/adapters/base-adapter.ts)¶
Abstract base class providing: - Common error handling - Response validation - Helper methods for tool conversion - Streaming support utilities
OpenAI Adapter (src/adapters/openai-adapter.ts)¶
Implements OpenAI integration:
- Uses Vercel AI SDK @ai-sdk/openai
- Supports streaming and non-streaming responses
- Handles tool calling
- Message format conversion (Jodit ↔ OpenAI)
Adapter Factory (src/adapters/adapter-factory.ts)¶
- Registry pattern for adapters
- Creates adapter instances based on provider type
- Manages API key priority (user key > config key)
3. Routes (src/routes/<name>/)¶
Each route is organized as a module with its own directory:
- handler.ts - Request handler logic
- index.ts - Router factory function
- schema.ts - Zod validation schemas
- handler.test.ts - Route-specific tests
Available routes:
- ai-request/ - POST /ai/request - AI text generation (streaming SSE)
- ai-providers/ - GET /ai/providers - List configured providers
- image-generate/ - POST /ai/image/generate - Image generation
- health/ - GET /ai/health - Health check
4. Rate Limiter (src/rate-limiter/)¶
Built-in rate limiting with pluggable backends:
- memory-rate-limiter.ts - In-memory rate limiting for single-instance deployments
- redis-rate-limiter.ts - Redis-based rate limiting for distributed deployments
- rate-limiter-factory.ts - Creates rate limiter based on configuration
5. Type System¶
Jodit AI Types (src/types/jodit-ai.ts)¶
Defines interfaces matching Jodit AI Assistant Pro contract:
- IAIRequestContext - Request from Jodit
- IAIAssistantResult - Response to Jodit (streaming or final)
- IAIMessage - Conversation message
- IToolCall - Tool/function call
- AIStreamEvent - Streaming event types
Config Types (src/types/config.ts)¶
AppConfig- Application configurationProviderConfig- Provider-specific settingsAuthCallback- Authentication callback signature
6. Application¶
Express App (src/app.ts)¶
Main application setup:
- Middleware chain configuration
- Route handlers (/ai/health, /ai/request, /ai/providers, /ai/image/generate)
- Request validation using Zod
- Error handling
- Streaming response handling (SSE)
Entry Points¶
src/index.ts- Library entry point withstart()(returns{ app, cleanup })src/run.ts- CLI entry point for standalone server
Request Flow¶
1. Client Request¶
POST /ai/request
Headers:
Authorization: Bearer 12345678-1234-1234-1234-123456789abc
Content-Type: application/json
Body:
{
provider: "openai",
context: {
mode: "full",
messages: [...],
tools: [...],
conversationOptions: { model: "gpt-5.2" }
}
}
2. Middleware Chain¶
- CORS Middleware: Validates origin, sets headers
- Auth Middleware:
- Extracts API key
- Validates format
- Checks referer (optional)
- Calls custom auth callback (optional)
- Sets
req.apiKeyandreq.userId
3. Request Handler¶
- Validate request body with Zod schema
- Check provider is supported and configured
- Create adapter instance via factory
- Create AbortController for timeout
- Call
adapter.handleRequest(context, signal)
4. Adapter Processing¶
OpenAI Adapter Flow:¶
- Build Messages:
- Add system instructions
- Convert Jodit messages to OpenAI format
-
Append selection contexts
-
Build Tools:
- Convert Jodit tool definitions to OpenAI schema
-
Build JSON Schema for parameters
-
Call Vercel AI SDK:
- Use
streamText()for streaming -
Use
generateText()for non-streaming -
Transform Response:
- Convert OpenAI format back to Jodit format
- Extract tool calls
- Handle artifacts (images, etc.)
5. Response Streaming¶
For streaming responses:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
event: created
data: {"type":"created","response":{"responseId":"...","content":"","finished":false}}
event: text-delta
data: {"type":"text-delta","delta":"Hello"}
event: text-delta
data: {"type":"text-delta","delta":" there"}
event: completed
data: {"type":"completed","response":{"responseId":"...","content":"Hello there","finished":true}}
6. Error Handling¶
Errors are caught at multiple levels:
- Adapter Level: Catches provider API errors
- Request Handler Level: Catches validation errors
- Express Error Handler: Catches unhandled errors
All errors converted to Boom errors with appropriate HTTP status codes.
Security Architecture¶
1. API Key Management¶
sequenceDiagram
participant Client
participant Adapter Service
participant AI Provider
Client->>Adapter Service: Request + API Key
Note over Adapter Service: Validate Key<br/>(36 chars, UUID)
Note over Adapter Service: Custom Auth<br/>(Optional)
Adapter Service->>AI Provider: Provider Request<br/>(Provider API Key)
AI Provider-->>Adapter Service: Response
Adapter Service-->>Client: Response
Key Points: - Client API key ≠ Provider API key - Provider keys stored server-side only - Client key validates user access - Custom callback can check database
2. Authentication Flow¶
1. Extract API key from request header
↓
2. Validate format (regex)
↓
3. Check referer (if configured)
↓
4. Call custom auth callback (optional)
├─ Returns user ID → Allow
└─ Returns null → Reject (401)
3. CORS Protection¶
Configurable CORS settings:
- Development: * (allow all)
- Production: Specific domains or patterns
- Supports string, array, or RegExp
Extensibility¶
Adding New Providers¶
The architecture supports easy addition of new providers:
- Create Adapter: Extend
BaseAdapter - Register: Add to
AdapterFactory.adapters - Configure: Add to default config
- Test: Write test suite with nock
Example:
// 1. Create adapter
export class MyProviderAdapter extends BaseAdapter {
protected async processRequest(context, signal) {
// Use Vercel AI SDK or custom implementation
}
}
// 2. Register
AdapterFactory.adapters.set('myprovider', MyProviderAdapter);
// 3. Configure
providers: {
myprovider: {
type: 'myprovider',
apiKey: process.env.MYPROVIDER_API_KEY,
defaultModel: 'model-v1'
}
}
Custom Middlewares¶
Add custom middleware in src/app.ts:
import { rateLimitMiddleware } from './middlewares/rate-limit';
export function createApp(config: AppConfig): { app: Application; cleanup: () => Promise<unknown> } {
const app = express();
// ... existing middlewares
app.use(rateLimitMiddleware(config.rateLimit));
// ... routes
}
Performance Considerations¶
1. Streaming¶
- Reduces time to first token
- Better user experience
- Lower memory usage
- Supports Server-Sent Events (SSE)
2. Connection Pooling¶
- HTTP keep-alive enabled
- Reuses connections to AI providers
- Reduces latency
3. Timeout Management¶
- Request timeout (default: 2 minutes)
- Configurable per request
- Graceful abort handling
4. Error Recovery¶
- Automatic retry with exponential backoff (configurable)
- Graceful degradation on provider failures
Testing Strategy¶
Unit Tests¶
- Individual functions and classes
- Mock external dependencies with nock
- High coverage requirement
Integration Tests¶
- Full request flow
- Middleware chain
- Provider integration
Test Structure¶
describe('Component', () => {
beforeEach(() => {
// Setup
});
afterEach(() => {
// Cleanup
});
it('should handle success case', () => {
// Arrange
// Act
// Assert
});
it('should handle error case', () => {
// Arrange
// Act
// Assert
});
});
Deployment Architecture¶
Docker Deployment¶
graph TD
subgraph Docker Container
subgraph Node.js 22
Service[Adapter Service<br/>Port: 8082]
end
end
Service --> LB[Load Balancer<br/>nginx / traefik]
Environment Variables¶
Sensitive data via environment: - API keys - CORS origins - Custom configuration path
Scalability¶
- Stateless design (horizontal scaling)
- No session storage
- Load balancer compatible
- Health check endpoint
Future Enhancements¶
Planned Features¶
- Additional providers (DeepSeek, Anthropic, Google)
- Caching layer for responses
- WebSocket support as alternative to SSE
- Admin dashboard
- Multi-tenancy support
Performance Optimizations¶
- Response caching (Redis)
- Request deduplication
- Connection pooling improvements
- Compression (gzip/brotli)