@oas-tools/oas-telemetry 0.7.0-alpha.4 → 0.7.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.
Files changed (112) hide show
  1. package/.env.example +6 -2
  2. package/README.md +35 -17
  3. package/dist/cjs/config/bootConfig.cjs +3 -1
  4. package/dist/cjs/config/config.cjs +7 -5
  5. package/dist/cjs/docs/openapi.yaml +1399 -0
  6. package/dist/cjs/routesManager.cjs +36 -48
  7. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs +43 -13
  8. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +10 -2
  9. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +21 -16
  10. package/dist/cjs/telemetry/initializeTelemetry.cjs +39 -15
  11. package/dist/cjs/telemetry/telemetryConfigurator.cjs +6 -9
  12. package/dist/cjs/telemetry/telemetryRegistry.cjs +11 -8
  13. package/dist/cjs/tlm-ai/agent.cjs +54 -84
  14. package/dist/cjs/tlm-ai/aiController.cjs +69 -47
  15. package/dist/cjs/tlm-ai/aiRoutes.cjs +10 -3
  16. package/dist/cjs/tlm-ai/aiService.cjs +109 -0
  17. package/dist/cjs/tlm-ai/tools.cjs +30 -268
  18. package/dist/cjs/tlm-auth/authController.cjs +91 -26
  19. package/dist/cjs/tlm-auth/authMiddleware.cjs +20 -7
  20. package/dist/cjs/tlm-auth/authRoutes.cjs +3 -2
  21. package/dist/cjs/tlm-log/logController.cjs +30 -36
  22. package/dist/cjs/tlm-log/logRoutes.cjs +3 -2
  23. package/dist/cjs/tlm-metric/metricsController.cjs +15 -8
  24. package/dist/cjs/tlm-metric/metricsRoutes.cjs +2 -1
  25. package/dist/cjs/tlm-plugin/pluginController.cjs +11 -1
  26. package/dist/cjs/tlm-plugin/pluginProcess.cjs +4 -2
  27. package/dist/cjs/tlm-plugin/pluginService.cjs +3 -0
  28. package/dist/cjs/tlm-trace/traceController.cjs +16 -9
  29. package/dist/cjs/tlm-trace/traceRoutes.cjs +2 -1
  30. package/dist/cjs/tlm-util/utilController.cjs +23 -2
  31. package/dist/cjs/tlm-util/utilRoutes.cjs +44 -5
  32. package/dist/cjs/utils/logger.cjs +35 -13
  33. package/dist/esm/config/bootConfig.js +2 -0
  34. package/dist/esm/config/config.js +4 -2
  35. package/dist/esm/docs/openapi.yaml +1399 -0
  36. package/dist/esm/routesManager.js +37 -49
  37. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js +32 -11
  38. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js +10 -2
  39. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +20 -13
  40. package/dist/esm/telemetry/initializeTelemetry.js +22 -14
  41. package/dist/esm/telemetry/telemetryConfigurator.js +7 -10
  42. package/dist/esm/telemetry/telemetryRegistry.js +10 -7
  43. package/dist/esm/tlm-ai/agent.js +37 -78
  44. package/dist/esm/tlm-ai/aiController.js +56 -39
  45. package/dist/esm/tlm-ai/aiRoutes.js +11 -4
  46. package/dist/esm/tlm-ai/aiService.js +94 -0
  47. package/dist/esm/tlm-ai/tools.js +29 -255
  48. package/dist/esm/tlm-auth/authController.js +62 -20
  49. package/dist/esm/tlm-auth/authMiddleware.js +18 -9
  50. package/dist/esm/tlm-auth/authRoutes.js +4 -3
  51. package/dist/esm/tlm-log/logController.js +26 -28
  52. package/dist/esm/tlm-log/logRoutes.js +4 -3
  53. package/dist/esm/tlm-metric/metricsController.js +10 -6
  54. package/dist/esm/tlm-metric/metricsRoutes.js +3 -2
  55. package/dist/esm/tlm-plugin/pluginController.js +2 -1
  56. package/dist/esm/tlm-plugin/pluginProcess.js +4 -2
  57. package/dist/esm/tlm-plugin/pluginService.js +4 -0
  58. package/dist/esm/tlm-trace/traceController.js +11 -7
  59. package/dist/esm/tlm-trace/traceRoutes.js +3 -2
  60. package/dist/esm/tlm-util/utilController.js +22 -0
  61. package/dist/esm/tlm-util/utilRoutes.js +40 -5
  62. package/dist/esm/utils/logger.js +35 -12
  63. package/dist/types/config/bootConfig.d.ts +1 -0
  64. package/dist/types/config/config.d.ts +6 -3
  65. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts +7 -1
  66. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts +1 -0
  67. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts +1 -0
  68. package/dist/types/telemetry/telemetryRegistry.d.ts +22 -6
  69. package/dist/types/tlm-ai/agent.d.ts +2 -2
  70. package/dist/types/tlm-ai/aiController.d.ts +5 -4
  71. package/dist/types/tlm-ai/aiRoutes.d.ts +1 -1
  72. package/dist/types/tlm-ai/aiService.d.ts +38 -0
  73. package/dist/types/tlm-ai/tools.d.ts +5 -14
  74. package/dist/types/tlm-auth/authController.d.ts +2 -1
  75. package/dist/types/tlm-log/logController.d.ts +2 -2
  76. package/dist/types/tlm-metric/metricsController.d.ts +2 -1
  77. package/dist/types/tlm-plugin/pluginService.d.ts +2 -0
  78. package/dist/types/tlm-trace/traceController.d.ts +2 -1
  79. package/dist/types/tlm-util/utilController.d.ts +1 -0
  80. package/dist/types/utils/logger.d.ts +5 -5
  81. package/dist/ui/assets/ApiDocsPage-C_VVPPHa.js +16 -0
  82. package/dist/ui/assets/CollapsibleCard-B3KR_8mL.js +1 -0
  83. package/dist/ui/assets/DevToolsPage-OyZcDcmw.js +1 -0
  84. package/dist/ui/assets/LandingPage-CppFBA6K.js +6 -0
  85. package/dist/ui/assets/LogsPage-9Fq8GArS.js +26 -0
  86. package/dist/ui/assets/NotFoundPage-B3quk3P1.js +1 -0
  87. package/dist/ui/assets/PluginCreatePage-X_aCH4t4.js +50 -0
  88. package/dist/ui/assets/PluginPage-DMDSihrZ.js +27 -0
  89. package/dist/ui/assets/alert-jQ9HCPIf.js +1133 -0
  90. package/dist/ui/assets/badge-CNq0-mH5.js +1 -0
  91. package/dist/ui/assets/card-DFAwwhN3.js +1 -0
  92. package/dist/ui/assets/chevron-down-CPsvsmqj.js +6 -0
  93. package/dist/ui/assets/chevron-up-Df9jMo1X.js +6 -0
  94. package/dist/ui/assets/circle-alert-DOPQPvU8.js +6 -0
  95. package/dist/ui/assets/index-BkD6DijD.js +15 -0
  96. package/dist/ui/assets/index-CERGVYZK.js +292 -0
  97. package/dist/ui/assets/index-CSIPf9qw.css +1 -0
  98. package/dist/ui/assets/input-Dzvg_ZEZ.js +1 -0
  99. package/dist/ui/assets/label-DuVnkZ4q.js +1 -0
  100. package/dist/ui/assets/loader-circle-CrvlRy5o.js +6 -0
  101. package/dist/ui/assets/loginPage-qa4V-B70.js +6 -0
  102. package/dist/ui/assets/select-DhS8YUtJ.js +1 -0
  103. package/dist/ui/assets/separator-isK4chBP.js +6 -0
  104. package/dist/ui/assets/severityOptions-O38dSOfk.js +11 -0
  105. package/dist/ui/assets/switch-Z3mImG9n.js +1 -0
  106. package/dist/ui/assets/tabs-_77MUUQe.js +16 -0
  107. package/dist/ui/assets/upload-C1LT4Gkb.js +16 -0
  108. package/dist/ui/assets/utilService-DNyqzwj0.js +1 -0
  109. package/dist/ui/index.html +2 -2
  110. package/package.json +18 -7
  111. package/dist/ui/assets/index-BzIdRox6.js +0 -1733
  112. package/dist/ui/assets/index-CkoHzrrt.css +0 -1
