@hiliosai/sdk 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,394 @@
1
+ # Integration Service Architecture
2
+
3
+ ## Overview
4
+
5
+ Integration Services are specialized Moleculer services that provide a standardized way to connect external messaging platforms (WhatsApp, Telegram, Discord, etc.) with the application. They handle webhook reception, message normalization, and platform-specific communication patterns.
6
+
7
+ ## Purpose
8
+
9
+ The Integration Service pattern solves several key challenges:
10
+
11
+ 1. **Platform Abstraction**: Provides unified interface for different messaging platforms
12
+ 2. **Webhook Security**: Implements signature validation and replay attack prevention
13
+ 3. **Message Normalization**: Converts platform-specific messages to unified format
14
+ 4. **Retry Logic**: Handles transient failures with exponential backoff
15
+ 5. **Event Broadcasting**: Emits standardized events for message processing
16
+ 6. **Health Monitoring**: Provides health checks and status reporting
17
+
18
+ ## Architecture
19
+
20
+ ```mermaid
21
+ graph TB
22
+ subgraph "External Platforms"
23
+ WA[WhatsApp]
24
+ TG[Telegram]
25
+ DC[Discord]
26
+ SL[Slack]
27
+ end
28
+
29
+ subgraph "Integration Layer"
30
+ subgraph "Integration Services"
31
+ WAS[WhatsApp Service]
32
+ TGS[Telegram Service]
33
+ DCS[Discord Service]
34
+ SLS[Slack Service]
35
+ end
36
+
37
+ subgraph "Service Actions"
38
+ RW[i_receiveWebhook]
39
+ SM[i_sendMessage]
40
+ HC[i_healthCheck]
41
+ VW[i_verifyWebhook]
42
+ ST[i_status]
43
+ VC[i_validateCredentials]
44
+ end
45
+ end
46
+
47
+ subgraph "Core Application"
48
+ subgraph "Message Processing"
49
+ MP[Message Processor]
50
+ MR[Message Router]
51
+ MS[Message Store]
52
+ end
53
+
54
+ subgraph "Event System"
55
+ EB[Event Bus]
56
+ EH[Event Handlers]
57
+ end
58
+ end
59
+
60
+ subgraph "Infrastructure"
61
+ DS[Datasources]
62
+ CA[Cache]
63
+ PE[Permissions]
64
+ CH[Context Helpers]
65
+ end
66
+
67
+ %% External webhook flows
68
+ WA -->|Webhook| WAS
69
+ TG -->|Webhook| TGS
70
+ DC -->|Webhook| DCS
71
+ SL -->|Webhook| SLS
72
+
73
+ %% Integration service actions
74
+ WAS --> RW
75
+ WAS --> SM
76
+ WAS --> HC
77
+
78
+ %% Service infrastructure
79
+ WAS --> DS
80
+ TGS --> CA
81
+ DCS --> PE
82
+ SLS --> CH
83
+
84
+ %% Event flows
85
+ RW -->|integration.message.received| EB
86
+ SM -->|integration.message.sent| EB
87
+ SM -->|integration.message.failed| EB
88
+
89
+ %% Processing flows
90
+ EB --> EH
91
+ EH --> MP
92
+ MP --> MR
93
+ MR --> MS
94
+
95
+ %% Response flows
96
+ MP -->|Send Response| SM
97
+ ```
98
+
99
+ ## Integration Service Structure
100
+
101
+ ### Core Components
102
+
103
+ 1. **defineIntegration()** - Factory function that creates integration services
104
+ 2. **Security Helpers** - Webhook validation, timestamp checking, correlation IDs
105
+ 3. **Retry Mechanism** - Exponential backoff for failed operations
106
+ 4. **Standard Actions** - Predefined actions for common integration operations
107
+
108
+ ### Standard Actions
109
+
110
+ All integration services automatically get these actions:
111
+
112
+ | Action | REST Endpoint | Purpose |
113
+ |--------|---------------|---------|
114
+ | `i_receiveWebhook` | `POST /webhook` | Receive and process incoming webhooks |
115
+ | `i_sendMessage` | `POST /send` | Send messages to external platform |
116
+ | `i_healthCheck` | `GET /health` | Check integration health status |
117
+ | `i_verifyWebhook` | `GET /webhook` | Verify webhook subscriptions |
118
+ | `i_status` | `GET /status` | Get integration status information |
119
+ | `i_validateCredentials` | `POST /validate` | Validate platform credentials |
120
+
121
+ ### Service Methods
122
+
123
+ Integration-specific logic is implemented as service methods:
124
+
125
+ | Method | Purpose | Required |
126
+ |--------|---------|----------|
127
+ | `normalize()` | Convert platform message to unified format | ✅ |
128
+ | `sendMessage()` | Send message to platform | ✅ |
129
+ | `transform()` | Transform outbound message to platform format | ❌ |
130
+ | `validateWebhook()` | Validate webhook payload structure | ❌ |
131
+ | `validateSignature()` | Validate webhook signature | ❌ |
132
+ | `verifyWebhook()` | Handle webhook verification challenge | ❌ |
133
+ | `checkHealth()` | Custom health check logic | ❌ |
134
+ | `validateCredentials()` | Validate platform credentials | ❌ |
135
+
136
+ ## Usage Example
137
+
138
+ ### Basic WhatsApp Integration
139
+
140
+ ```typescript
141
+ import {defineIntegration} from '@pkg/sdk';
142
+ import type {BaseIntegration, NormalizedMessage, WebhookEvent} from '@pkg/sdk';
143
+
144
+ const whatsappIntegration: BaseIntegration = {
145
+ id: 'whatsapp-business',
146
+ name: 'WhatsApp Business',
147
+ platform: 'whatsapp',
148
+ version: '1.0.0',
149
+ status: 'ACTIVE',
150
+ capabilities: ['SEND_MESSAGE', 'RECEIVE_MESSAGE', 'WEBHOOK_VALIDATION']
151
+ };
152
+
153
+ export default defineIntegration({
154
+ name: 'whatsapp',
155
+ integration: whatsappIntegration,
156
+
157
+ // Required: Convert WhatsApp webhook to normalized message
158
+ async normalize(webhook: WebhookEvent): Promise<NormalizedMessage[]> {
159
+ const messages: NormalizedMessage[] = [];
160
+
161
+ for (const entry of webhook.rawPayload.entry || []) {
162
+ for (const change of entry.changes || []) {
163
+ if (change.field === 'messages') {
164
+ for (const message of change.value.messages || []) {
165
+ messages.push({
166
+ id: message.id,
167
+ conversationId: message.from,
168
+ from: {
169
+ id: message.from,
170
+ name: change.value.contacts?.[0]?.profile?.name,
171
+ },
172
+ to: {
173
+ id: webhook.tenantId,
174
+ },
175
+ content: {
176
+ type: message.type === 'text' ? 'TEXT' : 'FILE',
177
+ text: message.text?.body,
178
+ media: message.image ? {
179
+ url: message.image.id,
180
+ mimeType: message.image.mime_type
181
+ } : undefined
182
+ },
183
+ timestamp: parseInt(message.timestamp) * 1000,
184
+ platform: 'whatsapp',
185
+ metadata: {
186
+ phoneNumberId: change.value.metadata.phone_number_id
187
+ }
188
+ });
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ return messages;
195
+ },
196
+
197
+ // Required: Send message to WhatsApp
198
+ async sendMessage(ctx, message, config) {
199
+ const phoneNumberId = config.credentials.phoneNumberId;
200
+ const accessToken = config.credentials.accessToken;
201
+
202
+ const response = await fetch(
203
+ `https://graph.facebook.com/v18.0/${phoneNumberId}/messages`,
204
+ {
205
+ method: 'POST',
206
+ headers: {
207
+ 'Authorization': `Bearer ${accessToken}`,
208
+ 'Content-Type': 'application/json'
209
+ },
210
+ body: JSON.stringify({
211
+ messaging_product: 'whatsapp',
212
+ to: message.to.id,
213
+ text: { body: message.content.text }
214
+ })
215
+ }
216
+ );
217
+
218
+ if (!response.ok) {
219
+ const error = await response.text();
220
+ return {
221
+ success: false,
222
+ error: new Error(`WhatsApp API error: ${error}`)
223
+ };
224
+ }
225
+
226
+ const result = await response.json();
227
+ return {
228
+ success: true,
229
+ messageId: result.messages[0].id,
230
+ metadata: { phoneNumberId }
231
+ };
232
+ },
233
+
234
+ // Optional: Validate webhook signature
235
+ validateSignature(webhook) {
236
+ const signature = webhook.headers['x-hub-signature-256'];
237
+ const secret = process.env.WHATSAPP_WEBHOOK_SECRET;
238
+
239
+ if (!signature || !secret) return false;
240
+
241
+ const expectedSignature = 'sha256=' +
242
+ crypto.createHmac('sha256', secret)
243
+ .update(webhook.rawBody || '')
244
+ .digest('hex');
245
+
246
+ return SecurityHelpers.secureCompare(signature, expectedSignature);
247
+ },
248
+
249
+ // Optional: Handle webhook verification
250
+ verifyWebhook(params) {
251
+ const { mode, token, challenge } = params;
252
+ const verifyToken = process.env.WHATSAPP_VERIFY_TOKEN;
253
+
254
+ if (mode === 'subscribe' && token === verifyToken) {
255
+ return challenge;
256
+ }
257
+ return null;
258
+ },
259
+
260
+ // Optional: Custom health check
261
+ async checkHealth(ctx, config) {
262
+ try {
263
+ const response = await fetch(
264
+ `https://graph.facebook.com/v18.0/${config.credentials.phoneNumberId}`,
265
+ {
266
+ headers: {
267
+ 'Authorization': `Bearer ${config.credentials.accessToken}`
268
+ }
269
+ }
270
+ );
271
+
272
+ return {
273
+ status: response.ok ? 'healthy' : 'unhealthy',
274
+ message: response.ok ? 'WhatsApp API accessible' : 'WhatsApp API error',
275
+ details: {
276
+ statusCode: response.status,
277
+ timestamp: new Date().toISOString()
278
+ }
279
+ };
280
+ } catch (error) {
281
+ return {
282
+ status: 'unhealthy',
283
+ message: error.message,
284
+ details: { timestamp: new Date().toISOString() }
285
+ };
286
+ }
287
+ }
288
+ });
289
+ ```
290
+
291
+ ### Advanced Integration with Datasources and Cache
292
+
293
+ ```typescript
294
+ import {defineIntegration} from '@pkg/sdk';
295
+ import {WhatsAppDatasource} from './datasources';
296
+
297
+ export default defineIntegration({
298
+ name: 'whatsapp',
299
+ integration: whatsappIntegration,
300
+
301
+ // Configure per-service datasources
302
+ datasources: {
303
+ whatsapp: WhatsAppDatasource
304
+ },
305
+
306
+ // Configure per-service cache
307
+ cache: {
308
+ redisUrl: process.env.WHATSAPP_REDIS_URL,
309
+ ttl: 5 * 60 * 1000, // 5 minutes
310
+ namespace: 'whatsapp'
311
+ },
312
+
313
+ async normalize(webhook) {
314
+ // Access datasource via context in actions
315
+ // this.datasources is not available in methods
316
+ // Use dependency injection pattern instead
317
+ return await this.processWebhookMessages(webhook);
318
+ },
319
+
320
+ async sendMessage(ctx, message, config) {
321
+ // Use cache for rate limiting
322
+ const rateLimitKey = `rate_limit:${config.credentials.phoneNumberId}`;
323
+ const currentCount = await ctx.cache.get(rateLimitKey) || 0;
324
+
325
+ if (currentCount >= 1000) {
326
+ return {
327
+ success: false,
328
+ error: new Error('Rate limit exceeded')
329
+ };
330
+ }
331
+
332
+ // Send message...
333
+ const result = await this.sendToWhatsApp(message, config);
334
+
335
+ // Update rate limit counter
336
+ await ctx.cache.set(rateLimitKey, currentCount + 1, 60 * 60 * 1000);
337
+
338
+ return result;
339
+ }
340
+ });
341
+ ```
342
+
343
+ ## Event Flow
344
+
345
+ ### Inbound Message Flow
346
+
347
+ 1. **Webhook Received** → `i_receiveWebhook` action
348
+ 2. **Security Validation** → Timestamp, signature, payload checks
349
+ 3. **Message Normalization** → Convert to unified format
350
+ 4. **Event Emission** → `integration.message.received` event
351
+ 5. **Message Processing** → Application-specific logic
352
+ 6. **Response Generation** → Optional automated responses
353
+
354
+ ### Outbound Message Flow
355
+
356
+ 1. **Send Request** → `i_sendMessage` action
357
+ 2. **Message Transformation** → Convert to platform format
358
+ 3. **Retry Logic** → Exponential backoff for failures
359
+ 4. **Platform API Call** → Send via platform API
360
+ 5. **Event Emission** → `integration.message.sent` or `integration.message.failed`
361
+ 6. **Status Tracking** → Update message delivery status
362
+
363
+ ## Security Features
364
+
365
+ ### Webhook Validation
366
+
367
+ - **Signature Verification**: HMAC-SHA256 validation using platform secrets
368
+ - **Replay Attack Prevention**: Timestamp validation (5-minute window)
369
+ - **Timing Attack Protection**: Constant-time string comparison
370
+ - **Correlation IDs**: Request tracking for debugging and audit trails
371
+
372
+ ### Best Practices
373
+
374
+ 1. **Always validate webhook signatures** in production
375
+ 2. **Use environment variables** for secrets and tokens
376
+ 3. **Implement proper error handling** with structured errors
377
+ 4. **Add correlation IDs** for request tracking
378
+ 5. **Use retry logic** for transient failures
379
+ 6. **Monitor health endpoints** for platform availability
380
+ 7. **Cache frequently accessed data** to reduce API calls
381
+
382
+ ## Infrastructure Integration
383
+
384
+ Integration services automatically inherit:
385
+
386
+ - **Datasource Access**: Database connections, external APIs
387
+ - **Cache Layer**: Redis/in-memory caching with TTL
388
+ - **Permission System**: Role-based access control
389
+ - **Context Helpers**: Common utilities and operations
390
+ - **Memoization**: Action result caching
391
+ - **Health Monitoring**: Automated health checks
392
+ - **Event System**: Pub/sub event broadcasting
393
+
394
+ This architecture provides a robust, scalable foundation for building messaging platform integrations while maintaining consistency, security, and observability across all platforms.
package/package.json CHANGED
@@ -1,17 +1,19 @@
1
1
  {
2
2
  "name": "@hiliosai/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
7
7
  },
8
8
  "scripts": {
9
9
  "build": "tsup",
10
- "format": "bunx prettier --write ./**/*.{ts,json,md,yaml}"
10
+ "format": "bunx prettier --write ./**/*.{ts,json,md,yaml}",
11
+ "typecheck": "tsc --noEmit"
11
12
  },
12
13
  "dependencies": {
13
14
  "@keyv/redis": "5.1.5",
14
15
  "@ltv/env": "4.0.3",
16
+ "@moleculer/channels": "0.2.0",
15
17
  "keyv": "5.5.5",
16
18
  "lodash": "4.17.21",
17
19
  "moleculer": "0.14.35"
@@ -24,4 +26,4 @@
24
26
  "bun-types": "latest"
25
27
  },
26
28
  "prettier": "@hiliosai/prettier"
27
- }
29
+ }
@@ -0,0 +1,122 @@
1
+ import env from '@ltv/env';
2
+
3
+ /**
4
+ * Base namespace for all channels
5
+ */
6
+ export const NAMESPACE = env.string('NAMESPACE', 'hios').toLowerCase();
7
+
8
+ export const CHANNELS = {
9
+ // Webhook processing channels
10
+ WEBHOOK: {
11
+ // Pattern: hios.webhook.{tenantId}.{platform}
12
+ PATTERN: `${NAMESPACE}.webhook.*.*`,
13
+ PREFIX: `${NAMESPACE}.webhook`,
14
+ build: (tenantId: string, platform: string) =>
15
+ `${NAMESPACE}.webhook.${tenantId}.${platform}`,
16
+ },
17
+
18
+ // Message processing channels
19
+ PROCESSING: {
20
+ // Pattern: hios.processing.{tenantId}.{messageType}
21
+ PATTERN: `${NAMESPACE}.processing.*.*`,
22
+ PREFIX: `${NAMESPACE}.processing`,
23
+ build: (tenantId: string, messageType: string) =>
24
+ `${NAMESPACE}.processing.${tenantId}.${messageType}`,
25
+ },
26
+
27
+ // Response/outbound message channels
28
+ RESPONSE: {
29
+ // Pattern: hios.response.{tenantId}.{platform}
30
+ PATTERN: `${NAMESPACE}.response.*.*`,
31
+ PREFIX: `${NAMESPACE}.response`,
32
+ build: (tenantId: string, platform: string) =>
33
+ `${NAMESPACE}.response.${tenantId}.${platform}`,
34
+ },
35
+
36
+ // System channels
37
+ SYSTEM: {
38
+ // Error handling
39
+ ERRORS: `${NAMESPACE}.system.errors`,
40
+
41
+ // Metrics and monitoring
42
+ METRICS: `${NAMESPACE}.system.metrics`,
43
+
44
+ // Health checks
45
+ HEALTH: `${NAMESPACE}.system.health`,
46
+
47
+ // Integration lifecycle events
48
+ INTEGRATION_REGISTERED: `${NAMESPACE}.system.integration.registered`,
49
+ INTEGRATION_UNREGISTERED: `${NAMESPACE}.system.integration.unregistered`,
50
+ },
51
+
52
+ // Dead letter queues
53
+ DLQ: {
54
+ // Failed webhook processing
55
+ WEBHOOK_FAILED: `${NAMESPACE}.dlq.webhook.failed`,
56
+
57
+ // Failed message sends
58
+ SEND_FAILED: `${NAMESPACE}.dlq.send.failed`,
59
+
60
+ // Failed processing
61
+ PROCESSING_FAILED: `${NAMESPACE}.dlq.processing.failed`,
62
+
63
+ // Build DLQ name for specific integration
64
+ buildSendFailed: (platform: string) =>
65
+ `${NAMESPACE}.dlq.send.${platform}.failed`,
66
+ },
67
+ } as const;
68
+
69
+ /**
70
+ * Channel Configuration Constants
71
+ */
72
+ export const CHANNEL_CONFIG = {
73
+ // Default settings for message channels
74
+ DEFAULTS: {
75
+ maxInFlight: 10,
76
+ maxRetries: 3,
77
+ deadLettering: {
78
+ enabled: true,
79
+ },
80
+ },
81
+
82
+ // High-priority channels (webhooks)
83
+ HIGH_PRIORITY: {
84
+ maxInFlight: 50,
85
+ maxRetries: 5,
86
+ deadLettering: {
87
+ enabled: true,
88
+ },
89
+ },
90
+
91
+ // Low-priority channels (metrics, logs)
92
+ LOW_PRIORITY: {
93
+ maxInFlight: 5,
94
+ maxRetries: 1,
95
+ deadLettering: {
96
+ enabled: false,
97
+ },
98
+ },
99
+ } as const;
100
+
101
+ /**
102
+ * Subject patterns for NATS JetStream
103
+ */
104
+ export const SUBJECTS = {
105
+ // All webhook subjects
106
+ WEBHOOK_ALL: `${NAMESPACE}.webhook.>`,
107
+
108
+ // All processing subjects
109
+ PROCESSING_ALL: `${NAMESPACE}.processing.>`,
110
+
111
+ // All response subjects
112
+ RESPONSE_ALL: `${NAMESPACE}.response.>`,
113
+
114
+ // All system subjects
115
+ SYSTEM_ALL: `${NAMESPACE}.system.>`,
116
+
117
+ // All DLQ subjects
118
+ DLQ_ALL: `${NAMESPACE}.dlq.>`,
119
+
120
+ // Wildcard for all HIOS subjects
121
+ ALL: `${NAMESPACE}.>`,
122
+ } as const;
@@ -1 +1,2 @@
1
1
  export * from './permissions';
2
+ export {default as moleculer} from './moleculer';
@@ -0,0 +1,8 @@
1
+ export const bulkheadConfig = {
2
+ // Enable feature.
3
+ enabled: false,
4
+ // Maximum concurrent executions.
5
+ concurrency: 10,
6
+ // Maximum size of queue
7
+ maxQueueSize: 100,
8
+ };
@@ -0,0 +1,102 @@
1
+ import env from '@ltv/env';
2
+ import {Middleware as createChannelsMiddleware} from '@moleculer/channels';
3
+ import {type Middleware} from 'moleculer';
4
+
5
+ import {SUBJECTS, NAMESPACE as cfgNamespace} from '../constants';
6
+
7
+ const NAMESPACE = cfgNamespace.toUpperCase();
8
+
9
+ const middleware = createChannelsMiddleware({
10
+ adapter: {
11
+ type: 'NATS',
12
+ options: {
13
+ nats: {
14
+ url: env.string('NATS_URL', 'nats://localhost:4222'),
15
+
16
+ /** Connection options for reliability */
17
+ connectionOptions: {
18
+ name: 'hios',
19
+ timeout: 10000,
20
+ reconnect: true,
21
+ maxReconnectAttempts: 10,
22
+ reconnectTimeWait: 2000,
23
+ maxReconnectTimeWait: 30000,
24
+ pingInterval: 20000,
25
+ maxPingOut: 2,
26
+ },
27
+
28
+ /**
29
+ * Stream configuration for multi-tenant messaging
30
+ *
31
+ * Environment variables for production:
32
+ * - NATS_MAX_MESSAGES: Default 100K (dev) -> 10M+ (prod)
33
+ * - NATS_MAX_BYTES_GB: Default 1GB (dev) -> 100GB+ (prod)
34
+ * - NATS_MAX_AGE_DAYS: Default 7 (dev) -> 30+ (prod)
35
+ * - NATS_MAX_MSG_SIZE_MB: Default 1MB (dev) -> 5MB (prod)
36
+ * - NATS_REPLICAS: Default 1 (dev) -> 3 (prod)
37
+ */
38
+ streamConfig: {
39
+ name: `${NAMESPACE}_MESSAGES`,
40
+ subjects: [
41
+ SUBJECTS.WEBHOOK_ALL,
42
+ SUBJECTS.PROCESSING_ALL,
43
+ SUBJECTS.RESPONSE_ALL,
44
+ SUBJECTS.SYSTEM_ALL,
45
+ SUBJECTS.DLQ_ALL,
46
+ ],
47
+ retention: 'limits',
48
+ max_msgs: env.int('NATS_MAX_MESSAGES', 100_000), // 100K for dev, 10M+ for prod
49
+ max_bytes: env.int('NATS_MAX_BYTES_GB', 1) * 1024 * 1024 * 1024, // 1GB for dev, 100GB+ for prod
50
+ max_age: env.int('NATS_MAX_AGE_DAYS', 7) * 24 * 60 * 60 * 1000000000, // 7 days dev, 30+ days prod
51
+ max_msg_size: env.int('NATS_MAX_MSG_SIZE_MB', 1) * 1024 * 1024, // 1MB dev, 5MB prod
52
+ storage: 'file', // Persistent storage
53
+ num_replicas: env.int('NATS_REPLICAS', 1), // 1 for dev, 3 for prod
54
+ discard: 'old', // Remove old messages when limits hit
55
+ duplicate_window: 2 * 60 * 1000000000, // 2 minutes dedup window
56
+ },
57
+
58
+ /** Consumer options optimized for LLM processing */
59
+ consumerOptions: {
60
+ config: {
61
+ // Start with new messages (don't replay old ones on restart)
62
+ deliver_policy: 'new',
63
+
64
+ // Explicit acknowledgment required (critical for LLM processing)
65
+ ack_policy: 'explicit',
66
+
67
+ // Allow 5 unacknowledged messages per consumer (rate limiting)
68
+ max_ack_pending: 5,
69
+
70
+ // Acknowledgment timeout for LLM processing (2 minutes)
71
+ ack_wait: 120 * 1000000000, // 2 minutes in nanoseconds
72
+
73
+ // Maximum delivery attempts before dead letter
74
+ max_deliver: 3,
75
+
76
+ // Backoff for failed message retries
77
+ backoff: [
78
+ 1000000000, // 1 second
79
+ 5000000000, // 5 seconds
80
+ 30000000000, // 30 seconds
81
+ ],
82
+ },
83
+ },
84
+ },
85
+
86
+ /** Application-level flow control */
87
+ maxInFlight: 5, // Limit concurrent LLM requests per service
88
+ maxRetries: 2, // App-level retries (NATS handles delivery retries)
89
+
90
+ /** Dead letter queue for failed messages */
91
+ deadLettering: {
92
+ enabled: true,
93
+ queueName: 'FAILED_MESSAGES',
94
+ // Send to dead letter after NATS max_deliver attempts
95
+ },
96
+ },
97
+ },
98
+ }) as unknown as Middleware;
99
+
100
+ export const ChannelsMiddleware: Middleware = {
101
+ ...middleware,
102
+ };
@@ -0,0 +1,17 @@
1
+ import type {Errors} from 'moleculer';
2
+
3
+ export const circuitBreakerConfig = {
4
+ // Enable feature
5
+ enabled: false,
6
+ // Threshold value. 0.5 means that 50% should be failed for tripping.
7
+ threshold: 0.5,
8
+ // Minimum request count. Below it, CB does not trip.
9
+ minRequestCount: 20,
10
+ // Number of seconds for time window.
11
+ windowTime: 60,
12
+ // Number of milliseconds to switch from open to half-open state
13
+ halfOpenTime: 10 * 1000,
14
+ // A function to check failed requests.
15
+ check: (err: Errors.MoleculerError | Error) =>
16
+ (err as Errors.MoleculerError).code >= 500,
17
+ };