@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,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Troubleshooting Helper Toolbox
|
|
3
|
+
*
|
|
4
|
+
* Utility functions to diagnose and test AI Gateway issues
|
|
5
|
+
* Based on TROUBLESHOOTING.md guide
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Converts JSON Schema to simple, human-readable format for LLM instructions
|
|
9
|
+
*
|
|
10
|
+
* Supports both formats:
|
|
11
|
+
* - JSON Schema format: { "type": "object", "properties": {...} }
|
|
12
|
+
* - Simple format: { "emails": ["<string> description"] }
|
|
13
|
+
*
|
|
14
|
+
* If schema is already in simple format, returns it as-is.
|
|
15
|
+
* If schema is in JSON Schema format, converts it to simple format.
|
|
16
|
+
*
|
|
17
|
+
* Simple format example:
|
|
18
|
+
* {
|
|
19
|
+
* "emails": [
|
|
20
|
+
* "<string> A valid email address extracted from the input text"
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* Format rules:
|
|
25
|
+
* - Arrays: Show as array with single example item containing type hint + description
|
|
26
|
+
* - Objects: Show nested structure recursively
|
|
27
|
+
* - Primitives: Show as type hint with description
|
|
28
|
+
* - Required fields: Implicit (just present in structure)
|
|
29
|
+
*
|
|
30
|
+
* @param schema - Schema object (JSON Schema or simple format)
|
|
31
|
+
* @returns Schema in simple format
|
|
32
|
+
*/
|
|
33
|
+
export function convertSchemaToSimpleFormat(schema) {
|
|
34
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
// Check if schema is already in simple format (no "type" or "properties" keys)
|
|
38
|
+
// Simple format: { "emails": ["<string> description"] }
|
|
39
|
+
// JSON Schema: { "type": "object", "properties": {...} }
|
|
40
|
+
const hasJsonSchemaStructure = schema.type !== undefined || schema.properties !== undefined;
|
|
41
|
+
if (!hasJsonSchemaStructure) {
|
|
42
|
+
// Already in simple format - return as-is
|
|
43
|
+
return schema;
|
|
44
|
+
}
|
|
45
|
+
// Convert from JSON Schema to simple format
|
|
46
|
+
const simpleSchema = {};
|
|
47
|
+
const properties = schema.properties || {};
|
|
48
|
+
const required = schema.required || [];
|
|
49
|
+
for (const [fieldName, fieldDef] of Object.entries(properties)) {
|
|
50
|
+
const field = fieldDef;
|
|
51
|
+
const fieldType = field.type || 'any';
|
|
52
|
+
const fieldDesc = field.description || '';
|
|
53
|
+
// Handle different field types
|
|
54
|
+
if (fieldType === 'array') {
|
|
55
|
+
const items = field.items || {};
|
|
56
|
+
const itemType = items.type || 'any';
|
|
57
|
+
const itemDesc = items.description || '';
|
|
58
|
+
// Build array item example: "<type> description"
|
|
59
|
+
let itemExample;
|
|
60
|
+
if (itemType === 'string') {
|
|
61
|
+
itemExample = `<string>${itemDesc ? ' ' + itemDesc : ''}`;
|
|
62
|
+
}
|
|
63
|
+
else if (itemType === 'number' || itemType === 'integer') {
|
|
64
|
+
itemExample = `<${itemType}>${itemDesc ? ' ' + itemDesc : ''}`;
|
|
65
|
+
}
|
|
66
|
+
else if (itemType === 'boolean') {
|
|
67
|
+
itemExample = `<boolean>${itemDesc ? ' ' + itemDesc : ''}`;
|
|
68
|
+
}
|
|
69
|
+
else if (itemType === 'object' && items.properties) {
|
|
70
|
+
// For object arrays, show nested structure as string representation
|
|
71
|
+
const nestedSimple = convertSchemaToSimpleFormat(items);
|
|
72
|
+
const nestedJson = JSON.stringify(nestedSimple, null, 2);
|
|
73
|
+
itemExample = `<object> ${itemDesc || 'Object with structure:'}\n${nestedJson}`;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
itemExample = `<${itemType}>${itemDesc ? ' ' + itemDesc : ''}`;
|
|
77
|
+
}
|
|
78
|
+
// Array format: [ "<type> description" ]
|
|
79
|
+
simpleSchema[fieldName] = [itemExample];
|
|
80
|
+
}
|
|
81
|
+
else if (fieldType === 'object' && field.properties) {
|
|
82
|
+
// Recursively convert nested objects
|
|
83
|
+
simpleSchema[fieldName] = convertSchemaToSimpleFormat(field);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// For primitive types, show as type hint with description
|
|
87
|
+
// Format: "<type> description" or just "<type>" if no description
|
|
88
|
+
let value;
|
|
89
|
+
if (fieldType === 'string') {
|
|
90
|
+
value = `<string>${fieldDesc ? ' ' + fieldDesc : ''}`;
|
|
91
|
+
}
|
|
92
|
+
else if (fieldType === 'number' || fieldType === 'integer') {
|
|
93
|
+
value = `<${fieldType}>${fieldDesc ? ' ' + fieldDesc : ''}`;
|
|
94
|
+
}
|
|
95
|
+
else if (fieldType === 'boolean') {
|
|
96
|
+
value = `<boolean>${fieldDesc ? ' ' + fieldDesc : ''}`;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
value = `<${fieldType}>${fieldDesc ? ' ' + fieldDesc : ''}`;
|
|
100
|
+
}
|
|
101
|
+
simpleSchema[fieldName] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return simpleSchema;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validates a single object type structure
|
|
108
|
+
* Handles both string (standard type) and object (custom type) formats
|
|
109
|
+
*/
|
|
110
|
+
function validateSingleObjectType(objType, context, index) {
|
|
111
|
+
const errors = [];
|
|
112
|
+
const warnings = [];
|
|
113
|
+
const indexLabel = index !== undefined ? `[${index}]` : '';
|
|
114
|
+
const fullContext = `${context}${indexLabel}`;
|
|
115
|
+
// If it's a string (standard type), validation passes
|
|
116
|
+
if (typeof objType === 'string') {
|
|
117
|
+
if (!objType.trim()) {
|
|
118
|
+
errors.push(`${fullContext} must be a non-empty string`);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
valid: errors.length === 0,
|
|
122
|
+
errors,
|
|
123
|
+
warnings
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Validate type field
|
|
127
|
+
if (!objType.type || typeof objType.type !== 'string' || objType.type.trim().length === 0) {
|
|
128
|
+
errors.push(`${fullContext}.type is required and must be a non-empty string`);
|
|
129
|
+
}
|
|
130
|
+
// Validate whenToUse field
|
|
131
|
+
if (!objType.whenToUse || typeof objType.whenToUse !== 'string' || objType.whenToUse.trim().length === 0) {
|
|
132
|
+
errors.push(`${fullContext}.whenToUse is required and must be a non-empty string`);
|
|
133
|
+
}
|
|
134
|
+
// Validate schema if provided (supports both JSON Schema and simple format)
|
|
135
|
+
if (objType.schema !== undefined) {
|
|
136
|
+
if (typeof objType.schema !== 'object' || objType.schema === null || Array.isArray(objType.schema)) {
|
|
137
|
+
errors.push(`${fullContext}.schema must be an object (JSON Schema or simple format)`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const schema = objType.schema;
|
|
141
|
+
// Validate properties if present (JSON Schema format)
|
|
142
|
+
if (schema.properties !== undefined) {
|
|
143
|
+
if (typeof schema.properties !== 'object' || schema.properties === null || Array.isArray(schema.properties)) {
|
|
144
|
+
errors.push(`${fullContext}.schema.properties must be an object`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Validate required if present (JSON Schema format)
|
|
148
|
+
if (schema.required !== undefined) {
|
|
149
|
+
if (!Array.isArray(schema.required)) {
|
|
150
|
+
errors.push(`${fullContext}.schema.required must be an array`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Warn about missing type in JSON Schema format
|
|
154
|
+
if (schema.type === undefined && schema.properties !== undefined) {
|
|
155
|
+
warnings.push(`${fullContext}.schema.type is recommended for JSON Schema format (should be 'object')`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
warnings.push(`${fullContext}.schema is optional but recommended for better validation`);
|
|
161
|
+
}
|
|
162
|
+
// Validate description if provided
|
|
163
|
+
if (objType.description !== undefined && typeof objType.description !== 'string') {
|
|
164
|
+
errors.push(`${fullContext}.description must be a string`);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
valid: errors.length === 0,
|
|
168
|
+
errors,
|
|
169
|
+
warnings
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Validates AIRequest structure
|
|
174
|
+
*/
|
|
175
|
+
export function validateAIRequest(request) {
|
|
176
|
+
const errors = [];
|
|
177
|
+
const warnings = [];
|
|
178
|
+
// Validate base fields
|
|
179
|
+
if (!request.jobId) {
|
|
180
|
+
errors.push('jobId is required');
|
|
181
|
+
}
|
|
182
|
+
if (!request.agentId) {
|
|
183
|
+
errors.push('agentId is required');
|
|
184
|
+
}
|
|
185
|
+
if (!request.instructions) {
|
|
186
|
+
warnings.push('instructions is optional (system-context will be used as fallback)');
|
|
187
|
+
}
|
|
188
|
+
if (!request.prompt) {
|
|
189
|
+
errors.push('Prompt is required (input field has been removed - use workingMemory.input instead)');
|
|
190
|
+
}
|
|
191
|
+
// Validate config
|
|
192
|
+
if (!request.config) {
|
|
193
|
+
errors.push('config is required');
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
if (!request.config.model) {
|
|
197
|
+
errors.push('config.model is required (no default models allowed)');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
valid: errors.length === 0,
|
|
202
|
+
errors,
|
|
203
|
+
warnings
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Validates JSON string
|
|
208
|
+
*/
|
|
209
|
+
export function validateJSON(jsonString) {
|
|
210
|
+
const errors = [];
|
|
211
|
+
const warnings = [];
|
|
212
|
+
if (!jsonString || typeof jsonString !== 'string') {
|
|
213
|
+
errors.push('Input must be a non-empty string');
|
|
214
|
+
return { valid: false, errors, warnings };
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const parsed = JSON.parse(jsonString);
|
|
218
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
219
|
+
warnings.push('Parsed JSON is not an object (might be a primitive value)');
|
|
220
|
+
}
|
|
221
|
+
return { valid: true, errors, warnings };
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
225
|
+
errors.push(`Invalid JSON: ${errorMessage}`);
|
|
226
|
+
// Try to extract JSON from string
|
|
227
|
+
const jsonMatch = jsonString.match(/\{[\s\S]*\}/);
|
|
228
|
+
if (jsonMatch) {
|
|
229
|
+
warnings.push('JSON object found in string - consider extracting it');
|
|
230
|
+
try {
|
|
231
|
+
JSON.parse(jsonMatch[0]);
|
|
232
|
+
warnings.push('Extracted JSON is valid');
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
warnings.push('Extracted JSON is also invalid');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { valid: false, errors, warnings };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Extracts JSON from text (handles markdown code blocks, etc.)
|
|
243
|
+
*/
|
|
244
|
+
export function extractJSON(text) {
|
|
245
|
+
if (!text || typeof text !== 'string') {
|
|
246
|
+
return { json: null, method: 'none' };
|
|
247
|
+
}
|
|
248
|
+
// Try direct parse first
|
|
249
|
+
try {
|
|
250
|
+
JSON.parse(text);
|
|
251
|
+
return { json: text, method: 'direct' };
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Continue to extraction
|
|
255
|
+
}
|
|
256
|
+
// Try extracting from markdown code block
|
|
257
|
+
const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
258
|
+
if (codeBlockMatch) {
|
|
259
|
+
try {
|
|
260
|
+
const extracted = codeBlockMatch[1].trim();
|
|
261
|
+
JSON.parse(extracted);
|
|
262
|
+
return { json: extracted, method: 'markdown-code-block' };
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// Continue
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Try extracting JSON object
|
|
269
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
270
|
+
if (jsonMatch) {
|
|
271
|
+
try {
|
|
272
|
+
const extracted = jsonMatch[0];
|
|
273
|
+
JSON.parse(extracted);
|
|
274
|
+
return { json: extracted, method: 'regex-extraction' };
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// Continue
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return { json: null, method: 'none' };
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Validates response structure
|
|
284
|
+
*/
|
|
285
|
+
export function validateResponse(response) {
|
|
286
|
+
const errors = [];
|
|
287
|
+
const warnings = [];
|
|
288
|
+
if (!response.content) {
|
|
289
|
+
errors.push('Response content is missing');
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
// Check for "[object Object]" string
|
|
293
|
+
if (response.content === '[object Object]') {
|
|
294
|
+
errors.push('Response content is "[object Object]" string - content normalization failed');
|
|
295
|
+
}
|
|
296
|
+
// Validate JSON if it's supposed to be JSON
|
|
297
|
+
if (response.metadata?.contentType === 'object' || response.metadata?.contentType === 'array') {
|
|
298
|
+
const jsonResult = validateJSON(response.content);
|
|
299
|
+
if (!jsonResult.valid) {
|
|
300
|
+
errors.push(...jsonResult.errors);
|
|
301
|
+
warnings.push(...jsonResult.warnings);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Check parsedContent
|
|
306
|
+
if (response.metadata?.contentType === 'object' || response.metadata?.contentType === 'array') {
|
|
307
|
+
if (!response.parsedContent) {
|
|
308
|
+
warnings.push('parsedContent is undefined but contentType suggests it should be parsed');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Check validation results if schema validation was enabled
|
|
312
|
+
if (response.metadata?.outputValidationPassed !== undefined) {
|
|
313
|
+
if (!response.metadata.outputValidationPassed) {
|
|
314
|
+
const validationErrors = response.metadata.outputValidationErrors || [];
|
|
315
|
+
if (validationErrors.length > 0) {
|
|
316
|
+
warnings.push(`Schema validation failed: ${validationErrors.join(', ')}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
valid: errors.length === 0,
|
|
322
|
+
errors,
|
|
323
|
+
warnings
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Collects comprehensive diagnostic information
|
|
328
|
+
*/
|
|
329
|
+
export function diagnoseRequest(request) {
|
|
330
|
+
// AIRequest is distinguished by having primaryObjectType, objectTypes, or other AIRequest-specific fields
|
|
331
|
+
const isAIRequest = 'primaryObjectType' in request ||
|
|
332
|
+
('objectTypes' in request && Array.isArray(request.objectTypes)) ||
|
|
333
|
+
'skillId' in request ||
|
|
334
|
+
'masterSkillId' in request ||
|
|
335
|
+
'reasoningEncrypted' in request;
|
|
336
|
+
const diagnostic = {
|
|
337
|
+
request: {
|
|
338
|
+
hasJobId: !!request.jobId,
|
|
339
|
+
hasAgentId: !!request.agentId,
|
|
340
|
+
hasInstructions: !!request.instructions,
|
|
341
|
+
hasPrompt: !!request.prompt,
|
|
342
|
+
hasWorkingMemory: !!request.workingMemory,
|
|
343
|
+
hasConfig: !!request.config,
|
|
344
|
+
hasModel: !!request.config?.model,
|
|
345
|
+
hasProvider: !!request.config?.provider
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
return diagnostic;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Diagnoses response issues
|
|
352
|
+
*/
|
|
353
|
+
export function diagnoseResponse(response) {
|
|
354
|
+
if (!response) {
|
|
355
|
+
return {
|
|
356
|
+
hasContent: false,
|
|
357
|
+
isJSON: false,
|
|
358
|
+
hasParsedContent: false,
|
|
359
|
+
contentType: 'unknown'
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const hasContent = !!response.content;
|
|
363
|
+
let isJSON = false;
|
|
364
|
+
let contentType = 'unknown';
|
|
365
|
+
if (hasContent && typeof response.content === 'string') {
|
|
366
|
+
try {
|
|
367
|
+
JSON.parse(response.content);
|
|
368
|
+
isJSON = true;
|
|
369
|
+
contentType = 'object';
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
isJSON = false;
|
|
373
|
+
contentType = 'string';
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
hasContent,
|
|
378
|
+
isJSON,
|
|
379
|
+
hasParsedContent: !!response.parsedContent,
|
|
380
|
+
contentType: response.metadata?.contentType || contentType,
|
|
381
|
+
validationPassed: response.metadata?.outputValidationPassed,
|
|
382
|
+
validationErrors: response.metadata?.outputValidationErrors
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Tests if a provider/model combination supports JSON mode
|
|
387
|
+
*/
|
|
388
|
+
export function supportsJSONMode(provider, model) {
|
|
389
|
+
if (!provider || !model) {
|
|
390
|
+
return { supported: false, notes: 'Provider and model must be specified' };
|
|
391
|
+
}
|
|
392
|
+
const providerLower = provider.toLowerCase();
|
|
393
|
+
const modelLower = model.toLowerCase();
|
|
394
|
+
// OpenAI models that support JSON mode
|
|
395
|
+
if (providerLower === 'openai') {
|
|
396
|
+
const supportedModels = [
|
|
397
|
+
'gpt-4',
|
|
398
|
+
'gpt-4-turbo',
|
|
399
|
+
'gpt-4o',
|
|
400
|
+
'gpt-3.5-turbo'
|
|
401
|
+
];
|
|
402
|
+
const isSupported = supportedModels.some(supported => modelLower.includes(supported));
|
|
403
|
+
// Check for specific version requirements
|
|
404
|
+
if (modelLower.includes('gpt-3.5-turbo')) {
|
|
405
|
+
// Only 1106+ supports JSON mode
|
|
406
|
+
const versionMatch = model.match(/gpt-3\.5-turbo-(\d+)/);
|
|
407
|
+
if (versionMatch) {
|
|
408
|
+
const version = parseInt(versionMatch[1], 10);
|
|
409
|
+
if (version < 1106) {
|
|
410
|
+
return { supported: false, notes: 'gpt-3.5-turbo versions before 1106 do not support JSON mode' };
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
supported: isSupported,
|
|
416
|
+
notes: isSupported
|
|
417
|
+
? 'OpenAI supports response_format: { type: "json_object" }'
|
|
418
|
+
: 'Model may not support JSON mode - gateway will attempt to extract JSON from response'
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
// Anthropic doesn't support response_format
|
|
422
|
+
if (providerLower === 'anthropic') {
|
|
423
|
+
return {
|
|
424
|
+
supported: false,
|
|
425
|
+
notes: 'Anthropic does not support response_format - gateway will attempt to extract JSON from response'
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
supported: false,
|
|
430
|
+
notes: 'Unknown provider - check provider documentation for JSON mode support'
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Creates a test AIRequest with minimal valid structure
|
|
435
|
+
*/
|
|
436
|
+
export function createTestAIRequest(overrides) {
|
|
437
|
+
return {
|
|
438
|
+
jobId: 'test-job-' + Date.now(),
|
|
439
|
+
agentId: 'test-agent',
|
|
440
|
+
instructions: 'Test instructions',
|
|
441
|
+
input: 'Test input',
|
|
442
|
+
config: {
|
|
443
|
+
model: 'gpt-4o',
|
|
444
|
+
provider: 'openai'
|
|
445
|
+
},
|
|
446
|
+
primaryObjectType: {
|
|
447
|
+
type: 'test-result',
|
|
448
|
+
whenToUse: 'For testing purposes',
|
|
449
|
+
schema: {
|
|
450
|
+
type: 'object',
|
|
451
|
+
properties: {
|
|
452
|
+
result: { type: 'string' }
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
...overrides
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Creates test cases for validation
|
|
461
|
+
*/
|
|
462
|
+
export function createValidationTestCases() {
|
|
463
|
+
return [
|
|
464
|
+
{
|
|
465
|
+
name: 'Valid minimal request',
|
|
466
|
+
request: {
|
|
467
|
+
jobId: 'test',
|
|
468
|
+
agentId: 'agent-1',
|
|
469
|
+
instructions: 'Test',
|
|
470
|
+
prompt: 'test.prompt',
|
|
471
|
+
workingMemory: { input: 'Test' }
|
|
472
|
+
},
|
|
473
|
+
shouldFail: false,
|
|
474
|
+
expectedErrors: []
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: 'Valid request with config',
|
|
478
|
+
request: {
|
|
479
|
+
jobId: 'test',
|
|
480
|
+
agentId: 'agent-1',
|
|
481
|
+
instructions: 'Test',
|
|
482
|
+
prompt: 'test.prompt',
|
|
483
|
+
workingMemory: { input: 'Test' },
|
|
484
|
+
config: {
|
|
485
|
+
model: 'gpt-4o',
|
|
486
|
+
provider: 'openai'
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
shouldFail: false,
|
|
490
|
+
expectedErrors: []
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
name: 'Missing jobId',
|
|
494
|
+
request: {
|
|
495
|
+
agentId: 'agent-1',
|
|
496
|
+
instructions: 'Test',
|
|
497
|
+
prompt: 'test.prompt',
|
|
498
|
+
workingMemory: { input: 'Test' }
|
|
499
|
+
},
|
|
500
|
+
shouldFail: true,
|
|
501
|
+
expectedErrors: ['jobId is required']
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: 'Missing agentId',
|
|
505
|
+
request: {
|
|
506
|
+
jobId: 'test',
|
|
507
|
+
instructions: 'Test',
|
|
508
|
+
prompt: 'test.prompt',
|
|
509
|
+
workingMemory: { input: 'Test' }
|
|
510
|
+
},
|
|
511
|
+
shouldFail: true,
|
|
512
|
+
expectedErrors: ['agentId is required']
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: 'Missing instructions',
|
|
516
|
+
request: {
|
|
517
|
+
jobId: 'test',
|
|
518
|
+
agentId: 'agent-1',
|
|
519
|
+
prompt: 'test.prompt',
|
|
520
|
+
workingMemory: { input: 'Test' }
|
|
521
|
+
},
|
|
522
|
+
shouldFail: true,
|
|
523
|
+
expectedErrors: ['instructions is required']
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: 'Missing prompt',
|
|
527
|
+
request: {
|
|
528
|
+
jobId: 'test',
|
|
529
|
+
agentId: 'agent-1',
|
|
530
|
+
instructions: 'Test'
|
|
531
|
+
},
|
|
532
|
+
shouldFail: true,
|
|
533
|
+
expectedErrors: ['input is required']
|
|
534
|
+
}
|
|
535
|
+
];
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Runs all validation tests and returns results
|
|
539
|
+
*/
|
|
540
|
+
export function runValidationTests() {
|
|
541
|
+
const testCases = createValidationTestCases();
|
|
542
|
+
const results = testCases.map(testCase => {
|
|
543
|
+
const validation = validateAIRequest(testCase.request);
|
|
544
|
+
const passed = testCase.shouldFail
|
|
545
|
+
? !validation.valid
|
|
546
|
+
: validation.valid;
|
|
547
|
+
return {
|
|
548
|
+
name: testCase.name,
|
|
549
|
+
passed,
|
|
550
|
+
errors: validation.errors,
|
|
551
|
+
warnings: validation.warnings
|
|
552
|
+
};
|
|
553
|
+
});
|
|
554
|
+
return results;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Formats diagnostic information for display
|
|
558
|
+
*/
|
|
559
|
+
export function formatDiagnostic(diagnostic) {
|
|
560
|
+
const lines = [];
|
|
561
|
+
lines.push('=== Request Diagnostics ===');
|
|
562
|
+
lines.push(`Job ID: ${diagnostic.request.hasJobId ? '✓' : '✗'}`);
|
|
563
|
+
lines.push(`Agent ID: ${diagnostic.request.hasAgentId ? '✓' : '✗'}`);
|
|
564
|
+
lines.push(`Instructions: ${diagnostic.request.hasInstructions ? '✓' : '⚠ (optional)'}`);
|
|
565
|
+
lines.push(`Input (via workingMemory): ${diagnostic.request.hasWorkingMemory ? '✓' : '✗'}`);
|
|
566
|
+
lines.push(`Prompt: ${diagnostic.request.hasPrompt ? '✓' : '✗ (required)'}`);
|
|
567
|
+
lines.push(`Config: ${diagnostic.request.hasConfig ? '✓' : '✗'}`);
|
|
568
|
+
lines.push(`Model: ${diagnostic.request.hasModel ? '✓' : '✗'}`);
|
|
569
|
+
lines.push(`Provider: ${diagnostic.request.hasProvider ? '✓' : '⚠ (optional)'}`);
|
|
570
|
+
if (diagnostic.response) {
|
|
571
|
+
lines.push('\n=== Response Diagnostics ===');
|
|
572
|
+
lines.push(`Has Content: ${diagnostic.response.hasContent ? '✓' : '✗'}`);
|
|
573
|
+
lines.push(`Is JSON: ${diagnostic.response.isJSON ? '✓' : '✗'}`);
|
|
574
|
+
lines.push(`Has Parsed Content: ${diagnostic.response.hasParsedContent ? '✓' : '⚠'}`);
|
|
575
|
+
lines.push(`Content Type: ${diagnostic.response.contentType}`);
|
|
576
|
+
if (diagnostic.response.validationPassed !== undefined) {
|
|
577
|
+
lines.push(`Schema Validation: ${diagnostic.response.validationPassed ? '✓' : '✗'}`);
|
|
578
|
+
if (diagnostic.response.validationErrors && diagnostic.response.validationErrors.length > 0) {
|
|
579
|
+
lines.push(`Validation Errors: ${diagnostic.response.validationErrors.join(', ')}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return lines.join('\n');
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Quick validation helper - throws if invalid
|
|
587
|
+
*/
|
|
588
|
+
export function assertValidAIRequest(request) {
|
|
589
|
+
const validation = validateAIRequest(request);
|
|
590
|
+
if (!validation.valid) {
|
|
591
|
+
throw new Error(`AIRequest validation failed:\n${validation.errors.join('\n')}`);
|
|
592
|
+
}
|
|
593
|
+
if (validation.warnings.length > 0) {
|
|
594
|
+
console.warn('AIRequest validation warnings:', validation.warnings);
|
|
595
|
+
}
|
|
596
|
+
}
|