@@ -1,4 +1,4 @@
1
- import { json } from "express";
1
+ import { Router, json } from "express";
2
2
  import logger from "./utils/logger.js";
3
3
  import cors from 'cors';
4
4
  import { getTraceRoutes } from "./tlm-trace/traceRoutes.js";
@@ -16,62 +16,50 @@ export const configureRoutes = (router, oasTlmConfig) => {
16
16
  if (bootEnvVariables.OASTLM_BOOT_ENV === 'development') {
17
17
  logger.info("Running in development mode, enabling CORS for all origins");
18
18
  router.use(cors({
19
- origin: '*', // Permitir todas las solicitudes en desarrollo
20
- methods: ['GET', 'POST', 'PUT', 'DELETE'],
21
- allowedHeaders: ['Content-Type', 'Authorization'],
19
+ origin: (origin, callback) => {
20
+ if (!origin || /^http:\/\/localhost:\d+$/.test(origin)) {
21
+ callback(null, true);
22
+ }
23
+ else {
24
+ callback(new Error('Not allowed by CORS'));
25
+ }
26
+ },
27
+ credentials: true
22
28
  }));
23
29
  }
24
- router.use((req, res, next) => {
30
+ const telemetryBaseUrl = oasTlmConfig.general.baseUrl;
31
+ // Sub-router for all telemetry endpoints
32
+ const telemetryRouter = Router();
33
+ // Body parser for JSON requests
34
+ telemetryRouter.use((req, res, next) => {
25
35
  if (req.body !== undefined) {
26
36
  return next(); // Already parsed, no need to parse again.
27
37
  }
28
38
  return json({ limit: '10mb' })(req, res, next);
29
39
  });
30
- const allAuthMiddlewares = getWrappedMiddlewares(() => oasTlmConfig.auth.enabled, [cookieParser(), getAuthRoutes(oasTlmConfig), getAuthMiddleware(oasTlmConfig)]);
31
- const baseUrl = oasTlmConfig.general.baseUrl;
32
- router.use(baseUrl, allAuthMiddlewares);
33
- router.use(baseUrl + "/traces", getTraceRoutes());
34
- router.use(baseUrl + "/metrics", getMetricsRoutes());
35
- router.use(baseUrl + "/logs", getLogRoutes());
36
- router.use(baseUrl + "/ai", getWrappedMiddlewares(() => oasTlmConfig.ai.openAIKey !== null, [getAIRoutes(oasTlmConfig)]));
37
- // WARNING: This path must be the same as the one used in the UI package App.tsx "oas-telemetry-ui"
38
- router.use(baseUrl + "/oas-telemetry-ui", getUIRoutes());
39
- router.use(baseUrl + "/utils", getUtilsRoutes(oasTlmConfig));
40
- router.use(baseUrl + "/plugins", getPluginRoutes());
41
- router.get(baseUrl + '/health', (_req, res) => {
40
+ telemetryRouter.get('/health', (_req, res) => {
42
41
  res.status(200).send({ status: 'OK' });
43
42
  });
44
- //redirect to the UI when accessing the base URL
45
- router.get(baseUrl, (req, res) => {
46
- res.redirect(baseUrl + "/oas-telemetry-ui");
43
+ // Redirect to the UI when accessing the base URL
44
+ telemetryRouter.get('/', (req, res) => {
45
+ res.redirect(`${telemetryBaseUrl}/oas-telemetry-ui/`);
47
46
  });
47
+ // WARNING: This path must be the same as the one used in the UI package App.tsx "oas-telemetry-ui"
48
+ telemetryRouter.use("/oas-telemetry-ui", getUIRoutes());
49
+ telemetryRouter.use("/utils", getUtilsRoutes(oasTlmConfig));
50
+ // Auth routes must be registered. If authentication is not enabled, all requests will be allowed.
51
+ // Frontend will use these endpoints;
52
+ telemetryRouter.use(cookieParser());
53
+ // Refresh token uses /auth/refresh path, careful if you change it
54
+ telemetryRouter.use('/auth', getAuthRoutes(oasTlmConfig));
55
+ telemetryRouter.use(getAuthMiddleware(oasTlmConfig));
56
+ telemetryRouter.use("/traces", getTraceRoutes());
57
+ telemetryRouter.use("/metrics", getMetricsRoutes());
58
+ telemetryRouter.use("/logs", getLogRoutes());
59
+ if (oasTlmConfig.ai.openAIKey) {
60
+ telemetryRouter.use("/ai", getAIRoutes(oasTlmConfig));
61
+ }
62
+ telemetryRouter.use("/plugins", getPluginRoutes());
63
+ // Mount the telemetryRouter under telemetryBaseUrl
64
+ router.use(telemetryBaseUrl, telemetryRouter);
48
65
  };
49
- /**
50
- * This function wraps the provided middleware functions with a condition callback.
51
- * If the condition callback returns true, the middleware/router will be executed.
52
- * If the condition callback returns false, the middleware/router will be skipped.
53
- *
54
- * @callback {function} conditionCallback A callback function that returns a boolean to determine if the middleware should be used.
55
- * @param {Array} middlewares An array of middleware or routers to be wrapped.
56
- * @returns {Array} An array of wrapped middleware functions.
57
- */
58
- function getWrappedMiddlewares(conditionCallback, middlewares) {
59
- return middlewares.map(middleware => {
60
- return function (req, res, next) {
61
- if (conditionCallback()) {
62
- if (typeof middleware === 'function') {
63
- // look for handle property, if it exists, it's a router. If not call middleware
64
- if (middleware.handle) {
65
- middleware.handle(req, res, next);
66
- }
67
- else {
68
- middleware(req, res, next);
69
- }
70
- }
71
- }
72
- else {
73
- next();
74
- }
75
- };
76
- });
77
- }
@@ -5,6 +5,7 @@ import MiniSearch from 'minisearch';
5
5
  import { applyNesting, removeCircularRefs } from '../utils/circular.js';
6
6
  import { Enabler } from '../wrappers.js';
7
7
  import logger from '../../../utils/logger.js';
8
+ import { pluginService } from '../../../tlm-plugin/pluginService.js';
8
9
  export class InMemoryDbLogExporter extends Enabler {
9
10
  constructor(retentionTimeInSeconds = 3600) {
10
11
  super();
@@ -30,10 +31,6 @@ export class InMemoryDbLogExporter extends Enabler {
30
31
  * @param resultCallback
31
32
  */
32
33
  export(logs, resultCallback) {
33
- if (!this.isEnabled()) {
34
- resultCallback({ code: ExportResultCode.SUCCESS });
35
- return;
36
- }
37
34
  const logsToInsert = logs.map(logRecord => {
38
35
  // Remove circular references first, then apply nesting, then export info
39
36
  const formattedLog = this._formatLogRecord(logRecord);
@@ -41,7 +38,14 @@ export class InMemoryDbLogExporter extends Enabler {
41
38
  const nestedLog = applyNesting(cleanedLog);
42
39
  return nestedLog;
43
40
  });
44
- this._insertLogs(logsToInsert, resultCallback);
41
+ logsToInsert.forEach(log => {
42
+ pluginService.broadcastLog(log);
43
+ });
44
+ // ENABLED only affect storage not plugin broadcasting
45
+ if (this.isEnabled()) {
46
+ this._insertLogs(logsToInsert, resultCallback);
47
+ }
48
+ resultCallback({ code: ExportResultCode.SUCCESS });
45
49
  }
46
50
  reset() {
47
51
  this._db = new Datastore();
@@ -58,15 +62,28 @@ export class InMemoryDbLogExporter extends Enabler {
58
62
  this._db = null;
59
63
  this._miniSearch = null;
60
64
  }
61
- find(query, messageSearch, callback) {
65
+ async find(findConfig) {
66
+ const { query, messageSearch, limit, sortOrder } = findConfig;
67
+ const finalQuery = { ...query };
68
+ const effectiveSortOrder = sortOrder || { timestamp: -1 };
62
69
  if (messageSearch) {
63
- const searchResults = this._miniSearch.search(messageSearch);
70
+ const searchResults = this._miniSearch.search(messageSearch, { prefix: true, fuzzy: 0.2 });
64
71
  const ids = searchResults.map((result) => result._id);
65
72
  logger.debug(`MiniSearch found ${ids.length} results for search term "${messageSearch}"`, { depth: 3 });
66
- // Add MiniSearch results to the query
67
- query._id = { $in: ids };
73
+ finalQuery._id = { $in: ids };
68
74
  }
69
- this._db.find(query, callback);
75
+ const docs = await new Promise((resolve, reject) => {
76
+ this._db.find(finalQuery)
77
+ .sort(effectiveSortOrder)
78
+ .limit(limit)
79
+ .exec((err, docs) => {
80
+ if (err)
81
+ reject(err);
82
+ else
83
+ resolve(docs);
84
+ });
85
+ });
86
+ return docs;
70
87
  }
71
88
  insert(data, callback) {
72
89
  this._insertLogs(data, (result) => {
@@ -100,7 +117,8 @@ export class InMemoryDbLogExporter extends Enabler {
100
117
  attributes: logRecord.resource.attributes,
101
118
  },
102
119
  instrumentationScope: logRecord.instrumentationScope,
103
- timestamp: hrTimeToMicroseconds(logRecord.hrTime) || Date.now(),
120
+ timestamp: hrTimeToMicroseconds(logRecord.hrTime) ?? Date.now(),
121
+ observedTimestamp: hrTimeToMicroseconds(logRecord.hrTimeObserved) ?? Date.now(),
104
122
  traceId: logRecord.spanContext?.traceId,
105
123
  spanId: logRecord.spanContext?.spanId,
106
124
  traceFlags: logRecord.spanContext?.traceFlags,
@@ -114,6 +132,9 @@ export class InMemoryDbLogExporter extends Enabler {
114
132
  this._retentionTimeInSeconds = retentionTimeInSeconds;
115
133
  logger.info(`InMemoryDbLogExporter retention time set to ${this._retentionTimeInSeconds} seconds`);
116
134
  }
135
+ get retentionTimeInSeconds() {
136
+ return this._retentionTimeInSeconds;
137
+ }
117
138
  _insertLogs(logsToInsert, resultCallback) {
118
139
  this._db.insert(logsToInsert, (err, newDocs) => {
119
140
  if (err) {
@@ -3,6 +3,7 @@ import dataStore from '@seald-io/nedb';
3
3
  import { applyNesting } from '../utils/circular.js';
4
4
  import { Enabler } from '../wrappers.js';
5
5
  import logger from '../../../utils/logger.js';
6
+ import { pluginService } from '../../../tlm-plugin/pluginService.js';
6
7
  export class InMemoryDbMetricExporter extends Enabler {
7
8
  constructor(retentionTimeInSeconds = 3600) {
8
9
  super();
@@ -13,9 +14,13 @@ export class InMemoryDbMetricExporter extends Enabler {
13
14
  }
14
15
  export(metrics, resultCallback) {
15
16
  try {
17
+ const scopeMetrics = metrics?.scopeMetrics;
18
+ const cleanMetrics = applyNesting(scopeMetrics);
19
+ cleanMetrics.forEach((metric) => {
20
+ pluginService.broadcastMetric(metric);
21
+ });
22
+ // Insert only if exporter is enabled
16
23
  if (this.isEnabled()) {
17
- const scopeMetrics = metrics?.scopeMetrics;
18
- const cleanMetrics = applyNesting(scopeMetrics);
19
24
  this._metrics.insert(cleanMetrics, (err, _newDoc) => {
20
25
  if (err) {
21
26
  logger.error('Insertion Error:', err);
@@ -62,6 +67,9 @@ export class InMemoryDbMetricExporter extends Enabler {
62
67
  this._retentionTimeInSeconds = retentionTimeInSeconds;
63
68
  logger.info(`InMemoryDbMetricExporter retention time set to ${this._retentionTimeInSeconds} seconds`);
64
69
  }
70
+ get retentionTimeInSeconds() {
71
+ return this._retentionTimeInSeconds;
72
+ }
65
73
  _startCleanupJob() {
66
74
  const interval = 1000;
67
75
  setInterval(() => {
@@ -3,6 +3,7 @@ import dataStore from '@seald-io/nedb';
3
3
  import logger from '../../../utils/logger.js';
4
4
  import { applyNesting, removeCircularRefs } from '../utils/circular.js';
5
5
  import { Enabler } from '../wrappers.js';
6
+ import { pluginService } from '../../../tlm-plugin/pluginService.js';
6
7
  export class InMemoryDbSpanExporter extends Enabler {
7
8
  constructor(retentionTimeInSeconds = 3600) {
8
9
  super();
@@ -20,32 +21,38 @@ export class InMemoryDbSpanExporter extends Enabler {
20
21
  this._retentionTimeInSeconds = retentionTimeInSeconds;
21
22
  logger.info(`InMemoryDbSpanExporter retention time set to ${this._retentionTimeInSeconds} seconds`);
22
23
  }
24
+ get retentionTimeInSeconds() {
25
+ return this._retentionTimeInSeconds;
26
+ }
23
27
  export(readableSpans, resultCallback) {
24
28
  logger.debug('InMemoryDbSpanExporter.export called with spans: ', readableSpans.length);
25
29
  try {
26
- if (!this.isEnabled()) {
27
- logger.debug('InMemoryDbSpanExporter is not enabled. Skipping export.');
28
- return resultCallback({ code: ExportResultCode.SUCCESS });
29
- }
30
30
  // Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
31
31
  const cleanSpans = readableSpans
32
32
  .map(nestedSpan => removeCircularRefs(nestedSpan)) // to avoid JSON parsing error
33
33
  .map(span => applyNesting(span)) // to avoid dot notation in keys (neDB does not support dot notation in keys)
34
34
  .filter(span => {
35
- const target = span?.attributes?.http?.target; // Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils'
35
+ const target = span?.attributes?.http?.target;
36
+ // Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils/generate-log' or 'telemetry/utils/generate-wait'
36
37
  if (target && target.includes(this._baseUrl)) {
37
- return target.includes(this._baseUrl + '/utils');
38
+ return (target.includes("generate"));
38
39
  }
39
40
  return true;
40
41
  });
41
- // Insert spans into the in-memory database
42
- this._spans.insert(cleanSpans, (err, _newDoc) => {
43
- if (err) {
44
- logger.error(err);
45
- return;
46
- }
42
+ cleanSpans.forEach(span => {
43
+ pluginService.broadcastTrace(span);
47
44
  });
48
- setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
45
+ //
46
+ if (this.isEnabled()) {
47
+ // Insert spans into the in-memory database
48
+ this._spans.insert(cleanSpans, (err, _newDoc) => {
49
+ if (err) {
50
+ logger.error(err);
51
+ return;
52
+ }
53
+ });
54
+ }
55
+ return resultCallback({ code: ExportResultCode.SUCCESS });
49
56
  }
50
57
  catch (error) {
51
58
  logger.error('Error exporting spans\n' + error.message + '\n' + error.stack);
@@ -1,11 +1,12 @@
1
1
  import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
2
2
  import logger from '../utils/logger.js';
3
3
  import { registerInstrumentations } from '@opentelemetry/instrumentation';
4
- import { dynamicMultiLogProcessor, dynamicMultiSpanProcessor, oasTelemetryResource } from './telemetryRegistry.js';
4
+ import { dynamicMultiLogProcessor, dynamicMultiSpanProcessor, oasTelemetryResource, originalConsoleMethods } from './telemetryRegistry.js';
5
5
  import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
6
6
  import { SeverityNumber } from '@opentelemetry/api-logs';
7
7
  import { LoggerProvider } from '@opentelemetry/sdk-logs';
8
8
  import { bootEnvVariables } from '../config/bootConfig.js';
9
+ import util from 'util';
9
10
  // THIS INSTRUMENTATIONS NEED TO BE LOADED BEFORE ANYTHING ELSE
10
11
  // They use monkey-patching to instrument the HTTP server and client.
11
12
  // THIS FILE MUST BE CALLED BEFORE ANYTHING ELSE
@@ -41,22 +42,15 @@ function initializeLogs() {
41
42
  });
42
43
  // Get a logger instance
43
44
  const loggerInstance = loggerProvider.getLogger('oas-telemetry'); // Use loggerProvider to get the logger
44
- // Override console methods to emit logs via OpenTelemetry, like an instrumentation
45
- const originalConsoleMethods = {
46
- log: console.log,
47
- warn: console.warn,
48
- error: console.error,
49
- info: console.info,
50
- debug: console.debug,
51
- };
52
45
  Object.keys(originalConsoleMethods).forEach((method) => {
53
46
  // @ts-expect-error yes
54
47
  console[method] = (...args) => {
48
+ const severity = getSeverityForMethod(method);
55
49
  loggerInstance.emit({
56
- severityNumber: SeverityNumber[method.toUpperCase()] || SeverityNumber.INFO,
57
- severityText: method.toUpperCase(),
58
- body: args.join(' '),
59
- attributes: { 'source.source': `console.${method}` },
50
+ severityNumber: severity.number,
51
+ severityText: severity.text,
52
+ body: util.format(...args),
53
+ attributes: { 'source': `console.${method}`, "library": "oas-telemetry" },
60
54
  });
61
55
  // @ts-expect-error yes
62
56
  originalConsoleMethods[method](...args);
@@ -65,7 +59,21 @@ function initializeLogs() {
65
59
  }
66
60
  function initializeMetrics() {
67
61
  logger.info('📈 Initializing MeterProvider');
68
- // WARN: This is a custom provider that allows adding readers dynamically at runtime.
69
62
  // WARN: Default PeriodicExportingMetricReader is added post initialization (see telemetryConfigurator.ts)
70
63
  // The in memory exporter is added by default to that reader. More readers are allowed to be added dynamically
71
64
  }
65
+ function getSeverityForMethod(method) {
66
+ switch (method) {
67
+ case "log":
68
+ case "info":
69
+ return { number: SeverityNumber.INFO, text: "INFO" };
70
+ case "debug":
71
+ return { number: SeverityNumber.DEBUG, text: "DEBUG" };
72
+ case "warn":
73
+ return { number: SeverityNumber.WARN, text: "WARN" };
74
+ case "error":
75
+ return { number: SeverityNumber.ERROR, text: "ERROR" };
76
+ default:
77
+ return { number: SeverityNumber.INFO, text: "INFO" };
78
+ }
79
+ }
@@ -1,23 +1,27 @@
1
1
  import { BatchSpanProcessor, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
2
- import { dynamicMultiLogProcessor, dynamicMultiSpanProcessor, inMemoryDbLogExporter, inMemoryDbMetricExporter, inMemoryDbSpanExporter, multiLogExporter, multiSpanExporter, oasTelemetryResource, pluginLogExporter, pluginMetricExporter, pluginSpanExporter } from './telemetryRegistry.js';
2
+ import { dynamicMultiLogProcessor, dynamicMultiSpanProcessor, inMemoryDbLogExporter, inMemoryDbMetricExporter, inMemoryDbSpanExporter, multiLogExporter, multiSpanExporter, oasTelemetryResource } from './telemetryRegistry.js';
3
3
  import logger from '../utils/logger.js';
4
4
  import { BatchLogRecordProcessor, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';
5
5
  import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
6
6
  import { HostMetrics } from '@opentelemetry/host-metrics';
7
7
  import { bootEnvVariables } from '../config/bootConfig.js';
8
+ import { pluginService } from '../tlm-plugin/pluginService.js';
8
9
  export const configureTelemetry = (oasTlmConfig) => {
10
+ configurePlugins(oasTlmConfig);
9
11
  configureTraces(oasTlmConfig);
10
12
  configureMetrics(oasTlmConfig);
11
13
  configureLogs(oasTlmConfig);
12
14
  logger.info("✅ Telemetry configured successfully. All exporters are ready");
13
15
  };
16
+ function configurePlugins(oasTlmConfig) {
17
+ pluginService.enabled = oasTlmConfig.plugins.enabled;
18
+ }
14
19
  function configureTraces(oasTlmConfig) {
15
20
  // TRACES CONFIGURATION
16
21
  // [OT]Provider -> [OT]SpanProcessor(multiSpan) -> n Processors(eg mainProcessor, extra) -> 1 SpanExporter
17
22
  inMemoryDbSpanExporter.baseUrl = oasTlmConfig.general.baseUrl; // TODO this will be done with filters
18
23
  inMemoryDbSpanExporter.retentionTimeInSeconds = oasTlmConfig.traces.memoryExporter.retentionTimeSeconds;
19
24
  inMemoryDbSpanExporter.setEnabledValue(oasTlmConfig.traces.memoryExporter.enabled);
20
- pluginSpanExporter.setEnabledValue(oasTlmConfig.plugins.enabled);
21
25
  const mainExporter = multiSpanExporter;
22
26
  let mainProcessor = new BatchSpanProcessor(mainExporter);
23
27
  if (bootEnvVariables.OASTLM_BOOT_ENV !== 'production') {
@@ -25,7 +29,6 @@ function configureTraces(oasTlmConfig) {
25
29
  mainProcessor = new SimpleSpanProcessor(mainExporter);
26
30
  }
27
31
  mainExporter.addExporters(inMemoryDbSpanExporter); // Main exporter have at least the in-memory exporter used by the traces controller
28
- mainExporter.addExporters(pluginSpanExporter);
29
32
  mainExporter.addExporters(oasTlmConfig.traces.extraExporters);
30
33
  dynamicMultiSpanProcessor.addProcessors(mainProcessor);
31
34
  dynamicMultiSpanProcessor.addProcessors(oasTlmConfig.traces.extraProcessors);
@@ -43,7 +46,6 @@ function configureLogs(oasTlmConfig) {
43
46
  }
44
47
  mainExporter.addExporters(inMemoryDbLogExporter); // Main exporter have at least the in-memory exporter used by the logs controller
45
48
  mainExporter.addExporters(oasTlmConfig.logs.extraExporters);
46
- mainExporter.addExporters(pluginLogExporter); // Allow logs to be sent to plugins too
47
49
  dynamicMultiLogProcessor.addProcessors(mainProcessor);
48
50
  dynamicMultiLogProcessor.addProcessors(oasTlmConfig.logs.extraProcessors);
49
51
  }
@@ -57,14 +59,9 @@ function configureMetrics(oasTlmConfig) {
57
59
  exportIntervalMillis: oasTlmConfig.metrics.mainMetricReaderOptions.exportIntervalMillis,
58
60
  metricProducers: oasTlmConfig.metrics.mainMetricReaderOptions.metricProducers
59
61
  });
60
- const pluginReader = new PeriodicExportingMetricReader({
61
- exporter: pluginMetricExporter,
62
- exportIntervalMillis: oasTlmConfig.metrics.mainMetricReaderOptions.exportIntervalMillis,
63
- metricProducers: oasTlmConfig.metrics.mainMetricReaderOptions.metricProducers
64
- });
65
62
  const meterProvider = new MeterProvider({
66
63
  resource: oasTelemetryResource,
67
- readers: [mainReader, pluginReader, ...oasTlmConfig.metrics.extraReaders],
64
+ readers: [mainReader, ...oasTlmConfig.metrics.extraReaders],
68
65
  views: oasTlmConfig.metrics.extraViews || []
69
66
  });
70
67
  // TODO maybe hostMetrics are too much, consider using only a subset of them.
@@ -6,12 +6,10 @@ import { DynamicMultiSpanProcessor } from "./custom-implementations/processors/d
6
6
  import { DynamicMultiLogRecordProcessor } from "./custom-implementations/processors/dynamicMultiLogProcessor.js";
7
7
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
8
8
  import { resourceFromAttributes } from "@opentelemetry/resources";
9
- import { PluginSpanExporter } from "./custom-implementations/exporters/PluginSpanExporter.js";
10
- import { PluginLogExporter } from "./custom-implementations/exporters/PluginLogExporter.js";
11
- import { PluginMetricExporter } from "./custom-implementations/exporters/PluginMetricExporter.js";
9
+ import { bootEnvVariables } from "../config/bootConfig.js";
12
10
  // GLOBAL REGISTRY of telemetry components, used by SDKs and controllers.
13
11
  export const oasTelemetryResource = resourceFromAttributes({
14
- [ATTR_SERVICE_NAME]: 'oas-telemetry-service'
12
+ [ATTR_SERVICE_NAME]: bootEnvVariables.OASTLM_BOOT_SERVICE_NAME
15
13
  });
16
14
  // TRACES -------------------------------------------------------------------------------------
17
15
  // This is the main exporter for oas-telemetry spans. (Used by the traces controller)
@@ -20,15 +18,20 @@ export const inMemoryDbSpanExporter = new InMemoryDbSpanExporter();
20
18
  export const multiSpanExporter = new EnablerMultiSpanExporter();
21
19
  // This allows the addition of more processors at runtime
22
20
  export const dynamicMultiSpanProcessor = new DynamicMultiSpanProcessor();
23
- export const pluginSpanExporter = new PluginSpanExporter(); // This exporter sends spans to the plugin module.
24
21
  // LOGS ----------------------------------------------------------------------------------------
25
22
  export const inMemoryDbLogExporter = new InMemoryDbLogExporter();
26
23
  export const multiLogExporter = new EnablerMultiLogExporter();
27
24
  export const dynamicMultiLogProcessor = new DynamicMultiLogRecordProcessor();
28
- export const pluginLogExporter = new PluginLogExporter(); // This exporter sends logs to the plugin module.
25
+ // Override console methods to emit logs via OpenTelemetry, like an instrumentation
26
+ export const originalConsoleMethods = {
27
+ log: console.log,
28
+ warn: console.warn,
29
+ error: console.error,
30
+ info: console.info,
31
+ debug: console.debug,
32
+ };
29
33
  // METRICS -------------------------------------------------------------------------------------
30
34
  // Metrics follow a different pattern in OpenTelemetry
31
35
  export const inMemoryDbMetricExporter = new InMemoryDbMetricExporter();
32
- export const pluginMetricExporter = new PluginMetricExporter(); // This exporter sends metrics to the plugin module.
33
36
  // Readers and their exporters cannot be grouped together in a MultiReader or similar construct
34
37
  // due to differences in aggregation temporality and aggregation selection.
@@ -1,86 +1,45 @@
1
- import OpenAI from 'openai';
2
1
  import { tools, availableTools } from './tools.js';
3
2
  import logger from '../utils/logger.js';
4
- export function getAgent(oasTlmConfig) {
5
- let openai;
6
- try {
7
- if (!oasTlmConfig.ai.openAIKey) {
8
- openai = null;
9
- }
10
- else {
11
- openai = new OpenAI({
12
- apiKey: oasTlmConfig.ai.openAIKey ?? undefined,
13
- dangerouslyAllowBrowser: true,
14
- });
15
- }
16
- }
17
- catch {
18
- openai = null;
19
- }
20
- const messages = [
21
- {
22
- role: "assistant",
23
- content: "You are a helpful telemetry assistant. Only use the functions you have been provided with. If the question is not related to the functions, respond with 'I cannot help with that.'. If you need to call to other agents, do so using the tools provided."
24
- },
25
- ];
26
- // Add extra context prompts if provided
27
- if (oasTlmConfig.ai.extraContextPrompts) {
28
- for (const prompt of oasTlmConfig.ai.extraContextPrompts) {
29
- messages.push({
30
- role: "system",
31
- content: prompt,
32
- });
33
- }
34
- }
35
- async function agent(userInput) {
36
- if (!openai) {
37
- logger.error("OpenAI client is not initialized. Please check your OpenAI API key.");
38
- return { content: "OpenAI client is not initialized. Please check your OpenAI API key." };
39
- }
40
- messages.push({
41
- role: "user",
42
- content: userInput,
3
+ export async function agent(openai, messages, model = "gpt-3.5-turbo", extraPrompts = []) {
4
+ for (let i = 0; i < 5; i++) {
5
+ const modelResponse = await openai.chat.completions.create({
6
+ model,
7
+ messages,
8
+ tools,
43
9
  });
44
- for (let i = 0; i < 5; i++) {
45
- const response = await (openai?.chat?.completions?.create?.({
46
- model: oasTlmConfig.ai.openAIModel,
47
- messages: messages,
48
- tools: tools,
49
- }));
50
- const { finish_reason, message } = response.choices[0];
51
- if (finish_reason === "tool_calls" && message.tool_calls) {
52
- logger.debug("Tool calls detected:", message.tool_calls);
53
- const results = [];
54
- for (const toolCall of message.tool_calls) {
55
- const functionName = toolCall.function.name;
56
- const functionToCall = availableTools[functionName];
57
- const functionArgs = JSON.parse(toolCall.function.arguments);
58
- const functionArgsArr = Object.values(functionArgs);
59
- // @ts-expect-error yes
60
- // eslint-disable-next-line prefer-spread
61
- const functionResponse = await functionToCall.apply(null, functionArgsArr);
62
- results.push({
63
- name: functionName,
64
- response: functionResponse,
65
- });
66
- }
67
- const resultMessage = results.map(({ name, response }) => `Result from "${name}":\n${JSON.stringify(response, null, 2)}`).join("\n\n");
68
- messages.push({
69
- role: "function",
70
- name: "multiple_tool_calls",
71
- content: resultMessage,
10
+ const { finish_reason, message } = modelResponse.choices[0];
11
+ if (finish_reason === "tool_calls" && message.tool_calls) {
12
+ logger.debug("Tool calls detected:", message.tool_calls);
13
+ const results = [];
14
+ for (const toolCall of message.tool_calls) {
15
+ const functionName = toolCall.function.name;
16
+ const functionToCall = availableTools[functionName];
17
+ const functionArgs = JSON.parse(toolCall.function.arguments);
18
+ const functionArgsArr = Object.values(functionArgs);
19
+ // @ts-expect-error yes
20
+ // eslint-disable-next-line prefer-spread
21
+ const functionResponse = await functionToCall.apply(null, functionArgsArr);
22
+ results.push({
23
+ name: functionName,
24
+ response: functionResponse,
72
25
  });
73
26
  }
74
- else if (finish_reason === "stop") {
75
- messages.push(message);
76
- return message;
77
- }
27
+ const resultMessage = results.map(({ name, response }, idx) => {
28
+ const toolCall = message.tool_calls?.[idx];
29
+ const params = toolCall ? JSON.parse(toolCall.function.arguments) : {};
30
+ return `Tool "${name}" called with parameters:\n${JSON.stringify(params, null, 2)}\nResult:\n${JSON.stringify(response, null, 2)}`;
31
+ }).join("\n\n");
32
+ messages.push({
33
+ role: "function",
34
+ name: "multiple_tool_calls",
35
+ content: resultMessage,
36
+ timestamp: new Date().toISOString()
37
+ });
38
+ }
39
+ else if (finish_reason === "stop") {
40
+ messages.push({ ...message, timestamp: new Date().toISOString() });
41
+ return;
78
42
  }
79
- return { content: "Se alcanzó el número máximo de iteraciones sin una respuesta adecuada. Intenta con una consulta más específica." };
80
43
  }
81
- return async function getAgentResponse(question) {
82
- const response = await agent(question);
83
- logger.debug("Response from agent:", response);
84
- return response.content;
85
- };
44
+ messages.push({ role: "assistant", content: "Maximum iterations reached without a suitable response. Try a more specific query.", timestamp: new Date().toISOString() });
86
45
  }