recursive-llm-ts 4.0.1 → 4.1.0

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/bin/rlm-go CHANGED
Binary file
package/dist/rlm.d.ts CHANGED
@@ -15,5 +15,6 @@ export declare class RLM {
15
15
  parallelExecution?: boolean;
16
16
  }): Promise<StructuredRLMResult<T>>;
17
17
  private zodToJsonSchema;
18
+ private escapeRegex;
18
19
  cleanup(): Promise<void>;
19
20
  }
package/dist/rlm.js CHANGED
@@ -52,62 +52,349 @@ class RLM {
52
52
  });
53
53
  }
54
54
  zodToJsonSchema(schema) {
55
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
55
56
  const def = schema._def;
56
- // Check for object type by presence of shape
57
- if (def.shape) {
58
- const shape = def.shape;
57
+ const defType = def.type;
58
+ // Handle wrapped types (optional, nullable, default, catch)
59
+ if (defType === 'optional' || defType === 'nullable' || defType === 'default' || defType === 'catch') {
60
+ const inner = this.zodToJsonSchema(def.innerType);
61
+ if (defType === 'nullable') {
62
+ return Object.assign(Object.assign({}, inner), { nullable: true });
63
+ }
64
+ return inner; // Optional/Default/Catch don't change the schema, just validation
65
+ }
66
+ // Handle effects (refine, transform, preprocess) - unwrap to inner type
67
+ if (defType === 'effects') {
68
+ return this.zodToJsonSchema(def.schema);
69
+ }
70
+ // Handle pipeline (pipe) - use the output schema
71
+ if (defType === 'pipeline') {
72
+ return this.zodToJsonSchema(def.out);
73
+ }
74
+ // Handle lazy schemas - unwrap the getter
75
+ if (defType === 'lazy') {
76
+ // For lazy schemas, we need to call the getter to get the actual schema
77
+ try {
78
+ const actualSchema = def.getter();
79
+ return this.zodToJsonSchema(actualSchema);
80
+ }
81
+ catch (e) {
82
+ // If lazy getter fails, fall back to generic object
83
+ return { type: 'object' };
84
+ }
85
+ }
86
+ // Handle branded types - unwrap to base type
87
+ if (defType === 'branded') {
88
+ return this.zodToJsonSchema(def.type);
89
+ }
90
+ // Handle readonly - pass through
91
+ if (defType === 'readonly') {
92
+ return this.zodToJsonSchema(def.innerType);
93
+ }
94
+ // Handle literals
95
+ if (defType === 'literal') {
96
+ // Literals in this Zod version use 'values' array
97
+ if (def.values && def.values.length > 0) {
98
+ const value = def.values[0];
99
+ const valueType = typeof value;
100
+ return {
101
+ type: valueType === 'object' ? 'string' : valueType,
102
+ enum: [value]
103
+ };
104
+ }
105
+ // Fallback for other literal formats
106
+ const value = def.value;
107
+ if (value !== undefined) {
108
+ const valueType = typeof value;
109
+ return {
110
+ type: valueType === 'object' ? 'string' : valueType,
111
+ enum: [value]
112
+ };
113
+ }
114
+ }
115
+ // Handle unions
116
+ if (defType === 'union' || defType === 'discriminatedUnion') {
117
+ const options = def.options || Array.from(((_a = def.optionsMap) === null || _a === void 0 ? void 0 : _a.values()) || []);
118
+ if (options.length > 0) {
119
+ return {
120
+ anyOf: options.map((opt) => this.zodToJsonSchema(opt))
121
+ };
122
+ }
123
+ }
124
+ // Handle intersections
125
+ if (defType === 'intersection') {
126
+ return {
127
+ allOf: [
128
+ this.zodToJsonSchema(def.left),
129
+ this.zodToJsonSchema(def.right)
130
+ ]
131
+ };
132
+ }
133
+ // Handle object type
134
+ if (def.shape || defType === 'object') {
135
+ const shape = def.shape || {};
59
136
  const properties = {};
60
137
  const required = [];
61
138
  for (const [key, value] of Object.entries(shape)) {
62
139
  properties[key] = this.zodToJsonSchema(value);
63
- if (!value.isOptional()) {
140
+ // A field is required if it's not optional and doesn't have a default
141
+ const valueDef = value._def;
142
+ const isOptional = (_d = (_c = (_b = value).isOptional) === null || _c === void 0 ? void 0 : _c.call(_b)) !== null && _d !== void 0 ? _d : false;
143
+ const hasDefault = (valueDef === null || valueDef === void 0 ? void 0 : valueDef.type) === 'default';
144
+ if (!isOptional && !hasDefault) {
64
145
  required.push(key);
65
146
  }
66
147
  }
67
- return {
148
+ const result = {
68
149
  type: 'object',
69
- properties,
70
- required: required.length > 0 ? required : undefined
150
+ properties
71
151
  };
152
+ if (required.length > 0) {
153
+ result.required = required;
154
+ }
155
+ // Handle unknown keys via catchall
156
+ if (def.catchall) {
157
+ const catchallType = (_e = def.catchall._def) === null || _e === void 0 ? void 0 : _e.type;
158
+ if (catchallType === 'unknown') {
159
+ result.additionalProperties = true;
160
+ }
161
+ else if (catchallType === 'never') {
162
+ result.additionalProperties = false;
163
+ }
164
+ }
165
+ // Also check legacy unknownKeys
166
+ if (def.unknownKeys === 'passthrough') {
167
+ result.additionalProperties = true;
168
+ }
169
+ else if (def.unknownKeys === 'strict') {
170
+ result.additionalProperties = false;
171
+ }
172
+ return result;
72
173
  }
73
- // Check for array type - Zod arrays have an 'element' property (or 'type' in older versions)
74
- if (def.type === 'array' && (def.element || def.type)) {
75
- const itemSchema = def.element || def.type;
76
- return {
174
+ // Handle array type
175
+ if (defType === 'array') {
176
+ const itemSchema = def.element;
177
+ const result = {
77
178
  type: 'array',
78
179
  items: this.zodToJsonSchema(itemSchema)
79
180
  };
181
+ // Handle array length constraints from checks
182
+ if (def.checks && Array.isArray(def.checks)) {
183
+ for (const check of def.checks) {
184
+ const checkDef = ((_f = check._zod) === null || _f === void 0 ? void 0 : _f.def) || check.def || check;
185
+ switch (checkDef.check) {
186
+ case 'min_length':
187
+ result.minItems = checkDef.minimum || checkDef.value;
188
+ break;
189
+ case 'max_length':
190
+ result.maxItems = checkDef.maximum || checkDef.value;
191
+ break;
192
+ case 'exact_length':
193
+ result.minItems = checkDef.value;
194
+ result.maxItems = checkDef.value;
195
+ break;
196
+ }
197
+ }
198
+ }
199
+ // Also check direct properties (legacy)
200
+ if (def.minLength)
201
+ result.minItems = def.minLength.value || def.minLength;
202
+ if (def.maxLength)
203
+ result.maxItems = def.maxLength.value || def.maxLength;
204
+ if (def.exactLength) {
205
+ const exact = def.exactLength.value || def.exactLength;
206
+ result.minItems = exact;
207
+ result.maxItems = exact;
208
+ }
209
+ return result;
80
210
  }
81
- // Check for enum - Zod enums have a 'type' of 'enum' and 'entries' object
82
- if (def.type === 'enum' && def.entries) {
211
+ // Handle tuple
212
+ if (defType === 'tuple') {
213
+ const items = ((_g = def.items) === null || _g === void 0 ? void 0 : _g.map((item) => this.zodToJsonSchema(item))) || [];
214
+ const result = {
215
+ type: 'array',
216
+ prefixItems: items,
217
+ minItems: items.length,
218
+ maxItems: def.rest ? undefined : items.length
219
+ };
220
+ // Handle rest element
221
+ if (def.rest) {
222
+ result.items = this.zodToJsonSchema(def.rest);
223
+ }
224
+ else {
225
+ result.items = false; // No additional items allowed
226
+ }
227
+ return result;
228
+ }
229
+ // Handle set - convert to array
230
+ if (defType === 'set') {
83
231
  return {
84
- type: 'string',
85
- enum: Object.keys(def.entries)
232
+ type: 'array',
233
+ uniqueItems: true,
234
+ items: def.valueType ? this.zodToJsonSchema(def.valueType) : {}
86
235
  };
87
236
  }
88
- // Check for legacy enum with values array
89
- if (def.values && Array.isArray(def.values)) {
237
+ // Handle map - convert to object
238
+ if (defType === 'map') {
90
239
  return {
91
- type: 'string',
92
- enum: def.values
240
+ type: 'object',
241
+ additionalProperties: def.valueType ? this.zodToJsonSchema(def.valueType) : true
93
242
  };
94
243
  }
95
- // Check for optional/nullable
96
- if (def.innerType) {
97
- const inner = this.zodToJsonSchema(def.innerType);
98
- return def.typeName === 'ZodNullable' ? Object.assign(Object.assign({}, inner), { nullable: true }) : inner;
244
+ // Handle record
245
+ if (defType === 'record') {
246
+ return {
247
+ type: 'object',
248
+ additionalProperties: def.valueType ? this.zodToJsonSchema(def.valueType) : true
249
+ };
99
250
  }
100
- // Detect primitive types
101
- const defType = def.type;
102
- if (defType === 'string')
103
- return { type: 'string' };
104
- if (defType === 'number')
105
- return { type: 'number' };
106
- if (defType === 'boolean')
251
+ // Handle enum
252
+ if (defType === 'enum' || defType === 'nativeEnum') {
253
+ if (def.values && Array.isArray(def.values)) {
254
+ return { type: 'string', enum: def.values };
255
+ }
256
+ if (def.entries) {
257
+ return { type: 'string', enum: Object.keys(def.entries) };
258
+ }
259
+ }
260
+ // Handle string with constraints
261
+ if (defType === 'string') {
262
+ const result = { type: 'string' };
263
+ if (def.checks && Array.isArray(def.checks)) {
264
+ for (const check of def.checks) {
265
+ // Access the actual check data via _zod.def
266
+ const checkDef = ((_h = check._zod) === null || _h === void 0 ? void 0 : _h.def) || check.def || check;
267
+ switch (checkDef.check) {
268
+ case 'min_length':
269
+ result.minLength = checkDef.minimum || checkDef.value;
270
+ break;
271
+ case 'max_length':
272
+ result.maxLength = checkDef.maximum || checkDef.value;
273
+ break;
274
+ case 'length_equals':
275
+ result.minLength = checkDef.length;
276
+ result.maxLength = checkDef.length;
277
+ break;
278
+ case 'string_format':
279
+ switch (checkDef.format) {
280
+ case 'email':
281
+ result.format = 'email';
282
+ break;
283
+ case 'url':
284
+ result.format = 'uri';
285
+ break;
286
+ case 'uuid':
287
+ result.format = 'uuid';
288
+ break;
289
+ case 'regex':
290
+ if (checkDef.pattern) {
291
+ result.pattern = checkDef.pattern.source || checkDef.pattern;
292
+ }
293
+ break;
294
+ }
295
+ break;
296
+ case 'regex':
297
+ result.pattern = ((_j = checkDef.pattern) === null || _j === void 0 ? void 0 : _j.source) || checkDef.pattern;
298
+ break;
299
+ }
300
+ // Also check nested def for formats
301
+ if (check.def && check.def.format) {
302
+ switch (check.def.format) {
303
+ case 'email':
304
+ result.format = 'email';
305
+ break;
306
+ case 'url':
307
+ result.format = 'uri';
308
+ break;
309
+ case 'uuid':
310
+ result.format = 'uuid';
311
+ break;
312
+ }
313
+ }
314
+ }
315
+ }
316
+ return result;
317
+ }
318
+ // Handle number/bigint with constraints
319
+ if (defType === 'number' || defType === 'bigint') {
320
+ const result = { type: defType === 'bigint' ? 'integer' : 'number' };
321
+ if (def.checks && Array.isArray(def.checks)) {
322
+ for (const check of def.checks) {
323
+ // Access the actual check data via _zod.def
324
+ const checkDef = ((_k = check._zod) === null || _k === void 0 ? void 0 : _k.def) || check.def || check;
325
+ switch (checkDef.check) {
326
+ case 'number_format':
327
+ if (checkDef.format === 'safeint') {
328
+ result.type = 'integer';
329
+ }
330
+ break;
331
+ case 'greater_than':
332
+ result.minimum = checkDef.value;
333
+ if (!checkDef.inclusive) {
334
+ result.exclusiveMinimum = true;
335
+ }
336
+ break;
337
+ case 'less_than':
338
+ result.maximum = checkDef.value;
339
+ if (!checkDef.inclusive) {
340
+ result.exclusiveMaximum = true;
341
+ }
342
+ break;
343
+ case 'multiple_of':
344
+ result.multipleOf = checkDef.value;
345
+ break;
346
+ }
347
+ // Also check direct properties (legacy support)
348
+ if (check.isInt === true) {
349
+ result.type = 'integer';
350
+ }
351
+ }
352
+ }
353
+ return result;
354
+ }
355
+ // Handle boolean
356
+ if (defType === 'boolean') {
107
357
  return { type: 'boolean' };
358
+ }
359
+ // Handle date
360
+ if (defType === 'date') {
361
+ return { type: 'string', format: 'date-time' };
362
+ }
363
+ // Handle null
364
+ if (defType === 'null') {
365
+ return { type: 'null' };
366
+ }
367
+ // Handle undefined (not really JSON-serializable, but treat as null)
368
+ if (defType === 'undefined') {
369
+ return { type: 'null' };
370
+ }
371
+ // Handle void (same as undefined)
372
+ if (defType === 'void') {
373
+ return { type: 'null' };
374
+ }
375
+ // Handle any/unknown - no constraints
376
+ if (defType === 'any' || defType === 'unknown') {
377
+ return {}; // Empty schema accepts anything
378
+ }
379
+ // Handle never - impossible to satisfy
380
+ if (defType === 'never') {
381
+ return { not: {} }; // Schema that matches nothing
382
+ }
383
+ // Handle promise - unwrap to inner type
384
+ if (defType === 'promise') {
385
+ return this.zodToJsonSchema(def.innerType || def.type);
386
+ }
387
+ // Handle function - not JSON-serializable
388
+ if (defType === 'function') {
389
+ return { type: 'string', description: 'Function (not serializable)' };
390
+ }
108
391
  // Default fallback
392
+ console.warn(`Unknown Zod type: ${defType}, falling back to string`);
109
393
  return { type: 'string' };
110
394
  }
395
+ escapeRegex(str) {
396
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
397
+ }
111
398
  cleanup() {
112
399
  return __awaiter(this, void 0, void 0, function* () {
113
400
  if (this.bridge) {
@@ -235,27 +235,75 @@ func generateSchemaConstraints(schema *JSONSchema) string {
235
235
  return ""
236
236
  }
237
237
 
238
- // generateFieldQuery creates a focused query for a specific field
238
+ // generateFieldQuery creates a focused query for a specific field based on its schema
239
239
  func generateFieldQuery(fieldName string, schema *JSONSchema) string {
240
- fieldQueries := map[string]string{
241
- "sentiment": "Analyze the overall sentiment of this conversation. Return a JSON object with: score (integer 1-5), confidence (number 0-1), and optional reasoning (string).",
242
- "sentimentValue": "What is the overall sentiment score (1-5) of this conversation?",
243
- "sentimentExplanation": "Explain in 2-3 sentences why the conversation has this sentiment score.",
244
- "phrases": "Extract key phrases that significantly impacted the sentiment, excluding neutral (3-value) phrases. For each phrase, include the sentiment value and the phrase itself (1 sentence).",
245
- "keyMoments": "Identify key moments in the conversation such as churn mentions, personnel changes, competitive mentions, etc. For each moment, provide the phrase and categorize the type.",
246
- }
247
-
248
- if query, exists := fieldQueries[fieldName]; exists {
249
- return query
250
- }
251
-
252
- // For object types, provide more detailed instructions about required fields
253
- if schema.Type == "object" && len(schema.Required) > 0 {
254
- return fmt.Sprintf("Extract the %s from the conversation. Return a JSON object with these required fields: %s.",
255
- fieldName, strings.Join(schema.Required, ", "))
240
+ var queryParts []string
241
+
242
+ // Start with field name
243
+ queryParts = append(queryParts, fmt.Sprintf("Extract the %s from the conversation.", fieldName))
244
+
245
+ // Add type-specific instructions
246
+ switch schema.Type {
247
+ case "object":
248
+ if len(schema.Required) > 0 {
249
+ fieldDetails := make([]string, 0, len(schema.Required))
250
+ for _, reqField := range schema.Required {
251
+ if propSchema, exists := schema.Properties[reqField]; exists {
252
+ fieldDetails = append(fieldDetails, fmt.Sprintf("'%s' (%s)", reqField, propSchema.Type))
253
+ }
254
+ }
255
+ queryParts = append(queryParts, fmt.Sprintf("Return a JSON object with these REQUIRED fields: %s.", strings.Join(fieldDetails, ", ")))
256
+ } else {
257
+ queryParts = append(queryParts, "Return a JSON object.")
258
+ }
259
+
260
+ case "array":
261
+ if schema.Items != nil {
262
+ if schema.Items.Type == "object" && schema.Items.Properties != nil {
263
+ // Build detailed description of array item structure
264
+ requiredFields := make([]string, 0)
265
+ optionalFields := make([]string, 0)
266
+
267
+ for propName, propSchema := range schema.Items.Properties {
268
+ fieldDesc := fmt.Sprintf("'%s' (%s)", propName, propSchema.Type)
269
+ if contains(schema.Items.Required, propName) {
270
+ requiredFields = append(requiredFields, fieldDesc)
271
+ } else {
272
+ optionalFields = append(optionalFields, fieldDesc)
273
+ }
274
+ }
275
+
276
+ var itemDesc []string
277
+ if len(requiredFields) > 0 {
278
+ itemDesc = append(itemDesc, fmt.Sprintf("REQUIRED fields: %s", strings.Join(requiredFields, ", ")))
279
+ }
280
+ if len(optionalFields) > 0 {
281
+ itemDesc = append(itemDesc, fmt.Sprintf("Optional fields: %s", strings.Join(optionalFields, ", ")))
282
+ }
283
+
284
+ queryParts = append(queryParts, fmt.Sprintf("Return a JSON array where each item is an object with %s.", strings.Join(itemDesc, ". ")))
285
+ } else {
286
+ queryParts = append(queryParts, fmt.Sprintf("Return a JSON array of %s values.", schema.Items.Type))
287
+ }
288
+ } else {
289
+ queryParts = append(queryParts, "Return a JSON array.")
290
+ }
291
+
292
+ case "string":
293
+ if schema.Enum != nil && len(schema.Enum) > 0 {
294
+ queryParts = append(queryParts, fmt.Sprintf("Return EXACTLY one of these values: %s (use exact strings).", strings.Join(schema.Enum, ", ")))
295
+ } else {
296
+ queryParts = append(queryParts, "Return a string value.")
297
+ }
298
+
299
+ case "number":
300
+ queryParts = append(queryParts, "Return a numeric value.")
301
+
302
+ case "boolean":
303
+ queryParts = append(queryParts, "Return a boolean value (true or false).")
256
304
  }
257
-
258
- return fmt.Sprintf("Extract the %s from the conversation.", fieldName)
305
+
306
+ return strings.Join(queryParts, " ")
259
307
  }
260
308
 
261
309
  // parseAndValidateJSON extracts JSON from response and validates against schema
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recursive-llm-ts",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "TypeScript bridge for recursive-llm: Recursive Language Models for unbounded context processing with structured outputs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",