@resolveio/server-lib 20.14.3 → 20.14.5

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.
@@ -95,6 +95,7 @@ var common_1 = require("../util/common");
95
95
  var report_builder_unwinds_1 = require("../util/report-builder-unwinds");
96
96
  var schema_report_builder_1 = require("../util/schema-report-builder");
97
97
  var tokenizer_1 = require("../util/tokenizer");
98
+ var ai_terminal_1 = require("./ai-terminal");
98
99
  var DEFAULT_REPORT_BUILDER_CODEX_MODEL = 'gpt-5.2-codex';
99
100
  var DEFAULT_REPORT_BUILDER_CODEX_TIMEOUT_MS = 60000;
100
101
  var reportBuilderCodexClient = null;
@@ -1663,7 +1664,7 @@ function expandLayoutColumnFilters(filters, selectedFields) {
1663
1664
  }
1664
1665
  function executeReportBuilderAi(payload, context) {
1665
1666
  return __awaiter(this, void 0, void 0, function () {
1666
- var input, prompt, isSuperAdmin, guardrailsEnabled, guardrail, reportType, collectionRoot, collectionJoins, collections, resolvedFields, fieldLimit, trimmedFields, arraySummary, systemPrompt, userPrompt, codexModel, codexClient, codexPrompt, runOptions, responseText, usage, parsed, sanitizeContext, patch, summaryNotes, idClient;
1667
+ var input, prompt, isSuperAdmin, guardrailsEnabled, guardrail, reportType, collectionRoot, collectionJoins, collections, fieldLimit, resolvedFields, usedFallbackFields, derivedFields, trimmedFields, arraySummary, systemPrompt, userPrompt, codexModel, codexClient, runOptions, responseText, usage, parsed, allowMongoRead, mongoPayload, mongoReadResult, error_1, message, mongoContext, usage2, sanitizeContext, patch, summaryNotes, idClient;
1667
1668
  return __generator(this, function (_a) {
1668
1669
  switch (_a.label) {
1669
1670
  case 0:
@@ -1686,9 +1687,15 @@ function executeReportBuilderAi(payload, context) {
1686
1687
  collectionRoot = normalizeOptionalString(input.collection_root);
1687
1688
  collectionJoins = Array.isArray(input.collection_joins) ? input.collection_joins : [];
1688
1689
  collections = sanitizeCollections(input.available_collections || []);
1689
- resolvedFields = sanitizeFields(input.available_fields || []);
1690
1690
  fieldLimit = normalizeFieldLimit(input.field_limit);
1691
- trimmedFields = trimFieldsForPrompt(resolvedFields, fieldLimit);
1691
+ resolvedFields = sanitizeFields(input.available_fields || []);
1692
+ usedFallbackFields = false;
1693
+ if (!resolvedFields.length && collections.length) {
1694
+ derivedFields = deriveFieldsFromCollections(collections);
1695
+ resolvedFields = sanitizeFields(derivedFields);
1696
+ usedFallbackFields = resolvedFields.length > 0;
1697
+ }
1698
+ trimmedFields = trimFieldsForPrompt(resolvedFields, fieldLimit, usedFallbackFields);
1692
1699
  arraySummary = buildArrayFieldSummary(trimmedFields.fields);
1693
1700
  systemPrompt = buildReportBuilderSystemPrompt(reportType);
1694
1701
  userPrompt = buildReportBuilderUserPrompt({
@@ -1710,12 +1717,11 @@ function executeReportBuilderAi(payload, context) {
1710
1717
  });
1711
1718
  codexModel = resolveReportBuilderCodexModel(input.config);
1712
1719
  codexClient = getReportBuilderCodexClient();
1713
- codexPrompt = buildReportBuilderCodexPrompt(systemPrompt, userPrompt);
1714
1720
  runOptions = {
1715
1721
  timeoutMs: resolveReportBuilderCodexTimeoutMs(input.config),
1716
1722
  threadOptions: resolveReportBuilderCodexThreadOptions(input.config, codexModel)
1717
1723
  };
1718
- return [4 /*yield*/, codexClient.run(codexPrompt, runOptions)];
1724
+ return [4 /*yield*/, codexClient.run(buildReportBuilderCodexPrompt(systemPrompt, userPrompt), runOptions)];
1719
1725
  case 2:
1720
1726
  responseText = _a.sent();
1721
1727
  usage = estimateUsage([
@@ -1723,6 +1729,58 @@ function executeReportBuilderAi(payload, context) {
1723
1729
  { role: 'user', content: userPrompt }
1724
1730
  ], responseText, codexModel);
1725
1731
  parsed = safeJsonParse(responseText);
1732
+ allowMongoRead = input.allow_mongo_read === true;
1733
+ if (!(allowMongoRead && parsed && typeof parsed === 'object' && parsed.mongo_read)) return [3 /*break*/, 8];
1734
+ mongoPayload = resolveMongoReadPayload(parsed.mongo_read, input, collections);
1735
+ if (!mongoPayload) return [3 /*break*/, 8];
1736
+ mongoReadResult = null;
1737
+ _a.label = 3;
1738
+ case 3:
1739
+ _a.trys.push([3, 5, , 6]);
1740
+ return [4 /*yield*/, (0, ai_terminal_1.executeAiAssistantMongoRead)(mongoPayload, context)];
1741
+ case 4:
1742
+ mongoReadResult = _a.sent();
1743
+ return [3 /*break*/, 6];
1744
+ case 5:
1745
+ error_1 = _a.sent();
1746
+ message = error_1 instanceof Error ? error_1.message : String(error_1 || 'Mongo read failed.');
1747
+ mongoReadResult = { error: message };
1748
+ return [3 /*break*/, 6];
1749
+ case 6:
1750
+ mongoContext = buildMongoReadContext(mongoPayload, mongoReadResult);
1751
+ userPrompt = buildReportBuilderUserPrompt({
1752
+ prompt: prompt,
1753
+ reportType: reportType,
1754
+ reportName: normalizeOptionalString(input.report_name),
1755
+ collectionRoot: collectionRoot,
1756
+ collectionJoins: collectionJoins,
1757
+ idDateField: normalizeOptionalString(input.id_date_field),
1758
+ dateInterval: normalizeOptionalString(input.date_interval),
1759
+ collections: collections,
1760
+ fields: trimmedFields.fields,
1761
+ fieldMeta: {
1762
+ truncated: trimmedFields.truncated,
1763
+ total: trimmedFields.total,
1764
+ used: trimmedFields.fields.length
1765
+ },
1766
+ arraySummary: arraySummary,
1767
+ mongoReadResult: mongoContext
1768
+ });
1769
+ return [4 /*yield*/, codexClient.run(buildReportBuilderCodexPrompt(systemPrompt, userPrompt), runOptions)];
1770
+ case 7:
1771
+ responseText = _a.sent();
1772
+ usage2 = estimateUsage([
1773
+ { role: 'system', content: systemPrompt },
1774
+ { role: 'user', content: userPrompt }
1775
+ ], responseText, codexModel);
1776
+ usage = {
1777
+ inputTokens: usage.inputTokens + usage2.inputTokens,
1778
+ outputTokens: usage.outputTokens + usage2.outputTokens,
1779
+ totalTokens: usage.totalTokens + usage2.totalTokens
1780
+ };
1781
+ parsed = safeJsonParse(responseText);
1782
+ _a.label = 8;
1783
+ case 8:
1726
1784
  if (!parsed || typeof parsed !== 'object') {
1727
1785
  throw new Error('AI response was not valid JSON.');
1728
1786
  }
@@ -1735,9 +1793,9 @@ function executeReportBuilderAi(payload, context) {
1735
1793
  usedFields: trimmedFields.fields.length
1736
1794
  });
1737
1795
  return [4 /*yield*/, resolveClientId(input.id_client, context === null || context === void 0 ? void 0 : context.id_user)];
1738
- case 3:
1796
+ case 9:
1739
1797
  idClient = _a.sent();
1740
- if (!idClient) return [3 /*break*/, 5];
1798
+ if (!idClient) return [3 /*break*/, 11];
1741
1799
  return [4 /*yield*/, (0, openai_usage_ledger_manager_1.recordOpenAIUsage)({
1742
1800
  id_client: idClient,
1743
1801
  model: codexModel || 'unknown',
@@ -1747,10 +1805,10 @@ function executeReportBuilderAi(payload, context) {
1747
1805
  category: 'report-builder-ai',
1748
1806
  id_request: ''
1749
1807
  })];
1750
- case 4:
1808
+ case 10:
1751
1809
  _a.sent();
1752
- _a.label = 5;
1753
- case 5: return [2 /*return*/, {
1810
+ _a.label = 11;
1811
+ case 11: return [2 /*return*/, {
1754
1812
  patch: patch,
1755
1813
  notes: summaryNotes,
1756
1814
  usage: {
@@ -1783,7 +1841,13 @@ function buildReportBuilderSystemPrompt(reportType) {
1783
1841
  '- notes (string)',
1784
1842
  'Use only provided collections and field_path values (they reflect the current models/collections).',
1785
1843
  'Always set collection_root to the best matching collection from available_collections.',
1844
+ 'When available_fields spans multiple collections, choose collection_root first and then select fields whose collection_name matches collection_root or a join alias.',
1786
1845
  'Prefer exact field_path values from available_fields; never invent field paths.',
1846
+ 'Translate the request into concrete layout_columns, groups_row, sort, totals, filters, and links whenever possible.',
1847
+ 'If the request specifies a time grain (daily/weekly/monthly/etc), set date_interval and id_date_field to the best matching date field.',
1848
+ 'If you need sample records to confirm field choices, you may return JSON with {"mongo_read": { "collection": "<collection>", "query": {}, "options": {"limit": 5} }, "notes": "why you need data"} .',
1849
+ 'Only request mongo_read when necessary and only use collections from available_collections.',
1850
+ 'When mongo_read_result is present in the user context, use it to finalize the patch and do not request another read.',
1787
1851
  'Use field_type to guide filters/totals (numeric fields for totals, date fields for date_interval, boolean fields for boolean_value).',
1788
1852
  'If field_summary.truncated is true, only use provided fields and note missing needs in notes.',
1789
1853
  'If you need joined data, add collection_joins with an alias and use the alias in field_path.',
@@ -1811,6 +1875,7 @@ function buildReportBuilderUserPrompt(input) {
1811
1875
  available_fields: input.fields || [],
1812
1876
  field_summary: input.fieldMeta || undefined,
1813
1877
  array_summary: input.arraySummary || [],
1878
+ mongo_read_result: input.mongoReadResult || undefined,
1814
1879
  request: input.prompt
1815
1880
  };
1816
1881
  return JSON.stringify(context);
@@ -1946,13 +2011,224 @@ function sanitizeFields(raw) {
1946
2011
  });
1947
2012
  return out;
1948
2013
  }
1949
- function trimFieldsForPrompt(fields, limit) {
2014
+ function resolveCollectionNameFromList(value, collections) {
2015
+ var e_1, _a;
2016
+ var key = normalizeKey(value);
2017
+ if (!key) {
2018
+ return '';
2019
+ }
2020
+ try {
2021
+ for (var _b = __values(collections || []), _c = _b.next(); !_c.done; _c = _b.next()) {
2022
+ var col = _c.value;
2023
+ if (normalizeKey(col.collection) === key || normalizeKey(col.name) === key) {
2024
+ return col.collection;
2025
+ }
2026
+ }
2027
+ }
2028
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2029
+ finally {
2030
+ try {
2031
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
2032
+ }
2033
+ finally { if (e_1) throw e_1.error; }
2034
+ }
2035
+ return '';
2036
+ }
2037
+ function resolveMongoReadPayload(raw, input, collections) {
2038
+ if (!raw || typeof raw !== 'object') {
2039
+ return null;
2040
+ }
2041
+ var collection = resolveCollectionNameFromList(raw.collection || raw.collection_name || raw.collectionName, collections);
2042
+ if (!collection) {
2043
+ return null;
2044
+ }
2045
+ var query = raw.query && typeof raw.query === 'object' && !Array.isArray(raw.query) ? raw.query : {};
2046
+ var optionsRaw = raw.options && typeof raw.options === 'object' ? raw.options : {};
2047
+ var limitRaw = normalizeOptionalNumber(optionsRaw.limit);
2048
+ var skipRaw = normalizeOptionalNumber(optionsRaw.skip);
2049
+ var permissionView = normalizeOptionalString(input.permission_view || input.permissionView) || '/report-builder';
2050
+ var request = {
2051
+ collection: collection,
2052
+ database: normalizeOptionalString(raw.database) || undefined,
2053
+ query: query,
2054
+ options: {
2055
+ projection: optionsRaw.projection && typeof optionsRaw.projection === 'object' ? optionsRaw.projection : undefined,
2056
+ sort: optionsRaw.sort && typeof optionsRaw.sort === 'object' ? optionsRaw.sort : undefined,
2057
+ limit: limitRaw ? Math.min(Math.max((0, common_1.round)(limitRaw), 1), 10) : 5,
2058
+ skip: skipRaw ? Math.max((0, common_1.round)(skipRaw), 0) : undefined,
2059
+ includeTotal: optionsRaw.includeTotal === true
2060
+ },
2061
+ permissionView: permissionView || undefined,
2062
+ id_client: normalizeOptionalString(input.id_client) || undefined,
2063
+ mongo: input.mongo
2064
+ };
2065
+ return request;
2066
+ }
2067
+ function buildMongoReadContext(request, response) {
2068
+ var documents = Array.isArray(response === null || response === void 0 ? void 0 : response.documents) ? response.documents : [];
2069
+ var total = typeof (response === null || response === void 0 ? void 0 : response.total) === 'number' ? response.total : null;
2070
+ var sample = summarizeMongoDocuments(documents);
2071
+ return {
2072
+ request: {
2073
+ collection: request.collection,
2074
+ query: request.query || {},
2075
+ options: request.options || {}
2076
+ },
2077
+ result: {
2078
+ total: total,
2079
+ sample: sample,
2080
+ fields: extractMongoSampleFields(sample)
2081
+ }
2082
+ };
2083
+ }
2084
+ function summarizeMongoDocuments(documents) {
2085
+ var maxDocs = 5;
2086
+ return (documents || []).slice(0, maxDocs).map(function (doc) { return pruneMongoValue(doc, 0); });
2087
+ }
2088
+ function pruneMongoValue(value, depth) {
2089
+ var maxDepth = 3;
2090
+ var maxKeys = 30;
2091
+ var maxArray = 8;
2092
+ var maxString = 200;
2093
+ if (depth >= maxDepth) {
2094
+ return '[Truncated]';
2095
+ }
2096
+ if (Array.isArray(value)) {
2097
+ return value.slice(0, maxArray).map(function (item) { return pruneMongoValue(item, depth + 1); });
2098
+ }
2099
+ if (value && typeof value === 'object') {
2100
+ var keys = Object.keys(value);
2101
+ var trimmedKeys = keys.slice(0, maxKeys);
2102
+ var result_1 = {};
2103
+ trimmedKeys.forEach(function (key) {
2104
+ result_1[key] = pruneMongoValue(value[key], depth + 1);
2105
+ });
2106
+ if (keys.length > trimmedKeys.length) {
2107
+ result_1.__truncated__ = true;
2108
+ }
2109
+ return result_1;
2110
+ }
2111
+ if (typeof value === 'string' && value.length > maxString) {
2112
+ return value.slice(0, maxString) + '...';
2113
+ }
2114
+ return value;
2115
+ }
2116
+ function extractMongoSampleFields(samples) {
2117
+ var fields = new Set();
2118
+ (samples || []).forEach(function (sample) {
2119
+ if (!sample || typeof sample !== 'object') {
2120
+ return;
2121
+ }
2122
+ Object.keys(sample).forEach(function (key) { return fields.add(key); });
2123
+ });
2124
+ return Array.from(fields);
2125
+ }
2126
+ function collectTreeLeaves(items, res) {
2127
+ (items || []).forEach(function (item) {
2128
+ var _a;
2129
+ if (item === null || item === void 0 ? void 0 : item.isLeaf) {
2130
+ res.push(item);
2131
+ return;
2132
+ }
2133
+ if ((_a = item === null || item === void 0 ? void 0 : item.children) === null || _a === void 0 ? void 0 : _a.length) {
2134
+ collectTreeLeaves(item.children, res);
2135
+ }
2136
+ });
2137
+ }
2138
+ function deriveFieldsFromCollections(collections) {
2139
+ var fields = [];
2140
+ var seen = new Set();
2141
+ (collections || []).forEach(function (col) {
2142
+ var collectionName = normalizeOptionalString(col === null || col === void 0 ? void 0 : col.collection);
2143
+ if (!collectionName) {
2144
+ return;
2145
+ }
2146
+ var tree;
2147
+ try {
2148
+ tree = buildCollectionTree(collectionName);
2149
+ }
2150
+ catch (error) {
2151
+ var message = error instanceof Error ? error.message : String(error || 'Unknown error');
2152
+ console.warn('RB AI field derivation failed', { collection: collectionName, error: message });
2153
+ return;
2154
+ }
2155
+ var leaves = [];
2156
+ collectTreeLeaves((tree === null || tree === void 0 ? void 0 : tree.children) || [], leaves);
2157
+ leaves.forEach(function (leaf) {
2158
+ var fieldPath = normalizeOptionalString(leaf === null || leaf === void 0 ? void 0 : leaf.fieldPath);
2159
+ if (!fieldPath) {
2160
+ return;
2161
+ }
2162
+ var key = fieldPath.toLowerCase();
2163
+ if (seen.has(key)) {
2164
+ return;
2165
+ }
2166
+ seen.add(key);
2167
+ fields.push({
2168
+ field_path: fieldPath,
2169
+ field_path_name: normalizeOptionalString(leaf === null || leaf === void 0 ? void 0 : leaf.fieldPathName) || fieldPath,
2170
+ field_type: normalizeOptionalString(leaf === null || leaf === void 0 ? void 0 : leaf.fieldType) || 'String',
2171
+ collection_name: normalizeOptionalString((leaf === null || leaf === void 0 ? void 0 : leaf.collection_name) || (leaf === null || leaf === void 0 ? void 0 : leaf.collection)) || collectionName
2172
+ });
2173
+ });
2174
+ });
2175
+ return fields;
2176
+ }
2177
+ function trimFieldsForPrompt(fields, limit, balanceByCollection) {
2178
+ var e_2, _a;
2179
+ if (balanceByCollection === void 0) { balanceByCollection = false; }
1950
2180
  if (!fields || !fields.length) {
1951
2181
  return { fields: [], truncated: false, total: 0 };
1952
2182
  }
1953
2183
  if (fields.length <= limit) {
1954
2184
  return { fields: fields, truncated: false, total: fields.length };
1955
2185
  }
2186
+ if (balanceByCollection) {
2187
+ var buckets_1 = new Map();
2188
+ var order_1 = [];
2189
+ fields.forEach(function (field) {
2190
+ var _a;
2191
+ var key = normalizeOptionalString(field === null || field === void 0 ? void 0 : field.collection_name) || 'unknown';
2192
+ if (!buckets_1.has(key)) {
2193
+ buckets_1.set(key, []);
2194
+ order_1.push(key);
2195
+ }
2196
+ (_a = buckets_1.get(key)) === null || _a === void 0 ? void 0 : _a.push(field);
2197
+ });
2198
+ if (buckets_1.size > 1) {
2199
+ var trackers = order_1.map(function (key) { return ({
2200
+ key: key,
2201
+ list: buckets_1.get(key) || [],
2202
+ index: 0
2203
+ }); });
2204
+ var trimmed_1 = [];
2205
+ var added = true;
2206
+ while (trimmed_1.length < limit && added) {
2207
+ added = false;
2208
+ try {
2209
+ for (var trackers_1 = (e_2 = void 0, __values(trackers)), trackers_1_1 = trackers_1.next(); !trackers_1_1.done; trackers_1_1 = trackers_1.next()) {
2210
+ var tracker = trackers_1_1.value;
2211
+ if (trimmed_1.length >= limit) {
2212
+ break;
2213
+ }
2214
+ if (tracker.index < tracker.list.length) {
2215
+ trimmed_1.push(tracker.list[tracker.index]);
2216
+ tracker.index += 1;
2217
+ added = true;
2218
+ }
2219
+ }
2220
+ }
2221
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
2222
+ finally {
2223
+ try {
2224
+ if (trackers_1_1 && !trackers_1_1.done && (_a = trackers_1.return)) _a.call(trackers_1);
2225
+ }
2226
+ finally { if (e_2) throw e_2.error; }
2227
+ }
2228
+ }
2229
+ return { fields: trimmed_1, truncated: true, total: fields.length };
2230
+ }
2231
+ }
1956
2232
  var trimmed = fields.slice(0, limit);
1957
2233
  return { fields: trimmed, truncated: true, total: fields.length };
1958
2234
  }
@@ -2368,7 +2644,7 @@ function safeJsonParse(value) {
2368
2644
  }
2369
2645
  }
2370
2646
  function evaluateReportBuilderGuardrails(message) {
2371
- var e_1, _a;
2647
+ var e_3, _a;
2372
2648
  var normalized = String(message || '').toLowerCase();
2373
2649
  var patterns = [
2374
2650
  { pattern: /\b(source\s*code|full\s*code|entire\s*code|repo\s*dump|repository|git\s*clone)\b/i, reason: 'Code access is restricted.' },
@@ -2390,12 +2666,12 @@ function evaluateReportBuilderGuardrails(message) {
2390
2666
  }
2391
2667
  }
2392
2668
  }
2393
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
2669
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
2394
2670
  finally {
2395
2671
  try {
2396
2672
  if (patterns_1_1 && !patterns_1_1.done && (_a = patterns_1.return)) _a.call(patterns_1);
2397
2673
  }
2398
- finally { if (e_1) throw e_1.error; }
2674
+ finally { if (e_3) throw e_3.error; }
2399
2675
  }
2400
2676
  return null;
2401
2677
  }