@resolveio/server-lib 20.14.6 → 20.14.8
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/fixtures/cron-jobs.js +20 -3
- package/fixtures/cron-jobs.js.map +1 -1
- package/methods/ai-terminal.js +1 -0
- package/methods/ai-terminal.js.map +1 -1
- package/methods/cron-jobs.js +205 -0
- package/methods/cron-jobs.js.map +1 -1
- package/methods.ts +3 -0
- package/models/method.model.d.ts +1 -0
- package/models/method.model.js.map +1 -1
- package/package.json +1 -1
- package/server-app.js +42 -28
- package/server-app.js.map +1 -1
package/methods/cron-jobs.js
CHANGED
|
@@ -84,12 +84,15 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
84
84
|
};
|
|
85
85
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
86
86
|
exports.loadCronJobMethods = loadCronJobMethods;
|
|
87
|
+
var axios_1 = require("axios");
|
|
87
88
|
var Excel = require("exceljs");
|
|
88
89
|
var moment = require("moment-timezone");
|
|
89
90
|
var simpl_schema_1 = require("simpl-schema");
|
|
90
91
|
var cron_job_collection_1 = require("../collections/cron-job.collection");
|
|
91
92
|
var file_collection_1 = require("../collections/file.collection");
|
|
93
|
+
var openai_usage_ledger_collection_1 = require("../collections/openai-usage-ledger.collection");
|
|
92
94
|
var report_builder_report_collection_1 = require("../collections/report-builder-report.collection");
|
|
95
|
+
var resolveio_server_app_1 = require("../resolveio-server-app");
|
|
93
96
|
var common_1 = require("../util/common");
|
|
94
97
|
function getFilterTargets(fieldPath, report) {
|
|
95
98
|
if (!fieldPath) {
|
|
@@ -789,6 +792,103 @@ function applyClientGroupSortToData(results, report, sorts) {
|
|
|
789
792
|
};
|
|
790
793
|
sortGroupLevel(results, 0);
|
|
791
794
|
}
|
|
795
|
+
var OPENAI_USAGE_BILLING_ENDPOINT = 'https://backend.resolveio.com/api/openai-usage/report';
|
|
796
|
+
var OPENAI_USAGE_DEFAULT_LOOKBACK_DAYS = 30;
|
|
797
|
+
var OPENAI_USAGE_DEFAULT_BATCH_SIZE = 500;
|
|
798
|
+
var OPENAI_USAGE_DEFAULT_OVERLAP_MINUTES = 10;
|
|
799
|
+
var toNumberSetting = function (value, fallback, allowZero) {
|
|
800
|
+
if (allowZero === void 0) { allowZero = false; }
|
|
801
|
+
var parsed = Number(value);
|
|
802
|
+
if (!Number.isFinite(parsed)) {
|
|
803
|
+
return fallback;
|
|
804
|
+
}
|
|
805
|
+
if (allowZero && parsed === 0) {
|
|
806
|
+
return 0;
|
|
807
|
+
}
|
|
808
|
+
return parsed > 0 ? Math.floor(parsed) : fallback;
|
|
809
|
+
};
|
|
810
|
+
var normalizeString = function (value) {
|
|
811
|
+
if (typeof value !== 'string') {
|
|
812
|
+
return '';
|
|
813
|
+
}
|
|
814
|
+
return value.trim();
|
|
815
|
+
};
|
|
816
|
+
var normalizeIdentifier = function (value) {
|
|
817
|
+
if (value === undefined || value === null) {
|
|
818
|
+
return '';
|
|
819
|
+
}
|
|
820
|
+
return String(value).trim();
|
|
821
|
+
};
|
|
822
|
+
var normalizeDate = function (value) {
|
|
823
|
+
if (!value) {
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
var date = value instanceof Date ? value : new Date(value);
|
|
827
|
+
if (Number.isNaN(date.getTime())) {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
return date;
|
|
831
|
+
};
|
|
832
|
+
var resolveOpenAIUsageSyncConfig = function () {
|
|
833
|
+
var _a, _b;
|
|
834
|
+
var config = ((_a = resolveio_server_app_1.ResolveIOServer.getServerConfig) === null || _a === void 0 ? void 0 : _a.call(resolveio_server_app_1.ResolveIOServer)) || {};
|
|
835
|
+
var endpoint = normalizeString(config['OPENAI_USAGE_BILLING_URL']
|
|
836
|
+
|| config['OPENAI_USAGE_REPORT_URL']
|
|
837
|
+
|| process.env.OPENAI_USAGE_BILLING_URL
|
|
838
|
+
|| process.env.OPENAI_USAGE_REPORT_URL) || OPENAI_USAGE_BILLING_ENDPOINT;
|
|
839
|
+
var apiKey = normalizeString(config['OPENAI_USAGE_INGEST_KEY']
|
|
840
|
+
|| config['OPENAI_USAGE_API_KEY']
|
|
841
|
+
|| process.env.OPENAI_USAGE_INGEST_KEY
|
|
842
|
+
|| process.env.OPENAI_USAGE_API_KEY
|
|
843
|
+
|| process.env.OPENAI_USAGE_REPORT_KEY);
|
|
844
|
+
var batchSize = toNumberSetting(config['OPENAI_USAGE_SYNC_BATCH_SIZE'] || process.env.OPENAI_USAGE_SYNC_BATCH_SIZE, OPENAI_USAGE_DEFAULT_BATCH_SIZE);
|
|
845
|
+
var lookbackDays = toNumberSetting(config['OPENAI_USAGE_SYNC_LOOKBACK_DAYS'] || process.env.OPENAI_USAGE_SYNC_LOOKBACK_DAYS, OPENAI_USAGE_DEFAULT_LOOKBACK_DAYS);
|
|
846
|
+
var overlapMinutes = toNumberSetting(config['OPENAI_USAGE_SYNC_OVERLAP_MINUTES'] || process.env.OPENAI_USAGE_SYNC_OVERLAP_MINUTES, OPENAI_USAGE_DEFAULT_OVERLAP_MINUTES, true);
|
|
847
|
+
var enabledRaw = (_b = config['OPENAI_USAGE_SYNC_ENABLED']) !== null && _b !== void 0 ? _b : process.env.OPENAI_USAGE_SYNC_ENABLED;
|
|
848
|
+
var enabled = typeof enabledRaw === 'string'
|
|
849
|
+
? enabledRaw.trim().toLowerCase() !== 'false' && enabledRaw.trim() !== '0'
|
|
850
|
+
: enabledRaw !== false;
|
|
851
|
+
return { config: config, endpoint: endpoint, apiKey: apiKey, batchSize: batchSize, lookbackDays: lookbackDays, overlapMinutes: overlapMinutes, enabled: enabled };
|
|
852
|
+
};
|
|
853
|
+
var buildOpenAIUsageQuery = function (cursorAt, cursorId) {
|
|
854
|
+
var base = {
|
|
855
|
+
billable: { $ne: false }
|
|
856
|
+
};
|
|
857
|
+
if (!cursorId) {
|
|
858
|
+
base.timestamp = { $gte: cursorAt };
|
|
859
|
+
return base;
|
|
860
|
+
}
|
|
861
|
+
base.$or = [
|
|
862
|
+
{ timestamp: { $gt: cursorAt } },
|
|
863
|
+
{ timestamp: cursorAt, _id: { $gt: cursorId } }
|
|
864
|
+
];
|
|
865
|
+
return base;
|
|
866
|
+
};
|
|
867
|
+
var normalizeUsagePayloadEntry = function (entry) {
|
|
868
|
+
if (!entry) {
|
|
869
|
+
return null;
|
|
870
|
+
}
|
|
871
|
+
var timestamp = normalizeDate(entry.timestamp);
|
|
872
|
+
var idClient = normalizeIdentifier(entry.id_client);
|
|
873
|
+
if (!timestamp || !idClient) {
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
var id = normalizeIdentifier(entry._id) || normalizeIdentifier(entry.id) || '';
|
|
877
|
+
return {
|
|
878
|
+
_id: id || undefined,
|
|
879
|
+
id_client: idClient,
|
|
880
|
+
timestamp: timestamp,
|
|
881
|
+
model: normalizeString(entry.model) || 'unknown',
|
|
882
|
+
input_tokens: Number(entry.input_tokens || 0),
|
|
883
|
+
output_tokens: Number(entry.output_tokens || 0),
|
|
884
|
+
total_tokens: Number(entry.total_tokens || 0),
|
|
885
|
+
cost_estimate: Number(entry.cost_estimate || 0),
|
|
886
|
+
billable: entry.billable !== false,
|
|
887
|
+
category: normalizeString(entry.category),
|
|
888
|
+
id_request: normalizeIdentifier(entry.id_request),
|
|
889
|
+
id_conversation: normalizeIdentifier(entry.id_conversation)
|
|
890
|
+
};
|
|
891
|
+
};
|
|
792
892
|
function loadCronJobMethods(methodManager) {
|
|
793
893
|
methodManager.methods({
|
|
794
894
|
cronEmailMergedDocsCleanUp: {
|
|
@@ -836,6 +936,111 @@ function loadCronJobMethods(methodManager) {
|
|
|
836
936
|
});
|
|
837
937
|
}
|
|
838
938
|
},
|
|
939
|
+
cronOpenAIUsageBillingSync: {
|
|
940
|
+
function: function () {
|
|
941
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
942
|
+
var _a, config, endpoint, apiKey, batchSize, lookbackDays, overlapMinutes, enabled, rootUrl, jobDoc, runData, lastSyncedAt, lastSyncedId, lookbackMs, cursorAt, cursorId, totalSent, cursorMoved, source, i, query, entries, payloadEntries, lastEntry;
|
|
943
|
+
var _b;
|
|
944
|
+
return __generator(this, function (_c) {
|
|
945
|
+
switch (_c.label) {
|
|
946
|
+
case 0:
|
|
947
|
+
_a = resolveOpenAIUsageSyncConfig(), config = _a.config, endpoint = _a.endpoint, apiKey = _a.apiKey, batchSize = _a.batchSize, lookbackDays = _a.lookbackDays, overlapMinutes = _a.overlapMinutes, enabled = _a.enabled;
|
|
948
|
+
if (!enabled) {
|
|
949
|
+
return [2 /*return*/, true];
|
|
950
|
+
}
|
|
951
|
+
rootUrl = normalizeString(config['ROOT_URL']);
|
|
952
|
+
if (rootUrl === 'https://resolveio.com' || rootUrl === 'http://localhost:4200') {
|
|
953
|
+
return [2 /*return*/, true];
|
|
954
|
+
}
|
|
955
|
+
if (!endpoint || !apiKey) {
|
|
956
|
+
console.warn('[OpenAI Usage Sync] Missing endpoint or API key.');
|
|
957
|
+
return [2 /*return*/, false];
|
|
958
|
+
}
|
|
959
|
+
return [4 /*yield*/, cron_job_collection_1.CronJobs.findOne({ name: 'OpenAI Usage Billing Sync' })];
|
|
960
|
+
case 1:
|
|
961
|
+
jobDoc = _c.sent();
|
|
962
|
+
runData = jobDoc === null || jobDoc === void 0 ? void 0 : jobDoc.method_run_data;
|
|
963
|
+
lastSyncedAt = normalizeDate(runData === null || runData === void 0 ? void 0 : runData.last_synced_at);
|
|
964
|
+
lastSyncedId = normalizeString(runData === null || runData === void 0 ? void 0 : runData.last_synced_id);
|
|
965
|
+
lookbackMs = lookbackDays * 24 * 60 * 60 * 1000;
|
|
966
|
+
cursorAt = lastSyncedAt || new Date(Date.now() - lookbackMs);
|
|
967
|
+
if (lastSyncedAt && overlapMinutes > 0) {
|
|
968
|
+
cursorAt = new Date(cursorAt.getTime() - overlapMinutes * 60 * 1000);
|
|
969
|
+
}
|
|
970
|
+
cursorId = lastSyncedId;
|
|
971
|
+
totalSent = 0;
|
|
972
|
+
cursorMoved = false;
|
|
973
|
+
source = {
|
|
974
|
+
clientSlug: (_b = resolveio_server_app_1.ResolveIOServer.getClientName) === null || _b === void 0 ? void 0 : _b.call(resolveio_server_app_1.ResolveIOServer),
|
|
975
|
+
clientName: normalizeString(config['CLIENT_NAME']),
|
|
976
|
+
rootUrl: rootUrl,
|
|
977
|
+
reportedAt: new Date(),
|
|
978
|
+
appVersion: process.env.APP_VERSION
|
|
979
|
+
};
|
|
980
|
+
i = 0;
|
|
981
|
+
_c.label = 2;
|
|
982
|
+
case 2:
|
|
983
|
+
if (!(i < 1000)) return [3 /*break*/, 7];
|
|
984
|
+
query = buildOpenAIUsageQuery(cursorAt, cursorId);
|
|
985
|
+
return [4 /*yield*/, openai_usage_ledger_collection_1.OpenAIUsageLedger.find(query, {
|
|
986
|
+
sort: { timestamp: 1, _id: 1 },
|
|
987
|
+
limit: batchSize
|
|
988
|
+
})];
|
|
989
|
+
case 3:
|
|
990
|
+
entries = _c.sent();
|
|
991
|
+
if (!entries.length) {
|
|
992
|
+
return [3 /*break*/, 7];
|
|
993
|
+
}
|
|
994
|
+
payloadEntries = entries
|
|
995
|
+
.map(function (entry) { return normalizeUsagePayloadEntry(entry); })
|
|
996
|
+
.filter(Boolean);
|
|
997
|
+
if (!payloadEntries.length) return [3 /*break*/, 5];
|
|
998
|
+
return [4 /*yield*/, axios_1.default.post(endpoint, { entries: payloadEntries, source: source }, {
|
|
999
|
+
headers: {
|
|
1000
|
+
'Content-Type': 'application/json',
|
|
1001
|
+
'X-ResolveIO-OpenAI-Usage-Key': apiKey,
|
|
1002
|
+
'X-OpenAI-Usage-Key': apiKey,
|
|
1003
|
+
'X-API-Key': apiKey
|
|
1004
|
+
},
|
|
1005
|
+
timeout: 20000
|
|
1006
|
+
})];
|
|
1007
|
+
case 4:
|
|
1008
|
+
_c.sent();
|
|
1009
|
+
totalSent += payloadEntries.length;
|
|
1010
|
+
_c.label = 5;
|
|
1011
|
+
case 5:
|
|
1012
|
+
lastEntry = entries[entries.length - 1];
|
|
1013
|
+
if (lastEntry) {
|
|
1014
|
+
cursorAt = normalizeDate(lastEntry.timestamp) || cursorAt;
|
|
1015
|
+
cursorId = normalizeString(lastEntry._id) || cursorId;
|
|
1016
|
+
cursorMoved = true;
|
|
1017
|
+
}
|
|
1018
|
+
if (entries.length < batchSize) {
|
|
1019
|
+
return [3 /*break*/, 7];
|
|
1020
|
+
}
|
|
1021
|
+
_c.label = 6;
|
|
1022
|
+
case 6:
|
|
1023
|
+
i++;
|
|
1024
|
+
return [3 /*break*/, 2];
|
|
1025
|
+
case 7:
|
|
1026
|
+
if (!cursorMoved) return [3 /*break*/, 9];
|
|
1027
|
+
return [4 /*yield*/, cron_job_collection_1.CronJobs.updateOne({ name: 'OpenAI Usage Billing Sync' }, {
|
|
1028
|
+
$set: {
|
|
1029
|
+
'method_run_data.last_synced_at': cursorAt,
|
|
1030
|
+
'method_run_data.last_synced_id': cursorId,
|
|
1031
|
+
'method_run_data.last_sent_at': new Date(),
|
|
1032
|
+
'method_run_data.last_sent_count': totalSent
|
|
1033
|
+
}
|
|
1034
|
+
})];
|
|
1035
|
+
case 8:
|
|
1036
|
+
_c.sent();
|
|
1037
|
+
_c.label = 9;
|
|
1038
|
+
case 9: return [2 /*return*/, true];
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
},
|
|
839
1044
|
reportbuilderCronJob: {
|
|
840
1045
|
check: new simpl_schema_1.default({
|
|
841
1046
|
data: {
|