@resolveio/server-lib 20.14.2 → 20.14.3

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.
@@ -90,11 +90,14 @@ var user_collection_1 = require("../collections/user.collection");
90
90
  var openai_usage_ledger_manager_1 = require("../managers/openai-usage-ledger.manager");
91
91
  var pagination_model_1 = require("../models/pagination.model");
92
92
  var resolveio_server_app_1 = require("../resolveio-server-app");
93
- var openai_client_1 = require("../services/openai-client");
93
+ var codex_client_1 = require("../services/codex-client");
94
94
  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 DEFAULT_REPORT_BUILDER_CODEX_MODEL = 'gpt-5.2-codex';
99
+ var DEFAULT_REPORT_BUILDER_CODEX_TIMEOUT_MS = 60000;
100
+ var reportBuilderCodexClient = null;
98
101
  function loadReportBuilderMethods(methodManager) {
99
102
  methodManager.methods({
100
103
  reportBuilderAiSuggest: {
@@ -1660,10 +1663,9 @@ function expandLayoutColumnFilters(filters, selectedFields) {
1660
1663
  }
1661
1664
  function executeReportBuilderAi(payload, context) {
1662
1665
  return __awaiter(this, void 0, void 0, function () {
1663
- var input, prompt, isSuperAdmin, guardrailsEnabled, guardrail, reportType, collectionRoot, collectionJoins, collections, resolvedFields, fieldLimit, trimmedFields, systemPrompt, userPrompt, openaiSettings, client, responseFormat, response, usage, parsed, sanitizeContext, patch, summaryNotes, idClient;
1664
- var _a;
1665
- return __generator(this, function (_b) {
1666
- switch (_b.label) {
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
+ return __generator(this, function (_a) {
1668
+ switch (_a.label) {
1667
1669
  case 0:
1668
1670
  input = payload || {};
1669
1671
  prompt = normalizeOptionalString(input.prompt);
@@ -1672,7 +1674,7 @@ function executeReportBuilderAi(payload, context) {
1672
1674
  }
1673
1675
  return [4 /*yield*/, resolveIsSuperAdmin(context === null || context === void 0 ? void 0 : context.id_user)];
1674
1676
  case 1:
1675
- isSuperAdmin = _b.sent();
1677
+ isSuperAdmin = _a.sent();
1676
1678
  guardrailsEnabled = input.guardrails !== false && !isSuperAdmin;
1677
1679
  if (guardrailsEnabled) {
1678
1680
  guardrail = evaluateReportBuilderGuardrails(prompt);
@@ -1687,6 +1689,7 @@ function executeReportBuilderAi(payload, context) {
1687
1689
  resolvedFields = sanitizeFields(input.available_fields || []);
1688
1690
  fieldLimit = normalizeFieldLimit(input.field_limit);
1689
1691
  trimmedFields = trimFieldsForPrompt(resolvedFields, fieldLimit);
1692
+ arraySummary = buildArrayFieldSummary(trimmedFields.fields);
1690
1693
  systemPrompt = buildReportBuilderSystemPrompt(reportType);
1691
1694
  userPrompt = buildReportBuilderUserPrompt({
1692
1695
  prompt: prompt,
@@ -1697,22 +1700,29 @@ function executeReportBuilderAi(payload, context) {
1697
1700
  idDateField: normalizeOptionalString(input.id_date_field),
1698
1701
  dateInterval: normalizeOptionalString(input.date_interval),
1699
1702
  collections: collections,
1700
- fields: trimmedFields.fields
1703
+ fields: trimmedFields.fields,
1704
+ fieldMeta: {
1705
+ truncated: trimmedFields.truncated,
1706
+ total: trimmedFields.total,
1707
+ used: trimmedFields.fields.length
1708
+ },
1709
+ arraySummary: arraySummary
1701
1710
  });
1702
- openaiSettings = resolveReportBuilderOpenAISettings(input.config);
1703
- client = new openai_client_1.OpenAIClient(openaiSettings);
1704
- responseFormat = normalizeOpenAIResponseFormat((_a = input.config) === null || _a === void 0 ? void 0 : _a.response_format) || 'json';
1705
- return [4 /*yield*/, client.chat([
1706
- { role: 'system', content: systemPrompt },
1707
- { role: 'user', content: userPrompt }
1708
- ], { timeoutMs: 60000, responseFormat: responseFormat })];
1711
+ codexModel = resolveReportBuilderCodexModel(input.config);
1712
+ codexClient = getReportBuilderCodexClient();
1713
+ codexPrompt = buildReportBuilderCodexPrompt(systemPrompt, userPrompt);
1714
+ runOptions = {
1715
+ timeoutMs: resolveReportBuilderCodexTimeoutMs(input.config),
1716
+ threadOptions: resolveReportBuilderCodexThreadOptions(input.config, codexModel)
1717
+ };
1718
+ return [4 /*yield*/, codexClient.run(codexPrompt, runOptions)];
1709
1719
  case 2:
1710
- response = _b.sent();
1711
- usage = response.usage || estimateUsage([
1720
+ responseText = _a.sent();
1721
+ usage = estimateUsage([
1712
1722
  { role: 'system', content: systemPrompt },
1713
1723
  { role: 'user', content: userPrompt }
1714
- ], response.content, response.model || openaiSettings.model);
1715
- parsed = safeJsonParse(response.content);
1724
+ ], responseText, codexModel);
1725
+ parsed = safeJsonParse(responseText);
1716
1726
  if (!parsed || typeof parsed !== 'object') {
1717
1727
  throw new Error('AI response was not valid JSON.');
1718
1728
  }
@@ -1726,20 +1736,20 @@ function executeReportBuilderAi(payload, context) {
1726
1736
  });
1727
1737
  return [4 /*yield*/, resolveClientId(input.id_client, context === null || context === void 0 ? void 0 : context.id_user)];
1728
1738
  case 3:
1729
- idClient = _b.sent();
1739
+ idClient = _a.sent();
1730
1740
  if (!idClient) return [3 /*break*/, 5];
1731
1741
  return [4 /*yield*/, (0, openai_usage_ledger_manager_1.recordOpenAIUsage)({
1732
1742
  id_client: idClient,
1733
- model: response.model || openaiSettings.model || 'unknown',
1743
+ model: codexModel || 'unknown',
1734
1744
  input_tokens: usage.inputTokens,
1735
1745
  output_tokens: usage.outputTokens,
1736
1746
  total_tokens: usage.totalTokens,
1737
1747
  category: 'report-builder-ai',
1738
- id_request: response.requestId || ''
1748
+ id_request: ''
1739
1749
  })];
1740
1750
  case 4:
1741
- _b.sent();
1742
- _b.label = 5;
1751
+ _a.sent();
1752
+ _a.label = 5;
1743
1753
  case 5: return [2 /*return*/, {
1744
1754
  patch: patch,
1745
1755
  notes: summaryNotes,
@@ -1748,7 +1758,7 @@ function executeReportBuilderAi(payload, context) {
1748
1758
  output_tokens: usage.outputTokens,
1749
1759
  total_tokens: usage.totalTokens
1750
1760
  },
1751
- model: response.model || openaiSettings.model
1761
+ model: codexModel
1752
1762
  }];
1753
1763
  }
1754
1764
  });
@@ -1771,7 +1781,17 @@ function buildReportBuilderSystemPrompt(reportType) {
1771
1781
  '- id_date_field (string)',
1772
1782
  '- date_interval (Seconds|Minutes|Hours|Daily|Weekly|Monthly|Quarterly|Yearly)',
1773
1783
  '- notes (string)',
1774
- 'Use only provided collections and field_path values.',
1784
+ 'Use only provided collections and field_path values (they reflect the current models/collections).',
1785
+ 'Always set collection_root to the best matching collection from available_collections.',
1786
+ 'Prefer exact field_path values from available_fields; never invent field paths.',
1787
+ 'Use field_type to guide filters/totals (numeric fields for totals, date fields for date_interval, boolean fields for boolean_value).',
1788
+ 'If field_summary.truncated is true, only use provided fields and note missing needs in notes.',
1789
+ 'If you need joined data, add collection_joins with an alias and use the alias in field_path.',
1790
+ 'array_summary lists array roots and candidate key_fields for linking arrays.',
1791
+ 'If you select fields from multiple array roots, add links that align the arrays using key_fields.',
1792
+ 'Avoid mixing unrelated array roots without links; prefer a single array root or non-array fields to prevent duplicate rows.',
1793
+ 'Return a single valid JSON object with double quotes and no trailing commas.',
1794
+ 'Omit keys you cannot confidently fill.',
1775
1795
  'Conditions allowed: eq, ne, lt, lte, gt, gte, bw, nnull, null, regex.',
1776
1796
  "Report type: ".concat(reportType || 'List', ". Ensure groups_row is set for Group/Dated reports."),
1777
1797
  'If unsure, leave a section empty instead of guessing.'
@@ -1789,32 +1809,62 @@ function buildReportBuilderUserPrompt(input) {
1789
1809
  },
1790
1810
  available_collections: input.collections || [],
1791
1811
  available_fields: input.fields || [],
1812
+ field_summary: input.fieldMeta || undefined,
1813
+ array_summary: input.arraySummary || [],
1792
1814
  request: input.prompt
1793
1815
  };
1794
1816
  return JSON.stringify(context);
1795
1817
  }
1796
- function resolveReportBuilderOpenAISettings(config) {
1797
- var _a;
1818
+ function resolveReportBuilderCodexTimeoutMs(config) {
1819
+ var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
1820
+ var raw = normalizeOptionalNumber((config === null || config === void 0 ? void 0 : config.timeout_ms)
1821
+ || serverConfig['REPORT_BUILDER_CODEX_TIMEOUT_MS']
1822
+ || process.env.REPORT_BUILDER_CODEX_TIMEOUT_MS);
1823
+ if (raw && raw > 0) {
1824
+ return (0, common_1.round)(raw);
1825
+ }
1826
+ return DEFAULT_REPORT_BUILDER_CODEX_TIMEOUT_MS;
1827
+ }
1828
+ function resolveReportBuilderCodexModel(config) {
1829
+ var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
1830
+ var overrideModel = normalizeOptionalString(config === null || config === void 0 ? void 0 : config.model);
1831
+ var raw = overrideModel
1832
+ || normalizeOptionalString(serverConfig['REPORT_BUILDER_CODEX_MODEL'] || process.env.REPORT_BUILDER_CODEX_MODEL)
1833
+ || normalizeOptionalString(serverConfig['AI_DASHBOARD_CODEX_MODEL'] || process.env.AI_DASHBOARD_CODEX_MODEL)
1834
+ || normalizeOptionalString(serverConfig['AI_TERMINAL_CODEX_MODEL'] || process.env.AI_TERMINAL_CODEX_MODEL)
1835
+ || normalizeOptionalString(serverConfig['AI_ASSISTANT_CODEX_MODEL'] || process.env.AI_ASSISTANT_CODEX_MODEL);
1836
+ return raw || DEFAULT_REPORT_BUILDER_CODEX_MODEL;
1837
+ }
1838
+ function resolveReportBuilderCodexSettings() {
1798
1839
  var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
1799
1840
  var apiKey = (serverConfig['OPENAI_API_KEY'] || process.env.OPENAI_API_KEY || '').trim();
1800
1841
  if (!apiKey) {
1801
1842
  throw new Error('OpenAI API key missing. Add OPENAI_API_KEY to server config.');
1802
1843
  }
1803
- var overrideModel = normalizeOptionalString(config === null || config === void 0 ? void 0 : config.model);
1804
- var model = overrideModel
1805
- || normalizeOptionalString(serverConfig['REPORT_BUILDER_OPENAI_MODEL'] || process.env.REPORT_BUILDER_OPENAI_MODEL)
1806
- || normalizeOptionalString(serverConfig['OPENAI_MODEL'] || process.env.OPENAI_MODEL)
1807
- || 'gpt-5.1';
1808
1844
  return {
1809
1845
  apiKey: apiKey,
1810
1846
  baseUrl: (serverConfig['OPENAI_BASE_URL'] || process.env.OPENAI_BASE_URL || '').trim() || undefined,
1811
- model: model,
1812
- temperature: (_a = normalizeOptionalNumber(config === null || config === void 0 ? void 0 : config.temperature)) !== null && _a !== void 0 ? _a : 0.2,
1813
- maxTokens: normalizeOptionalNumber(config === null || config === void 0 ? void 0 : config.max_tokens),
1847
+ model: resolveReportBuilderCodexModel(),
1814
1848
  maxRetries: normalizeOptionalNumber(serverConfig['OPENAI_MAX_RETRIES'] || process.env.OPENAI_MAX_RETRIES),
1815
1849
  retryDelayMs: normalizeOptionalNumber(serverConfig['OPENAI_RETRY_DELAY_MS'] || process.env.OPENAI_RETRY_DELAY_MS)
1816
1850
  };
1817
1851
  }
1852
+ function resolveReportBuilderCodexThreadOptions(config, model) {
1853
+ var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
1854
+ var effort = normalizeReasoningEffort((config === null || config === void 0 ? void 0 : config.reasoning_effort)
1855
+ || serverConfig['REPORT_BUILDER_CODEX_REASONING_EFFORT']
1856
+ || process.env.REPORT_BUILDER_CODEX_REASONING_EFFORT) || 'low';
1857
+ return __assign(__assign({}, (model ? { model: model } : {})), { sandboxMode: 'read-only', skipGitRepoCheck: true, modelReasoningEffort: effort, networkAccessEnabled: false, webSearchMode: 'disabled', webSearchEnabled: false, approvalPolicy: 'never' });
1858
+ }
1859
+ function getReportBuilderCodexClient() {
1860
+ if (!reportBuilderCodexClient) {
1861
+ reportBuilderCodexClient = new codex_client_1.CodexClient(resolveReportBuilderCodexSettings());
1862
+ }
1863
+ return reportBuilderCodexClient;
1864
+ }
1865
+ function buildReportBuilderCodexPrompt(systemPrompt, userPrompt) {
1866
+ return "System:\n".concat(systemPrompt, "\n\nUser:\n").concat(userPrompt).trim();
1867
+ }
1818
1868
  function normalizeReportType(value) {
1819
1869
  var normalized = normalizeOptionalString(value);
1820
1870
  if (normalized.toLowerCase() === 'group') {
@@ -1833,28 +1883,10 @@ function normalizeOptionalNumber(value) {
1833
1883
  var parsed = Number(value);
1834
1884
  return Number.isFinite(parsed) ? parsed : undefined;
1835
1885
  }
1836
- function normalizeOpenAIResponseFormat(value) {
1837
- if (!value) {
1838
- return undefined;
1839
- }
1840
- if (typeof value === 'string') {
1841
- var normalized = value.trim().toLowerCase();
1842
- if (!normalized) {
1843
- return undefined;
1844
- }
1845
- if (normalized === 'text' || normalized === 'json' || normalized === 'json_object') {
1846
- return normalized;
1847
- }
1848
- return { type: normalized };
1849
- }
1850
- if (typeof value === 'object') {
1851
- var typeValue = normalizeOptionalString(value.type);
1852
- if (!typeValue) {
1853
- return undefined;
1854
- }
1855
- return __assign(__assign({}, value), { type: typeValue });
1856
- }
1857
- return undefined;
1886
+ function normalizeReasoningEffort(value) {
1887
+ var normalized = normalizeOptionalString(value).toLowerCase();
1888
+ var allowed = new Set(['minimal', 'low', 'medium', 'high', 'xhigh']);
1889
+ return allowed.has(normalized) ? normalized : undefined;
1858
1890
  }
1859
1891
  function normalizeFieldLimit(value) {
1860
1892
  var parsed = Number(value);
@@ -1924,6 +1956,31 @@ function trimFieldsForPrompt(fields, limit) {
1924
1956
  var trimmed = fields.slice(0, limit);
1925
1957
  return { fields: trimmed, truncated: true, total: fields.length };
1926
1958
  }
1959
+ function buildArrayFieldSummary(fields) {
1960
+ var roots = new Map();
1961
+ (fields || []).forEach(function (field) {
1962
+ var fieldPath = normalizeOptionalString(field === null || field === void 0 ? void 0 : field.field_path);
1963
+ if (!fieldPath || !fieldPath.includes('.$')) {
1964
+ return;
1965
+ }
1966
+ var root = resolveArrayRoot(fieldPath);
1967
+ if (!root) {
1968
+ return;
1969
+ }
1970
+ var entry = roots.get(root);
1971
+ if (!entry) {
1972
+ entry = { root_path: root, key_fields: [], sample_fields: [] };
1973
+ roots.set(root, entry);
1974
+ }
1975
+ if (!entry.sample_fields.includes(fieldPath) && entry.sample_fields.length < 8) {
1976
+ entry.sample_fields.push(fieldPath);
1977
+ }
1978
+ if (isLinkCandidateFieldPath(fieldPath) && !entry.key_fields.includes(fieldPath) && entry.key_fields.length < 8) {
1979
+ entry.key_fields.push(fieldPath);
1980
+ }
1981
+ });
1982
+ return Array.from(roots.values());
1983
+ }
1927
1984
  function buildSanitizerContext(collections, fields) {
1928
1985
  var collectionIndex = new Map();
1929
1986
  var fieldIndex = new Map();
@@ -2259,6 +2316,37 @@ function normalizeKey(value) {
2259
2316
  function normalizePathKey(value) {
2260
2317
  return normalizeOptionalString(value).toLowerCase();
2261
2318
  }
2319
+ function resolveArrayRoot(path) {
2320
+ var raw = normalizeOptionalString(path);
2321
+ if (!raw) {
2322
+ return '';
2323
+ }
2324
+ var idx = raw.indexOf('.$');
2325
+ if (idx === -1) {
2326
+ return '';
2327
+ }
2328
+ return raw.slice(0, idx);
2329
+ }
2330
+ function isLinkCandidateFieldPath(path) {
2331
+ var normalized = normalizeOptionalString(path).toLowerCase();
2332
+ if (!normalized) {
2333
+ return false;
2334
+ }
2335
+ var last = (normalized.split('.').pop() || '').replace(/\$/g, '');
2336
+ if (!last) {
2337
+ return false;
2338
+ }
2339
+ if (last === 'id' || last === '_id') {
2340
+ return true;
2341
+ }
2342
+ if (last.endsWith('_id') || last.endsWith('_uuid') || last.endsWith('_guid')) {
2343
+ return true;
2344
+ }
2345
+ if (last.startsWith('id_')) {
2346
+ return true;
2347
+ }
2348
+ return false;
2349
+ }
2262
2350
  function safeJsonParse(value) {
2263
2351
  if (!value) {
2264
2352
  return null;