@x12i/ai-gateway 7.9.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.
- package/README.md +4259 -0
- package/config.defaults.json +31 -0
- package/dist/activity-manager.d.ts +206 -0
- package/dist/activity-manager.js +1051 -0
- package/dist/config/activity-tracking-config.d.ts +11 -0
- package/dist/config/activity-tracking-config.js +15 -0
- package/dist/config.defaults.json +31 -0
- package/dist/content-normalizer/content-normalizer.d.ts +46 -0
- package/dist/content-normalizer/content-normalizer.js +393 -0
- package/dist/content-normalizer/index.d.ts +7 -0
- package/dist/content-normalizer/index.js +6 -0
- package/dist/content-normalizer/types.d.ts +33 -0
- package/dist/content-normalizer/types.js +4 -0
- package/dist/defaults/instructions-blocks.json +61 -0
- package/dist/defaults/model-config.json +16 -0
- package/dist/defaults/template-rendering.json +6 -0
- package/dist/flex-md-loader.d.ts +109 -0
- package/dist/flex-md-loader.js +940 -0
- package/dist/gateway-config.d.ts +49 -0
- package/dist/gateway-config.js +292 -0
- package/dist/gateway-conversion.d.ts +29 -0
- package/dist/gateway-conversion.js +174 -0
- package/dist/gateway-instructions.d.ts +30 -0
- package/dist/gateway-instructions.js +62 -0
- package/dist/gateway-memory.d.ts +51 -0
- package/dist/gateway-memory.js +207 -0
- package/dist/gateway-messages.d.ts +23 -0
- package/dist/gateway-messages.js +83 -0
- package/dist/gateway-meta.d.ts +25 -0
- package/dist/gateway-meta.js +87 -0
- package/dist/gateway-provider-auto-register.d.ts +17 -0
- package/dist/gateway-provider-auto-register.js +159 -0
- package/dist/gateway-provider.d.ts +54 -0
- package/dist/gateway-provider.js +202 -0
- package/dist/gateway-rate-limiter-constants.d.ts +16 -0
- package/dist/gateway-rate-limiter-constants.js +16 -0
- package/dist/gateway-rate-limiter.d.ts +56 -0
- package/dist/gateway-rate-limiter.js +107 -0
- package/dist/gateway-retry.d.ts +49 -0
- package/dist/gateway-retry.js +204 -0
- package/dist/gateway-utils.d.ts +21 -0
- package/dist/gateway-utils.js +181 -0
- package/dist/gateway-validation.d.ts +13 -0
- package/dist/gateway-validation.js +50 -0
- package/dist/gateway.d.ts +39 -0
- package/dist/gateway.js +430 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +55 -0
- package/dist/instruction-errors.d.ts +16 -0
- package/dist/instruction-errors.js +29 -0
- package/dist/instruction-optimizer.d.ts +113 -0
- package/dist/instruction-optimizer.js +293 -0
- package/dist/instructions-parser.d.ts +31 -0
- package/dist/instructions-parser.js +56 -0
- package/dist/logger-factory.d.ts +17 -0
- package/dist/logger-factory.js +42 -0
- package/dist/message-builder.d.ts +41 -0
- package/dist/message-builder.js +522 -0
- package/dist/object-types-library-integration.d.ts +22 -0
- package/dist/object-types-library-integration.js +27 -0
- package/dist/object-types-library.d.ts +351 -0
- package/dist/object-types-library.js +210 -0
- package/dist/output-auditor.d.ts +44 -0
- package/dist/output-auditor.js +49 -0
- package/dist/request-report-generator.d.ts +60 -0
- package/dist/request-report-generator.js +169 -0
- package/dist/response-analyzer/format-type-detector.d.ts +35 -0
- package/dist/response-analyzer/format-type-detector.js +115 -0
- package/dist/response-analyzer/index.d.ts +9 -0
- package/dist/response-analyzer/index.js +8 -0
- package/dist/response-analyzer/object-type-detector.d.ts +42 -0
- package/dist/response-analyzer/object-type-detector.js +95 -0
- package/dist/response-analyzer/response-analyzer.d.ts +38 -0
- package/dist/response-analyzer/response-analyzer.js +97 -0
- package/dist/response-analyzer/types.d.ts +97 -0
- package/dist/response-analyzer/types.js +4 -0
- package/dist/response-fallback-fixer.d.ts +11 -0
- package/dist/response-fallback-fixer.js +123 -0
- package/dist/runtime-objects.d.ts +52 -0
- package/dist/runtime-objects.js +46 -0
- package/dist/template-parser.d.ts +58 -0
- package/dist/template-parser.js +99 -0
- package/dist/template-render-merge.d.ts +9 -0
- package/dist/template-render-merge.js +40 -0
- package/dist/troubleshooting-helper.d.ts +123 -0
- package/dist/troubleshooting-helper.js +596 -0
- package/dist/types.d.ts +1173 -0
- package/dist/types.js +6 -0
- package/dist/usage-tracker.d.ts +78 -0
- package/dist/usage-tracker.js +79 -0
- package/dist-cjs/activity-manager.cjs +1056 -0
- package/dist-cjs/activity-manager.d.ts +206 -0
- package/dist-cjs/config/activity-tracking-config.cjs +18 -0
- package/dist-cjs/config/activity-tracking-config.d.ts +11 -0
- package/dist-cjs/config.defaults.json +31 -0
- package/dist-cjs/content-normalizer/content-normalizer.cjs +398 -0
- package/dist-cjs/content-normalizer/content-normalizer.d.ts +46 -0
- package/dist-cjs/content-normalizer/index.cjs +12 -0
- package/dist-cjs/content-normalizer/index.d.ts +7 -0
- package/dist-cjs/content-normalizer/types.cjs +5 -0
- package/dist-cjs/content-normalizer/types.d.ts +33 -0
- package/dist-cjs/defaults/instructions-blocks.json +61 -0
- package/dist-cjs/defaults/model-config.json +16 -0
- package/dist-cjs/defaults/template-rendering.json +6 -0
- package/dist-cjs/flex-md-loader.cjs +986 -0
- package/dist-cjs/flex-md-loader.d.ts +109 -0
- package/dist-cjs/gateway-config.cjs +331 -0
- package/dist-cjs/gateway-config.d.ts +49 -0
- package/dist-cjs/gateway-conversion.cjs +212 -0
- package/dist-cjs/gateway-conversion.d.ts +29 -0
- package/dist-cjs/gateway-instructions.cjs +67 -0
- package/dist-cjs/gateway-instructions.d.ts +30 -0
- package/dist-cjs/gateway-memory.cjs +211 -0
- package/dist-cjs/gateway-memory.d.ts +51 -0
- package/dist-cjs/gateway-messages.cjs +86 -0
- package/dist-cjs/gateway-messages.d.ts +23 -0
- package/dist-cjs/gateway-meta.cjs +90 -0
- package/dist-cjs/gateway-meta.d.ts +25 -0
- package/dist-cjs/gateway-provider-auto-register.cjs +195 -0
- package/dist-cjs/gateway-provider-auto-register.d.ts +17 -0
- package/dist-cjs/gateway-provider.cjs +214 -0
- package/dist-cjs/gateway-provider.d.ts +54 -0
- package/dist-cjs/gateway-rate-limiter-constants.cjs +19 -0
- package/dist-cjs/gateway-rate-limiter-constants.d.ts +16 -0
- package/dist-cjs/gateway-rate-limiter.cjs +111 -0
- package/dist-cjs/gateway-rate-limiter.d.ts +56 -0
- package/dist-cjs/gateway-retry.cjs +212 -0
- package/dist-cjs/gateway-retry.d.ts +49 -0
- package/dist-cjs/gateway-utils.cjs +219 -0
- package/dist-cjs/gateway-utils.d.ts +21 -0
- package/dist-cjs/gateway-validation.cjs +54 -0
- package/dist-cjs/gateway-validation.d.ts +13 -0
- package/dist-cjs/gateway.cjs +434 -0
- package/dist-cjs/gateway.d.ts +39 -0
- package/dist-cjs/index.cjs +108 -0
- package/dist-cjs/index.d.ts +36 -0
- package/dist-cjs/instruction-errors.cjs +34 -0
- package/dist-cjs/instruction-errors.d.ts +16 -0
- package/dist-cjs/instruction-optimizer.cjs +299 -0
- package/dist-cjs/instruction-optimizer.d.ts +113 -0
- package/dist-cjs/instructions-parser.cjs +61 -0
- package/dist-cjs/instructions-parser.d.ts +31 -0
- package/dist-cjs/logger-factory.cjs +45 -0
- package/dist-cjs/logger-factory.d.ts +17 -0
- package/dist-cjs/message-builder.cjs +558 -0
- package/dist-cjs/message-builder.d.ts +41 -0
- package/dist-cjs/object-types-library-integration.cjs +32 -0
- package/dist-cjs/object-types-library-integration.d.ts +22 -0
- package/dist-cjs/object-types-library.cjs +215 -0
- package/dist-cjs/object-types-library.d.ts +351 -0
- package/dist-cjs/output-auditor.cjs +52 -0
- package/dist-cjs/output-auditor.d.ts +44 -0
- package/dist-cjs/request-report-generator.cjs +172 -0
- package/dist-cjs/request-report-generator.d.ts +60 -0
- package/dist-cjs/response-analyzer/format-type-detector.cjs +119 -0
- package/dist-cjs/response-analyzer/format-type-detector.d.ts +35 -0
- package/dist-cjs/response-analyzer/index.cjs +14 -0
- package/dist-cjs/response-analyzer/index.d.ts +9 -0
- package/dist-cjs/response-analyzer/object-type-detector.cjs +99 -0
- package/dist-cjs/response-analyzer/object-type-detector.d.ts +42 -0
- package/dist-cjs/response-analyzer/response-analyzer.cjs +101 -0
- package/dist-cjs/response-analyzer/response-analyzer.d.ts +38 -0
- package/dist-cjs/response-analyzer/types.cjs +5 -0
- package/dist-cjs/response-analyzer/types.d.ts +97 -0
- package/dist-cjs/response-fallback-fixer.cjs +126 -0
- package/dist-cjs/response-fallback-fixer.d.ts +11 -0
- package/dist-cjs/runtime-objects.cjs +52 -0
- package/dist-cjs/runtime-objects.d.ts +52 -0
- package/dist-cjs/template-parser.cjs +136 -0
- package/dist-cjs/template-parser.d.ts +58 -0
- package/dist-cjs/template-render-merge.cjs +43 -0
- package/dist-cjs/template-render-merge.d.ts +9 -0
- package/dist-cjs/troubleshooting-helper.cjs +611 -0
- package/dist-cjs/troubleshooting-helper.d.ts +123 -0
- package/dist-cjs/types.cjs +7 -0
- package/dist-cjs/types.d.ts +1173 -0
- package/dist-cjs/usage-tracker.cjs +83 -0
- package/dist-cjs/usage-tracker.d.ts +78 -0
- package/package.json +91 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Retry Module
|
|
3
|
+
* Handles retry logic for network and server errors
|
|
4
|
+
*
|
|
5
|
+
* NOTE: Retry delays use SIMPLE SLEEP (not smart rate limiting).
|
|
6
|
+
* Between-calls rate limiting is handled separately in gateway-rate-limiter.ts (smart).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Determines if an error is a network error (fetch failed, DNS, connectivity)
|
|
10
|
+
*/
|
|
11
|
+
export function isNetworkError(error) {
|
|
12
|
+
const message = error.message.toLowerCase();
|
|
13
|
+
const networkPatterns = [
|
|
14
|
+
'fetch failed',
|
|
15
|
+
'network error',
|
|
16
|
+
'dns',
|
|
17
|
+
'connectivity',
|
|
18
|
+
'connection',
|
|
19
|
+
'econnrefused',
|
|
20
|
+
'enotfound',
|
|
21
|
+
'etimedout',
|
|
22
|
+
'econnreset',
|
|
23
|
+
'api endpoint unavailability'
|
|
24
|
+
];
|
|
25
|
+
return networkPatterns.some(pattern => message.includes(pattern));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extracts HTTP status code from error message or error object properties
|
|
29
|
+
*/
|
|
30
|
+
export function extractHttpStatusCode(error) {
|
|
31
|
+
// Try to extract from error message
|
|
32
|
+
const statusMatch = error.message.match(/\b(4\d{2}|5\d{2})\b/);
|
|
33
|
+
if (statusMatch) {
|
|
34
|
+
return parseInt(statusMatch[1], 10);
|
|
35
|
+
}
|
|
36
|
+
// Try to extract from error object properties
|
|
37
|
+
const errorAny = error;
|
|
38
|
+
if (errorAny.statusCode) {
|
|
39
|
+
return errorAny.statusCode;
|
|
40
|
+
}
|
|
41
|
+
if (errorAny.status) {
|
|
42
|
+
return errorAny.status;
|
|
43
|
+
}
|
|
44
|
+
if (errorAny.response?.status) {
|
|
45
|
+
return errorAny.response.status;
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Determines if an HTTP error should be retried
|
|
51
|
+
* Retryable: 429 (throttling), 5xx (server errors)
|
|
52
|
+
* Non-retryable: 4xx except 429 (client errors)
|
|
53
|
+
*/
|
|
54
|
+
export function isRetryableHttpError(error, statusCode) {
|
|
55
|
+
// 429 (throttling) is retryable
|
|
56
|
+
if (statusCode === 429) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
// 5xx (server errors) are retryable
|
|
60
|
+
if (statusCode >= 500 && statusCode < 600) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
// 4xx (client errors) are NOT retryable except 429
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Determines if an error should be retried
|
|
68
|
+
*/
|
|
69
|
+
export function isRetryableError(error) {
|
|
70
|
+
// Network errors are always retryable
|
|
71
|
+
if (isNetworkError(error)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
// Check HTTP status code
|
|
75
|
+
const statusCode = extractHttpStatusCode(error);
|
|
76
|
+
if (statusCode !== undefined) {
|
|
77
|
+
return isRetryableHttpError(error, statusCode);
|
|
78
|
+
}
|
|
79
|
+
// Timeout errors are retryable
|
|
80
|
+
if (error.name === 'TimeoutError' || error.message.toLowerCase().includes('timeout')) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// Default: don't retry unknown errors
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sleep helper for retry delays
|
|
88
|
+
*/
|
|
89
|
+
export function sleep(ms) {
|
|
90
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Invokes router with retry logic for network and server errors
|
|
94
|
+
* Returns response and retry metadata
|
|
95
|
+
*/
|
|
96
|
+
export async function invokeWithRetry(routerRequest, retryConfig, jobId, router, logger) {
|
|
97
|
+
const maxRetries = retryConfig.maxRetries ?? 3;
|
|
98
|
+
const initialDelay = retryConfig.initialDelay ?? 1000;
|
|
99
|
+
const maxDelay = retryConfig.maxDelay ?? 30000;
|
|
100
|
+
const backoffMultiplier = retryConfig.backoffMultiplier ?? 2;
|
|
101
|
+
const enableJitter = retryConfig.enableJitter ?? true;
|
|
102
|
+
const throttlingDelay = retryConfig.throttlingDelay ?? 5000;
|
|
103
|
+
let lastError;
|
|
104
|
+
const retryAttempts = [];
|
|
105
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
106
|
+
try {
|
|
107
|
+
const response = await router.invoke(routerRequest);
|
|
108
|
+
// Log success after retry if this wasn't the first attempt
|
|
109
|
+
if (attempt > 0) {
|
|
110
|
+
logger.info('Request succeeded after retry', {
|
|
111
|
+
jobId,
|
|
112
|
+
attempt: attempt + 1,
|
|
113
|
+
totalAttempts: attempt + 1,
|
|
114
|
+
retryCount: retryAttempts.length
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// Return response with retry metadata if there were retries
|
|
118
|
+
if (retryAttempts.length > 0) {
|
|
119
|
+
return {
|
|
120
|
+
response,
|
|
121
|
+
retryMetadata: {
|
|
122
|
+
retryCount: retryAttempts.length,
|
|
123
|
+
retryAttempts
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return { response };
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
131
|
+
// Check if error is retryable
|
|
132
|
+
if (!isRetryableError(lastError)) {
|
|
133
|
+
logger.debug('Non-retryable error, not retrying', {
|
|
134
|
+
jobId,
|
|
135
|
+
error: lastError.message,
|
|
136
|
+
errorName: lastError.name,
|
|
137
|
+
attempt: attempt + 1
|
|
138
|
+
});
|
|
139
|
+
throw lastError;
|
|
140
|
+
}
|
|
141
|
+
// If this was the last attempt, throw the error
|
|
142
|
+
if (attempt >= maxRetries) {
|
|
143
|
+
logger.warn('Max retries exceeded', {
|
|
144
|
+
jobId,
|
|
145
|
+
error: lastError.message,
|
|
146
|
+
errorName: lastError.name,
|
|
147
|
+
totalAttempts: attempt + 1,
|
|
148
|
+
maxRetries
|
|
149
|
+
});
|
|
150
|
+
throw lastError;
|
|
151
|
+
}
|
|
152
|
+
// Calculate delay with exponential backoff
|
|
153
|
+
const baseDelay = initialDelay * Math.pow(backoffMultiplier, attempt);
|
|
154
|
+
let delay = Math.min(baseDelay, maxDelay);
|
|
155
|
+
// Add extra delay for throttling
|
|
156
|
+
const statusCode = extractHttpStatusCode(lastError);
|
|
157
|
+
if (statusCode === 429) {
|
|
158
|
+
delay += throttlingDelay;
|
|
159
|
+
logger.debug('Throttling detected, adding extra delay', {
|
|
160
|
+
jobId,
|
|
161
|
+
throttlingDelay,
|
|
162
|
+
baseDelay: Math.round(delay - throttlingDelay)
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// Add jitter if enabled
|
|
166
|
+
if (enableJitter) {
|
|
167
|
+
const jitter = Math.random() * delay * 0.3; // Up to 30% jitter
|
|
168
|
+
delay += jitter;
|
|
169
|
+
}
|
|
170
|
+
// Classify error type for logging
|
|
171
|
+
const errorType = isNetworkError(lastError)
|
|
172
|
+
? 'network'
|
|
173
|
+
: statusCode !== undefined
|
|
174
|
+
? `http-${statusCode}`
|
|
175
|
+
: 'timeout';
|
|
176
|
+
// Record retry attempt for activity tracking
|
|
177
|
+
retryAttempts.push({
|
|
178
|
+
attempt: attempt + 1,
|
|
179
|
+
timestamp: Date.now(),
|
|
180
|
+
error: lastError.message,
|
|
181
|
+
errorType,
|
|
182
|
+
delayMs: Math.round(delay)
|
|
183
|
+
});
|
|
184
|
+
logger.warn('Retrying request after error', {
|
|
185
|
+
jobId,
|
|
186
|
+
error: lastError.message,
|
|
187
|
+
errorName: lastError.name,
|
|
188
|
+
errorType,
|
|
189
|
+
attempt: attempt + 1,
|
|
190
|
+
maxRetries: maxRetries + 1,
|
|
191
|
+
delayMs: Math.round(delay),
|
|
192
|
+
statusCode
|
|
193
|
+
});
|
|
194
|
+
await sleep(delay);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// If we get here, all retries exhausted - throw error with retry metadata
|
|
198
|
+
const errorWithRetries = lastError;
|
|
199
|
+
errorWithRetries.retryMetadata = {
|
|
200
|
+
retryCount: retryAttempts.length,
|
|
201
|
+
retryAttempts
|
|
202
|
+
};
|
|
203
|
+
throw errorWithRetries;
|
|
204
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Utilities Module
|
|
3
|
+
* Handles utility functions
|
|
4
|
+
*/
|
|
5
|
+
import type { ChatRequest, GatewayConfig } from './types.js';
|
|
6
|
+
import type { Logxer } from '@x12i/logxer';
|
|
7
|
+
/**
|
|
8
|
+
* Generates MD5 hash of a string
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateMD5Hash(text: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Auto-generates taskTypeId from MD5 hash of pre-parsed instructions if not provided
|
|
13
|
+
*/
|
|
14
|
+
export declare function ensureTaskTypeId(request: ChatRequest, logger: Logxer): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Merges config with defaults
|
|
17
|
+
* Supports using internal system action defaults (internalSkill or skillAudit) when useInternalDefaults is set
|
|
18
|
+
*/
|
|
19
|
+
export declare function mergeConfig(request: ChatRequest & {
|
|
20
|
+
useInternalDefaults?: 'skill' | 'audit';
|
|
21
|
+
}, config: GatewayConfig, logger: Logxer): Promise<ChatRequest['config']>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Utilities Module
|
|
3
|
+
* Handles utility functions
|
|
4
|
+
*/
|
|
5
|
+
import * as crypto from 'crypto';
|
|
6
|
+
import { getPreParsedInstructions } from './gateway-instructions.js';
|
|
7
|
+
import { getModelMaxTokensFromFlexMd } from './flex-md-loader.js';
|
|
8
|
+
/**
|
|
9
|
+
* Generates MD5 hash of a string
|
|
10
|
+
*/
|
|
11
|
+
export function generateMD5Hash(text) {
|
|
12
|
+
return crypto.createHash('md5').update(text).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Auto-generates taskTypeId from MD5 hash of pre-parsed instructions if not provided
|
|
16
|
+
*/
|
|
17
|
+
export async function ensureTaskTypeId(request, logger) {
|
|
18
|
+
if (request.taskTypeId) {
|
|
19
|
+
return request.taskTypeId;
|
|
20
|
+
}
|
|
21
|
+
const preParsedInstructions = getPreParsedInstructions(request.instructions);
|
|
22
|
+
// Generate MD5 hash
|
|
23
|
+
const taskTypeId = generateMD5Hash(preParsedInstructions);
|
|
24
|
+
logger.debug('Auto-generated taskTypeId from instructions MD5 hash', {
|
|
25
|
+
jobId: request.jobId,
|
|
26
|
+
taskTypeId,
|
|
27
|
+
instructionsLength: preParsedInstructions.length,
|
|
28
|
+
instructionsPreview: preParsedInstructions.substring(0, 100) + (preParsedInstructions.length > 100 ? '...' : '')
|
|
29
|
+
});
|
|
30
|
+
return taskTypeId;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Merges config with defaults
|
|
34
|
+
* Supports using internal system action defaults (internalSkill or skillAudit) when useInternalDefaults is set
|
|
35
|
+
*/
|
|
36
|
+
export async function mergeConfig(request, config, logger) {
|
|
37
|
+
const useInternalDefaults = request.useInternalDefaults;
|
|
38
|
+
const internalDefaults = useInternalDefaults
|
|
39
|
+
? (useInternalDefaults === 'skill'
|
|
40
|
+
? config.internalSystemActions?.internalSkill
|
|
41
|
+
: config.internalSystemActions?.skillAudit)
|
|
42
|
+
: undefined;
|
|
43
|
+
logger.verbose('Merging request config with gateway defaults', {
|
|
44
|
+
jobId: request.jobId,
|
|
45
|
+
hasRequestConfig: !!request.config,
|
|
46
|
+
hasModelConfig: !!request.modelConfig,
|
|
47
|
+
requestModel: request.config?.model,
|
|
48
|
+
modelConfigModel: request.modelConfig?.model || request.modelConfig?.modelId,
|
|
49
|
+
requestProvider: request.config?.provider,
|
|
50
|
+
modelConfigProvider: request.modelConfig?.provider,
|
|
51
|
+
defaultEngine: config.defaultEngine,
|
|
52
|
+
useInternalDefaults,
|
|
53
|
+
hasInternalDefaults: !!internalDefaults
|
|
54
|
+
});
|
|
55
|
+
// Default model to "gpt-5-nano" if nothing is provided (most permissive - always works)
|
|
56
|
+
const defaultModel = 'gpt-5-nano';
|
|
57
|
+
// Priority: modelConfig > request.config > internalSystemActions[useInternalDefaults] > gateway defaults
|
|
58
|
+
// First, merge modelConfig into a config-like object if present
|
|
59
|
+
const modelConfigAsConfig = request.modelConfig ? {
|
|
60
|
+
model: request.modelConfig.model || request.modelConfig.modelId,
|
|
61
|
+
provider: request.modelConfig.provider,
|
|
62
|
+
temperature: request.modelConfig.temperature,
|
|
63
|
+
maxTokens: request.modelConfig.maxTokens,
|
|
64
|
+
topP: request.modelConfig.topP,
|
|
65
|
+
frequencyPenalty: request.modelConfig.frequencyPenalty,
|
|
66
|
+
presencePenalty: request.modelConfig.presencePenalty,
|
|
67
|
+
stop: request.modelConfig.stop,
|
|
68
|
+
// Include any additional properties from modelConfig
|
|
69
|
+
...Object.fromEntries(Object.entries(request.modelConfig).filter(([key]) => !['model', 'modelId', 'provider', 'temperature', 'maxTokens', 'topP', 'frequencyPenalty', 'presencePenalty', 'stop'].includes(key)))
|
|
70
|
+
} : undefined;
|
|
71
|
+
const merged = {
|
|
72
|
+
// Start with gateway defaults (lowest priority)
|
|
73
|
+
temperature: config.temperature ?? 0.7,
|
|
74
|
+
// maxTokens will be set from flex-md if available, otherwise fallback to 2000
|
|
75
|
+
topP: config.topP ?? 1.0,
|
|
76
|
+
frequencyPenalty: config.frequencyPenalty ?? 0.0,
|
|
77
|
+
presencePenalty: config.presencePenalty ?? 0.0,
|
|
78
|
+
// Apply internal system action defaults (medium priority) if useInternalDefaults is set
|
|
79
|
+
...(internalDefaults ? {
|
|
80
|
+
model: internalDefaults.model,
|
|
81
|
+
provider: internalDefaults.engine || config.defaultEngine,
|
|
82
|
+
temperature: internalDefaults.temperature ?? config.temperature ?? 0.7,
|
|
83
|
+
// maxTokens from internalDefaults only if explicitly set, otherwise will be auto-detected
|
|
84
|
+
...(internalDefaults.maxTokens !== undefined ? { maxTokens: internalDefaults.maxTokens } : {})
|
|
85
|
+
} : {}),
|
|
86
|
+
// Request config overrides (higher priority)
|
|
87
|
+
...request.config,
|
|
88
|
+
// ModelConfig overrides (highest priority) - merge only defined values
|
|
89
|
+
...(modelConfigAsConfig ? Object.fromEntries(Object.entries(modelConfigAsConfig).filter(([_, value]) => value !== undefined)) : {}),
|
|
90
|
+
// Ensure model is set: modelConfig > request.config > internalDefaults > default
|
|
91
|
+
model: modelConfigAsConfig?.model || request.config?.model || internalDefaults?.model || defaultModel,
|
|
92
|
+
// Ensure provider is set: modelConfig > request.config > internalDefaults > gateway default
|
|
93
|
+
// Provider is required for router to know which provider to use
|
|
94
|
+
provider: modelConfigAsConfig?.provider || request.config?.provider || internalDefaults?.engine || config.defaultEngine
|
|
95
|
+
};
|
|
96
|
+
// Log if using default model
|
|
97
|
+
if (!request.config?.model && !internalDefaults?.model) {
|
|
98
|
+
logger.info('Using default model: gpt-5-nano (no model provided in request)', {
|
|
99
|
+
jobId: request.jobId,
|
|
100
|
+
note: 'Default model ensures requests always work regardless of configuration'
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// Auto-get maxTokens from flex-md if not explicitly set in ANY config source
|
|
104
|
+
// Check all possible sources: request.config, internalDefaults, gateway config
|
|
105
|
+
const maxTokensExplicitlySet = request.config?.maxTokens !== undefined ||
|
|
106
|
+
internalDefaults?.maxTokens !== undefined ||
|
|
107
|
+
config.maxTokens !== undefined;
|
|
108
|
+
if (!maxTokensExplicitlySet && merged.model && merged.provider) {
|
|
109
|
+
// Try to get maxTokens from flex-md
|
|
110
|
+
try {
|
|
111
|
+
const flexMdMaxTokens = await getModelMaxTokensFromFlexMd(merged.provider, merged.model);
|
|
112
|
+
if (flexMdMaxTokens && flexMdMaxTokens > 0) {
|
|
113
|
+
merged.maxTokens = flexMdMaxTokens;
|
|
114
|
+
logger.debug('Using maxTokens from flex-md', {
|
|
115
|
+
jobId: request.jobId,
|
|
116
|
+
model: merged.model,
|
|
117
|
+
provider: merged.provider,
|
|
118
|
+
maxTokens: merged.maxTokens,
|
|
119
|
+
source: 'flex-md'
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// flex-md doesn't have model info or returned invalid value - use fallback
|
|
124
|
+
merged.maxTokens = 2000;
|
|
125
|
+
logger.debug('Using fallback maxTokens (flex-md unavailable or no model info)', {
|
|
126
|
+
jobId: request.jobId,
|
|
127
|
+
model: merged.model,
|
|
128
|
+
provider: merged.provider,
|
|
129
|
+
maxTokens: merged.maxTokens,
|
|
130
|
+
note: 'Set maxTokens explicitly in config for custom values.'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
// Error loading flex-md or getting model info - use fallback
|
|
136
|
+
merged.maxTokens = 2000;
|
|
137
|
+
logger.debug('Using fallback maxTokens (flex-md error)', {
|
|
138
|
+
jobId: request.jobId,
|
|
139
|
+
model: merged.model,
|
|
140
|
+
provider: merged.provider,
|
|
141
|
+
maxTokens: merged.maxTokens,
|
|
142
|
+
error: error instanceof Error ? error.message : String(error),
|
|
143
|
+
note: 'Set maxTokens explicitly in config for custom values.'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (!merged.maxTokens) {
|
|
148
|
+
// If maxTokens wasn't set and wasn't auto-detected, use fallback
|
|
149
|
+
// This should rarely happen, but handle edge cases
|
|
150
|
+
merged.maxTokens = 2000;
|
|
151
|
+
logger.debug('Using fallback maxTokens (not auto-detected and not explicitly set)', {
|
|
152
|
+
jobId: request.jobId,
|
|
153
|
+
model: merged.model,
|
|
154
|
+
provider: merged.provider,
|
|
155
|
+
maxTokens: merged.maxTokens,
|
|
156
|
+
maxTokensExplicitlySet
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// maxTokens was explicitly set - log which source
|
|
161
|
+
const source = request.config?.maxTokens !== undefined ? 'request.config' :
|
|
162
|
+
internalDefaults?.maxTokens !== undefined ? `internalSystemActions.${useInternalDefaults}` :
|
|
163
|
+
'gateway.config';
|
|
164
|
+
logger.debug('Using explicitly set maxTokens', {
|
|
165
|
+
jobId: request.jobId,
|
|
166
|
+
model: merged.model,
|
|
167
|
+
provider: merged.provider,
|
|
168
|
+
maxTokens: merged.maxTokens,
|
|
169
|
+
source
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
logger.debug('Config merged', {
|
|
173
|
+
jobId: request.jobId,
|
|
174
|
+
finalModel: merged.model,
|
|
175
|
+
finalProvider: merged.provider,
|
|
176
|
+
finalTemperature: merged.temperature,
|
|
177
|
+
finalMaxTokens: merged.maxTokens,
|
|
178
|
+
usedInternalDefaults: !!internalDefaults
|
|
179
|
+
});
|
|
180
|
+
return merged;
|
|
181
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Validation Module
|
|
3
|
+
* Basic validation for clean proxy implementation
|
|
4
|
+
*/
|
|
5
|
+
import type { ChatRequest, AIRequest } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validates ChatRequest has required fields
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateChatRequest(request: ChatRequest): void;
|
|
10
|
+
/**
|
|
11
|
+
* Validates AIRequest has required fields
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateAIRequest(request: AIRequest): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Validation Module
|
|
3
|
+
* Basic validation for clean proxy implementation
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validates ChatRequest has required fields
|
|
7
|
+
*/
|
|
8
|
+
export function validateChatRequest(request) {
|
|
9
|
+
if (!request.aiRequestId) {
|
|
10
|
+
throw new Error('aiRequestId is required');
|
|
11
|
+
}
|
|
12
|
+
if (!request.agentId) {
|
|
13
|
+
throw new Error('agentId is required');
|
|
14
|
+
}
|
|
15
|
+
// Reject input field - it has been removed
|
|
16
|
+
if ('input' in request && request.input !== undefined) {
|
|
17
|
+
const err = new Error(`The 'input' field has been removed. Use workingMemory.input instead for template rendering. Prompt templates should contain {{input}} which will be resolved from workingMemory.input.`);
|
|
18
|
+
err.code = 'INPUT_FIELD_DEPRECATED';
|
|
19
|
+
err.details = {
|
|
20
|
+
field: 'input',
|
|
21
|
+
alternative: 'workingMemory.input',
|
|
22
|
+
message: 'Input data must be provided via workingMemory.input for template rendering'
|
|
23
|
+
};
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validates AIRequest has required fields
|
|
29
|
+
*/
|
|
30
|
+
export function validateAIRequest(request) {
|
|
31
|
+
if (!request.aiRequestId) {
|
|
32
|
+
throw new Error('aiRequestId is required for AI requests');
|
|
33
|
+
}
|
|
34
|
+
if (!request.agentId) {
|
|
35
|
+
throw new Error('agentId is required for AI requests');
|
|
36
|
+
}
|
|
37
|
+
// Reject input field - it has been removed
|
|
38
|
+
if ('input' in request && request.input !== undefined) {
|
|
39
|
+
const err = new Error(`The 'input' field has been removed. Use workingMemory.input instead for template rendering. Prompt templates should contain {{input}} which will be resolved from workingMemory.input.`);
|
|
40
|
+
err.code = 'INPUT_FIELD_DEPRECATED';
|
|
41
|
+
err.details = {
|
|
42
|
+
field: 'input',
|
|
43
|
+
alternative: 'workingMemory.input',
|
|
44
|
+
message: 'Input data must be provided via workingMemory.input for template rendering'
|
|
45
|
+
};
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
// Note: primaryObjectType and secondaryObjectTypes are now optional
|
|
49
|
+
// The gateway is a clean proxy - no object type validation logic
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @x12i/ai-gateway
|
|
3
|
+
*
|
|
4
|
+
* Simplified AI Gateway - Clean proxy implementation
|
|
5
|
+
*/
|
|
6
|
+
import { LLMProviderRouter } from '@x12i/ai-providers-router';
|
|
7
|
+
import type { GatewayConfig, ChatRequest, AIRequest, EnhancedLLMResponse } from './types.js';
|
|
8
|
+
import type { Logxer } from '@x12i/logxer';
|
|
9
|
+
import { ActivityManager } from './activity-manager.js';
|
|
10
|
+
/**
|
|
11
|
+
* Simplified AI Gateway - Clean proxy implementation
|
|
12
|
+
*/
|
|
13
|
+
export declare class AIGateway {
|
|
14
|
+
private router;
|
|
15
|
+
private config;
|
|
16
|
+
private logger;
|
|
17
|
+
private activityManager?;
|
|
18
|
+
private messageBuilderConfig?;
|
|
19
|
+
private _autoRegisterDone;
|
|
20
|
+
constructor(config?: GatewayConfig, activityManager?: ActivityManager);
|
|
21
|
+
/**
|
|
22
|
+
* Invoke chat request (without structured output requirements)
|
|
23
|
+
*/
|
|
24
|
+
invokeChat<TContent = unknown>(request: ChatRequest): Promise<EnhancedLLMResponse<TContent>>;
|
|
25
|
+
/**
|
|
26
|
+
* Invoke AI request (with structured output support)
|
|
27
|
+
*/
|
|
28
|
+
invoke<TContent = unknown>(request: AIRequest): Promise<EnhancedLLMResponse<TContent>>;
|
|
29
|
+
/**
|
|
30
|
+
* Build simple messages from request (instructions and prompt as literal template text; no registry).
|
|
31
|
+
*/
|
|
32
|
+
private buildSimpleMessages;
|
|
33
|
+
register(provider: any): void;
|
|
34
|
+
listProviders(): string[];
|
|
35
|
+
getRouter(): LLMProviderRouter;
|
|
36
|
+
getLogger(): Logxer;
|
|
37
|
+
getActivityManager(): ActivityManager | undefined;
|
|
38
|
+
setActivityManager(activityManager: ActivityManager): void;
|
|
39
|
+
}
|