@juspay/neurolink 9.41.0 → 9.42.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 (189) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +7 -1
  3. package/dist/auth/anthropicOAuth.d.ts +18 -3
  4. package/dist/auth/anthropicOAuth.js +137 -4
  5. package/dist/auth/providers/firebase.js +5 -1
  6. package/dist/auth/providers/jwt.js +5 -1
  7. package/dist/auth/providers/workos.js +5 -1
  8. package/dist/auth/sessionManager.d.ts +1 -1
  9. package/dist/auth/sessionManager.js +58 -27
  10. package/dist/browser/neurolink.min.js +337 -318
  11. package/dist/cli/commands/mcp.js +3 -0
  12. package/dist/cli/commands/proxy.d.ts +2 -1
  13. package/dist/cli/commands/proxy.js +279 -16
  14. package/dist/cli/commands/task.js +3 -0
  15. package/dist/cli/factories/commandFactory.d.ts +2 -0
  16. package/dist/cli/factories/commandFactory.js +38 -0
  17. package/dist/cli/parser.js +4 -3
  18. package/dist/client/aiSdkAdapter.js +3 -0
  19. package/dist/client/streamingClient.js +30 -10
  20. package/dist/core/modules/GenerationHandler.js +3 -2
  21. package/dist/core/redisConversationMemoryManager.js +7 -3
  22. package/dist/evaluation/BatchEvaluator.js +4 -1
  23. package/dist/evaluation/hooks/observabilityHooks.js +5 -3
  24. package/dist/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  25. package/dist/evaluation/pipeline/evaluationPipeline.js +20 -8
  26. package/dist/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  27. package/dist/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  28. package/dist/lib/auth/anthropicOAuth.d.ts +18 -3
  29. package/dist/lib/auth/anthropicOAuth.js +137 -4
  30. package/dist/lib/auth/providers/firebase.js +5 -1
  31. package/dist/lib/auth/providers/jwt.js +5 -1
  32. package/dist/lib/auth/providers/workos.js +5 -1
  33. package/dist/lib/auth/sessionManager.d.ts +1 -1
  34. package/dist/lib/auth/sessionManager.js +58 -27
  35. package/dist/lib/client/aiSdkAdapter.js +3 -0
  36. package/dist/lib/client/streamingClient.js +30 -10
  37. package/dist/lib/core/modules/GenerationHandler.js +3 -2
  38. package/dist/lib/core/redisConversationMemoryManager.js +7 -3
  39. package/dist/lib/evaluation/BatchEvaluator.js +4 -1
  40. package/dist/lib/evaluation/hooks/observabilityHooks.js +5 -3
  41. package/dist/lib/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  42. package/dist/lib/evaluation/pipeline/evaluationPipeline.js +20 -8
  43. package/dist/lib/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  44. package/dist/lib/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  45. package/dist/lib/neurolink.d.ts +3 -2
  46. package/dist/lib/neurolink.js +260 -494
  47. package/dist/lib/observability/otelBridge.d.ts +2 -2
  48. package/dist/lib/observability/otelBridge.js +12 -3
  49. package/dist/lib/providers/amazonBedrock.js +2 -4
  50. package/dist/lib/providers/anthropic.d.ts +9 -5
  51. package/dist/lib/providers/anthropic.js +19 -14
  52. package/dist/lib/providers/anthropicBaseProvider.d.ts +3 -3
  53. package/dist/lib/providers/anthropicBaseProvider.js +5 -4
  54. package/dist/lib/providers/azureOpenai.d.ts +1 -1
  55. package/dist/lib/providers/azureOpenai.js +5 -4
  56. package/dist/lib/providers/googleAiStudio.js +30 -1
  57. package/dist/lib/providers/googleVertex.js +28 -6
  58. package/dist/lib/providers/huggingFace.d.ts +3 -3
  59. package/dist/lib/providers/huggingFace.js +6 -8
  60. package/dist/lib/providers/litellm.js +41 -29
  61. package/dist/lib/providers/mistral.js +2 -1
  62. package/dist/lib/providers/ollama.js +80 -23
  63. package/dist/lib/providers/openAI.js +3 -2
  64. package/dist/lib/providers/openRouter.js +2 -1
  65. package/dist/lib/providers/openaiCompatible.d.ts +4 -4
  66. package/dist/lib/providers/openaiCompatible.js +4 -4
  67. package/dist/lib/proxy/claudeFormat.d.ts +3 -2
  68. package/dist/lib/proxy/claudeFormat.js +25 -20
  69. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  70. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  71. package/dist/lib/proxy/modelRouter.js +3 -0
  72. package/dist/lib/proxy/oauthFetch.d.ts +1 -1
  73. package/dist/lib/proxy/oauthFetch.js +65 -72
  74. package/dist/lib/proxy/proxyConfig.js +44 -24
  75. package/dist/lib/proxy/proxyEnv.d.ts +19 -0
  76. package/dist/lib/proxy/proxyEnv.js +73 -0
  77. package/dist/lib/proxy/proxyFetch.js +50 -4
  78. package/dist/lib/proxy/proxyTracer.d.ts +133 -0
  79. package/dist/lib/proxy/proxyTracer.js +645 -0
  80. package/dist/lib/proxy/rawStreamCapture.d.ts +10 -0
  81. package/dist/lib/proxy/rawStreamCapture.js +83 -0
  82. package/dist/lib/proxy/requestLogger.d.ts +32 -5
  83. package/dist/lib/proxy/requestLogger.js +406 -37
  84. package/dist/lib/proxy/sseInterceptor.d.ts +97 -0
  85. package/dist/lib/proxy/sseInterceptor.js +402 -0
  86. package/dist/lib/proxy/usageStats.d.ts +4 -3
  87. package/dist/lib/proxy/usageStats.js +25 -12
  88. package/dist/lib/rag/chunkers/MarkdownChunker.js +13 -5
  89. package/dist/lib/rag/chunking/markdownChunker.js +15 -6
  90. package/dist/lib/server/routes/claudeProxyRoutes.d.ts +7 -2
  91. package/dist/lib/server/routes/claudeProxyRoutes.js +1737 -508
  92. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +7 -1
  93. package/dist/lib/services/server/ai/observability/instrumentation.js +240 -40
  94. package/dist/lib/tasks/backends/bullmqBackend.d.ts +1 -0
  95. package/dist/lib/tasks/backends/bullmqBackend.js +14 -7
  96. package/dist/lib/tasks/store/redisTaskStore.d.ts +1 -0
  97. package/dist/lib/tasks/store/redisTaskStore.js +34 -26
  98. package/dist/lib/tasks/taskManager.d.ts +3 -0
  99. package/dist/lib/tasks/taskManager.js +63 -30
  100. package/dist/lib/telemetry/index.d.ts +2 -1
  101. package/dist/lib/telemetry/index.js +2 -1
  102. package/dist/lib/telemetry/telemetryService.d.ts +3 -0
  103. package/dist/lib/telemetry/telemetryService.js +65 -5
  104. package/dist/lib/types/cli.d.ts +10 -0
  105. package/dist/lib/types/proxyTypes.d.ts +37 -5
  106. package/dist/lib/types/streamTypes.d.ts +25 -3
  107. package/dist/lib/utils/messageBuilder.js +3 -2
  108. package/dist/lib/utils/providerHealth.d.ts +18 -0
  109. package/dist/lib/utils/providerHealth.js +240 -9
  110. package/dist/lib/utils/providerUtils.js +14 -8
  111. package/dist/lib/utils/toolChoice.d.ts +4 -0
  112. package/dist/lib/utils/toolChoice.js +7 -0
  113. package/dist/neurolink.d.ts +3 -2
  114. package/dist/neurolink.js +260 -494
  115. package/dist/observability/otelBridge.d.ts +2 -2
  116. package/dist/observability/otelBridge.js +12 -3
  117. package/dist/providers/amazonBedrock.js +2 -4
  118. package/dist/providers/anthropic.d.ts +9 -5
  119. package/dist/providers/anthropic.js +19 -14
  120. package/dist/providers/anthropicBaseProvider.d.ts +3 -3
  121. package/dist/providers/anthropicBaseProvider.js +5 -4
  122. package/dist/providers/azureOpenai.d.ts +1 -1
  123. package/dist/providers/azureOpenai.js +5 -4
  124. package/dist/providers/googleAiStudio.js +30 -1
  125. package/dist/providers/googleVertex.js +28 -6
  126. package/dist/providers/huggingFace.d.ts +3 -3
  127. package/dist/providers/huggingFace.js +6 -7
  128. package/dist/providers/litellm.js +41 -29
  129. package/dist/providers/mistral.js +2 -1
  130. package/dist/providers/ollama.js +80 -23
  131. package/dist/providers/openAI.js +3 -2
  132. package/dist/providers/openRouter.js +2 -1
  133. package/dist/providers/openaiCompatible.d.ts +4 -4
  134. package/dist/providers/openaiCompatible.js +4 -3
  135. package/dist/proxy/claudeFormat.d.ts +3 -2
  136. package/dist/proxy/claudeFormat.js +25 -20
  137. package/dist/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  138. package/dist/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  139. package/dist/proxy/modelRouter.js +3 -0
  140. package/dist/proxy/oauthFetch.d.ts +1 -1
  141. package/dist/proxy/oauthFetch.js +65 -72
  142. package/dist/proxy/proxyConfig.js +44 -24
  143. package/dist/proxy/proxyEnv.d.ts +19 -0
  144. package/dist/proxy/proxyEnv.js +72 -0
  145. package/dist/proxy/proxyFetch.js +50 -4
  146. package/dist/proxy/proxyTracer.d.ts +133 -0
  147. package/dist/proxy/proxyTracer.js +644 -0
  148. package/dist/proxy/rawStreamCapture.d.ts +10 -0
  149. package/dist/proxy/rawStreamCapture.js +82 -0
  150. package/dist/proxy/requestLogger.d.ts +32 -5
  151. package/dist/proxy/requestLogger.js +406 -37
  152. package/dist/proxy/sseInterceptor.d.ts +97 -0
  153. package/dist/proxy/sseInterceptor.js +401 -0
  154. package/dist/proxy/usageStats.d.ts +4 -3
  155. package/dist/proxy/usageStats.js +25 -12
  156. package/dist/rag/chunkers/MarkdownChunker.js +13 -5
  157. package/dist/rag/chunking/markdownChunker.js +15 -6
  158. package/dist/server/routes/claudeProxyRoutes.d.ts +7 -2
  159. package/dist/server/routes/claudeProxyRoutes.js +1737 -508
  160. package/dist/services/server/ai/observability/instrumentation.d.ts +7 -1
  161. package/dist/services/server/ai/observability/instrumentation.js +240 -40
  162. package/dist/tasks/backends/bullmqBackend.d.ts +1 -0
  163. package/dist/tasks/backends/bullmqBackend.js +14 -7
  164. package/dist/tasks/store/redisTaskStore.d.ts +1 -0
  165. package/dist/tasks/store/redisTaskStore.js +34 -26
  166. package/dist/tasks/taskManager.d.ts +3 -0
  167. package/dist/tasks/taskManager.js +63 -30
  168. package/dist/telemetry/index.d.ts +2 -1
  169. package/dist/telemetry/index.js +2 -1
  170. package/dist/telemetry/telemetryService.d.ts +3 -0
  171. package/dist/telemetry/telemetryService.js +65 -5
  172. package/dist/types/cli.d.ts +10 -0
  173. package/dist/types/proxyTypes.d.ts +37 -5
  174. package/dist/types/streamTypes.d.ts +25 -3
  175. package/dist/utils/messageBuilder.js +3 -2
  176. package/dist/utils/providerHealth.d.ts +18 -0
  177. package/dist/utils/providerHealth.js +240 -9
  178. package/dist/utils/providerUtils.js +14 -8
  179. package/dist/utils/toolChoice.d.ts +4 -0
  180. package/dist/utils/toolChoice.js +6 -0
  181. package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +6609 -0
  182. package/docs/changelog.md +252 -0
  183. package/package.json +17 -1
  184. package/scripts/observability/check-proxy-telemetry.mjs +235 -0
  185. package/scripts/observability/docker-compose.proxy-observability.yaml +55 -0
  186. package/scripts/observability/import-openobserve-dashboard.mjs +240 -0
  187. package/scripts/observability/manage-local-openobserve.sh +184 -0
  188. package/scripts/observability/otel-collector.proxy-observability.yaml +78 -0
  189. package/scripts/observability/proxy-observability.env.example +23 -0
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import process from "node:process";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const defaultDashboardPath = path.resolve(
10
+ __dirname,
11
+ "../../docs/assets/dashboards/neurolink-proxy-observability-dashboard.json",
12
+ );
13
+
14
+ function usage() {
15
+ console.error(`Usage:
16
+ node scripts/observability/import-openobserve-dashboard.mjs [options]
17
+
18
+ Options:
19
+ --dashboard <path> Dashboard JSON to import
20
+ --openobserve-url <url> OpenObserve base URL
21
+ --org <org> OpenObserve org identifier
22
+ --user <email> OpenObserve login email
23
+ --password <password> OpenObserve login password
24
+ --replace-by-title Delete older dashboards with the same title after import
25
+ --dry-run Validate config and list matching dashboards without importing
26
+ --help Show this message
27
+ `);
28
+ }
29
+
30
+ function parseArgs(argv) {
31
+ const options = {
32
+ dashboard: defaultDashboardPath,
33
+ openobserveUrl: process.env.NEUROLINK_OPENOBSERVE_URL ?? "http://localhost:5080",
34
+ org: process.env.NEUROLINK_OPENOBSERVE_ORG ?? "default",
35
+ user: process.env.NEUROLINK_OPENOBSERVE_USER ?? "root@example.com",
36
+ password:
37
+ process.env.NEUROLINK_OPENOBSERVE_PASSWORD ?? "Complexpass#123",
38
+ replaceByTitle: false,
39
+ dryRun: false,
40
+ };
41
+
42
+ for (let index = 0; index < argv.length; index += 1) {
43
+ const arg = argv[index];
44
+
45
+ switch (arg) {
46
+ case "--dashboard":
47
+ options.dashboard = argv[index + 1];
48
+ index += 1;
49
+ break;
50
+ case "--openobserve-url":
51
+ options.openobserveUrl = argv[index + 1];
52
+ index += 1;
53
+ break;
54
+ case "--org":
55
+ options.org = argv[index + 1];
56
+ index += 1;
57
+ break;
58
+ case "--user":
59
+ options.user = argv[index + 1];
60
+ index += 1;
61
+ break;
62
+ case "--password":
63
+ options.password = argv[index + 1];
64
+ index += 1;
65
+ break;
66
+ case "--replace-by-title":
67
+ options.replaceByTitle = true;
68
+ break;
69
+ case "--dry-run":
70
+ options.dryRun = true;
71
+ break;
72
+ case "--help":
73
+ usage();
74
+ process.exit(0);
75
+ break; // satisfy noFallthroughSwitchClause
76
+ default:
77
+ throw new Error(`Unknown argument: ${arg}`);
78
+ }
79
+ }
80
+
81
+ return options;
82
+ }
83
+
84
+ async function readDashboardTemplate(filePath) {
85
+ const raw = JSON.parse(await fs.readFile(filePath, "utf8"));
86
+ const template = structuredClone(raw.v5 ?? raw);
87
+
88
+ delete template.dashboardId;
89
+ delete template.owner;
90
+ delete template.created;
91
+
92
+ if (!template.title) {
93
+ throw new Error(`Dashboard template ${filePath} is missing v5.title`);
94
+ }
95
+
96
+ return template;
97
+ }
98
+
99
+ function normalizeDashboardList(payload) {
100
+ if (Array.isArray(payload)) {
101
+ return payload;
102
+ }
103
+
104
+ if (Array.isArray(payload?.dashboards)) {
105
+ return payload.dashboards;
106
+ }
107
+
108
+ if (Array.isArray(payload?.data)) {
109
+ return payload.data;
110
+ }
111
+
112
+ return [];
113
+ }
114
+
115
+ function dashboardIdOf(item) {
116
+ return item?.dashboardId ?? item?.v5?.dashboardId ?? null;
117
+ }
118
+
119
+ function dashboardTitleOf(item) {
120
+ return item?.title ?? item?.v5?.title ?? null;
121
+ }
122
+
123
+ async function requestJson(url, { user, password, ...init }) {
124
+ const response = await fetch(url, {
125
+ ...init,
126
+ headers: {
127
+ Authorization: `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`,
128
+ "Content-Type": "application/json",
129
+ ...(init.headers ?? {}),
130
+ },
131
+ });
132
+
133
+ const text = await response.text();
134
+ let payload = null;
135
+
136
+ if (text) {
137
+ try {
138
+ payload = JSON.parse(text);
139
+ } catch {
140
+ payload = text;
141
+ }
142
+ }
143
+
144
+ if (!response.ok) {
145
+ const detail =
146
+ typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
147
+ throw new Error(`${response.status} ${response.statusText}: ${detail}`);
148
+ }
149
+
150
+ return payload;
151
+ }
152
+
153
+ async function main() {
154
+ const options = parseArgs(process.argv.slice(2));
155
+ const dashboard = await readDashboardTemplate(
156
+ path.resolve(process.cwd(), options.dashboard),
157
+ );
158
+ const baseUrl = options.openobserveUrl.replace(/\/+$/, "");
159
+ const dashboardsUrl = `${baseUrl}/api/${options.org}/dashboards`;
160
+
161
+ const existingPayload = await requestJson(dashboardsUrl, {
162
+ method: "GET",
163
+ user: options.user,
164
+ password: options.password,
165
+ });
166
+ const existingDashboards = normalizeDashboardList(existingPayload).filter(
167
+ (item) => dashboardTitleOf(item) === dashboard.title,
168
+ );
169
+
170
+ if (options.dryRun) {
171
+ console.log(
172
+ JSON.stringify(
173
+ {
174
+ dashboardPath: path.resolve(process.cwd(), options.dashboard),
175
+ title: dashboard.title,
176
+ matchingDashboardIds: existingDashboards
177
+ .map((item) => dashboardIdOf(item))
178
+ .filter(Boolean),
179
+ openobserveUrl: baseUrl,
180
+ org: options.org,
181
+ },
182
+ null,
183
+ 2,
184
+ ),
185
+ );
186
+ return;
187
+ }
188
+
189
+ const createdPayload = await requestJson(dashboardsUrl, {
190
+ method: "POST",
191
+ body: JSON.stringify(dashboard),
192
+ user: options.user,
193
+ password: options.password,
194
+ });
195
+
196
+ const createdDashboardId =
197
+ createdPayload?.dashboardId ??
198
+ createdPayload?.v5?.dashboardId ??
199
+ createdPayload?.data?.dashboardId;
200
+
201
+ if (!createdDashboardId) {
202
+ throw new Error("OpenObserve did not return a dashboardId for the new dashboard");
203
+ }
204
+
205
+ const deletedDashboardIds = [];
206
+
207
+ if (options.replaceByTitle) {
208
+ for (const entry of existingDashboards) {
209
+ const duplicateId = dashboardIdOf(entry);
210
+ if (!duplicateId || duplicateId === createdDashboardId) {
211
+ continue;
212
+ }
213
+
214
+ await requestJson(`${dashboardsUrl}/${duplicateId}`, {
215
+ method: "DELETE",
216
+ user: options.user,
217
+ password: options.password,
218
+ });
219
+ deletedDashboardIds.push(duplicateId);
220
+ }
221
+ }
222
+
223
+ console.log(
224
+ JSON.stringify(
225
+ {
226
+ title: dashboard.title,
227
+ dashboardId: createdDashboardId,
228
+ deletedDashboardIds,
229
+ openobserveUrl: `${baseUrl}/web/dashboards?org_identifier=${options.org}`,
230
+ },
231
+ null,
232
+ 2,
233
+ ),
234
+ );
235
+ }
236
+
237
+ main().catch((error) => {
238
+ console.error(error instanceof Error ? error.message : String(error));
239
+ process.exit(1);
240
+ });
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
6
+ COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.proxy-observability.yaml"
7
+ ENV_FILE="${SCRIPT_DIR}/proxy-observability.env"
8
+ IMPORT_SCRIPT="${SCRIPT_DIR}/import-openobserve-dashboard.mjs"
9
+ DOCTOR_SCRIPT="${SCRIPT_DIR}/check-proxy-telemetry.mjs"
10
+
11
+ load_env() {
12
+ if [[ -f "${ENV_FILE}" ]]; then
13
+ set -a
14
+ # shellcheck source=/dev/null
15
+ source "${ENV_FILE}"
16
+ set +a
17
+ fi
18
+ }
19
+
20
+ select_compose() {
21
+ if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
22
+ COMPOSE_CMD=(docker compose)
23
+ return
24
+ fi
25
+
26
+ if command -v docker-compose >/dev/null 2>&1; then
27
+ COMPOSE_CMD=(docker-compose)
28
+ return
29
+ fi
30
+
31
+ if command -v podman-compose >/dev/null 2>&1; then
32
+ COMPOSE_CMD=(podman-compose)
33
+ return
34
+ fi
35
+
36
+ echo "No supported compose command found. Install docker compose, docker-compose, or podman-compose." >&2
37
+ exit 1
38
+ }
39
+
40
+ compose() {
41
+ "${COMPOSE_CMD[@]}" -p "${NEUROLINK_OBSERVABILITY_PROJECT_NAME}" -f "${COMPOSE_FILE}" "$@"
42
+ }
43
+
44
+ wait_for_http() {
45
+ local name="$1"
46
+ local url="$2"
47
+ local attempts="${3:-60}"
48
+
49
+ for ((attempt = 1; attempt <= attempts; attempt += 1)); do
50
+ if curl -fsS "${url}" >/dev/null 2>&1; then
51
+ return 0
52
+ fi
53
+ sleep 1
54
+ done
55
+
56
+ echo "${name} did not become healthy at ${url}" >&2
57
+ return 1
58
+ }
59
+
60
+ print_status() {
61
+ local openobserve_status="DOWN"
62
+ local collector_status="DOWN"
63
+
64
+ if curl -fsS "${NEUROLINK_OPENOBSERVE_URL}/healthz" >/dev/null 2>&1; then
65
+ openobserve_status="UP"
66
+ fi
67
+
68
+ if curl -fsS "http://localhost:${NEUROLINK_OTEL_HEALTH_PORT}/" >/dev/null 2>&1; then
69
+ collector_status="UP"
70
+ fi
71
+
72
+ cat <<EOF
73
+ OpenObserve: ${openobserve_status}
74
+ UI: ${NEUROLINK_OPENOBSERVE_URL}
75
+ Login: ${NEUROLINK_OPENOBSERVE_USER}
76
+
77
+ OTEL Collector: ${collector_status}
78
+ OTLP gRPC: http://localhost:${NEUROLINK_OTLP_GRPC_PORT}
79
+ OTLP HTTP: http://localhost:${NEUROLINK_OTLP_HTTP_PORT}
80
+ Health: http://localhost:${NEUROLINK_OTEL_HEALTH_PORT}
81
+ EOF
82
+ }
83
+
84
+ print_usage() {
85
+ cat <<EOF
86
+ Usage: bash scripts/observability/manage-local-openobserve.sh <command>
87
+
88
+ Commands:
89
+ setup Start OpenObserve + OTEL collector and import the dashboard
90
+ up Start the local stack
91
+ start Alias for up
92
+ down Stop the local stack
93
+ stop Alias for down
94
+ logs Follow stack logs
95
+ status Show stack health and URLs
96
+ doctor Validate proxy telemetry freshness across logs, traces, and metrics
97
+ import-dashboard Import the NeuroLink proxy dashboard and dedupe by title
98
+ EOF
99
+ }
100
+
101
+ load_env
102
+
103
+ : "${NEUROLINK_OPENOBSERVE_USER:=root@example.com}"
104
+ : "${NEUROLINK_OPENOBSERVE_PASSWORD:=Complexpass#123}"
105
+ : "${NEUROLINK_OPENOBSERVE_ORG:=default}"
106
+ : "${NEUROLINK_OPENOBSERVE_HTTP_PORT:=5080}"
107
+ : "${NEUROLINK_OPENOBSERVE_GRPC_PORT:=5081}"
108
+ : "${NEUROLINK_OTLP_GRPC_PORT:=14317}"
109
+ : "${NEUROLINK_OTLP_HTTP_PORT:=14318}"
110
+ : "${NEUROLINK_OTEL_HEALTH_PORT:=14333}"
111
+ : "${NEUROLINK_OTEL_METRICS_PORT:=18888}"
112
+ : "${NEUROLINK_OTEL_PPROF_PORT:=11777}"
113
+ : "${NEUROLINK_OBSERVABILITY_PROJECT_NAME:=neurolink-proxy-observability}"
114
+ : "${NEUROLINK_PROXY_STREAM_HEADER:=neurolink-proxy}"
115
+ : "${NEUROLINK_OPENOBSERVE_URL:=http://localhost:${NEUROLINK_OPENOBSERVE_HTTP_PORT}}"
116
+ : "${NEUROLINK_OPENOBSERVE_OTLP_ENDPOINT:=http://openobserve:5080/api/${NEUROLINK_OPENOBSERVE_ORG}}"
117
+
118
+ if [[ -z "${NEUROLINK_OPENOBSERVE_BASIC_AUTH:-}" ]]; then
119
+ encoded_auth="$(printf '%s' "${NEUROLINK_OPENOBSERVE_USER}:${NEUROLINK_OPENOBSERVE_PASSWORD}" | base64 | tr -d '\n')"
120
+ export NEUROLINK_OPENOBSERVE_BASIC_AUTH="Basic ${encoded_auth}"
121
+ fi
122
+
123
+ export NEUROLINK_OPENOBSERVE_USER
124
+ export NEUROLINK_OPENOBSERVE_PASSWORD
125
+ export NEUROLINK_OPENOBSERVE_ORG
126
+ export NEUROLINK_OPENOBSERVE_HTTP_PORT
127
+ export NEUROLINK_OPENOBSERVE_GRPC_PORT
128
+ export NEUROLINK_OTLP_GRPC_PORT
129
+ export NEUROLINK_OTLP_HTTP_PORT
130
+ export NEUROLINK_OTEL_HEALTH_PORT
131
+ export NEUROLINK_OTEL_METRICS_PORT
132
+ export NEUROLINK_OTEL_PPROF_PORT
133
+ export NEUROLINK_OBSERVABILITY_PROJECT_NAME
134
+ export NEUROLINK_PROXY_STREAM_HEADER
135
+ export NEUROLINK_OPENOBSERVE_URL
136
+ export NEUROLINK_OPENOBSERVE_OTLP_ENDPOINT
137
+
138
+ select_compose
139
+
140
+ command="${1:-setup}"
141
+
142
+ case "${command}" in
143
+ setup)
144
+ compose up -d
145
+ wait_for_http "OpenObserve" "${NEUROLINK_OPENOBSERVE_URL}/healthz"
146
+ wait_for_http "OTEL collector" "http://localhost:${NEUROLINK_OTEL_HEALTH_PORT}/"
147
+ node "${IMPORT_SCRIPT}" --replace-by-title
148
+ cat <<EOF
149
+ Local proxy observability is ready.
150
+
151
+ OpenObserve UI: ${NEUROLINK_OPENOBSERVE_URL}
152
+ OpenObserve login: ${NEUROLINK_OPENOBSERVE_USER} / ${NEUROLINK_OPENOBSERVE_PASSWORD}
153
+ OTLP HTTP endpoint: http://localhost:${NEUROLINK_OTLP_HTTP_PORT}
154
+
155
+ Set this before starting the proxy:
156
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:${NEUROLINK_OTLP_HTTP_PORT}
157
+ EOF
158
+ ;;
159
+ up|start)
160
+ compose up -d
161
+ ;;
162
+ down|stop)
163
+ compose down
164
+ ;;
165
+ logs)
166
+ compose logs -f
167
+ ;;
168
+ status)
169
+ print_status
170
+ ;;
171
+ doctor)
172
+ node "${DOCTOR_SCRIPT}"
173
+ ;;
174
+ import-dashboard)
175
+ node "${IMPORT_SCRIPT}" --replace-by-title
176
+ ;;
177
+ help|-h|--help)
178
+ print_usage
179
+ ;;
180
+ *)
181
+ print_usage >&2
182
+ exit 1
183
+ ;;
184
+ esac
@@ -0,0 +1,78 @@
1
+ # OpenTelemetry Collector configuration dedicated to the NeuroLink proxy.
2
+ #
3
+ # Active OpenObserve streams created by this setup:
4
+ # - Logs: neurolink_proxy
5
+ # - Traces: neurolink_proxy
6
+ # - Metrics: proxy_* (metric name becomes the stream)
7
+
8
+ receivers:
9
+ otlp:
10
+ protocols:
11
+ grpc:
12
+ endpoint: 0.0.0.0:4317
13
+ http:
14
+ endpoint: 0.0.0.0:4318
15
+
16
+ processors:
17
+ memory_limiter:
18
+ check_interval: 1s
19
+ limit_mib: 512
20
+ spike_limit_mib: 128
21
+
22
+ batch:
23
+ timeout: 1s
24
+ send_batch_size: 1024
25
+ send_batch_max_size: 2048
26
+
27
+ filter/healthcheck:
28
+ error_mode: ignore
29
+ traces:
30
+ span:
31
+ - 'IsMatch(name, ".*health.*") or IsMatch(name, ".*healthz.*") or IsMatch(name, ".*ready.*")'
32
+
33
+ exporters:
34
+ otlphttp/openobserve:
35
+ endpoint: ${env:NEUROLINK_OPENOBSERVE_OTLP_ENDPOINT}
36
+ headers:
37
+ Authorization: ${env:NEUROLINK_OPENOBSERVE_BASIC_AUTH}
38
+ stream-name: ${env:NEUROLINK_PROXY_STREAM_HEADER}
39
+ tls:
40
+ insecure: true
41
+
42
+ extensions:
43
+ health_check:
44
+ endpoint: 0.0.0.0:13133
45
+
46
+ pprof:
47
+ endpoint: 0.0.0.0:1777
48
+
49
+ service:
50
+ extensions: [health_check, pprof]
51
+
52
+ pipelines:
53
+ traces:
54
+ receivers: [otlp]
55
+ processors: [memory_limiter, filter/healthcheck, batch]
56
+ exporters: [otlphttp/openobserve]
57
+
58
+ metrics:
59
+ receivers: [otlp]
60
+ processors: [memory_limiter, batch]
61
+ exporters: [otlphttp/openobserve]
62
+
63
+ logs:
64
+ receivers: [otlp]
65
+ processors: [memory_limiter, batch]
66
+ exporters: [otlphttp/openobserve]
67
+
68
+ telemetry:
69
+ logs:
70
+ level: info
71
+ metrics:
72
+ level: detailed
73
+ readers:
74
+ - pull:
75
+ exporter:
76
+ prometheus:
77
+ host: "0.0.0.0"
78
+ port: 8888
@@ -0,0 +1,23 @@
1
+ # Copy this file to scripts/observability/proxy-observability.env only if the
2
+ # defaults collide with your machine or you want different credentials.
3
+
4
+ NEUROLINK_OPENOBSERVE_USER=root@example.com
5
+ NEUROLINK_OPENOBSERVE_PASSWORD=Complexpass#123
6
+ NEUROLINK_OPENOBSERVE_ORG=default
7
+
8
+ # Host ports exposed by the local stack.
9
+ NEUROLINK_OPENOBSERVE_HTTP_PORT=5080
10
+ NEUROLINK_OPENOBSERVE_GRPC_PORT=5081
11
+ NEUROLINK_OTLP_GRPC_PORT=14317
12
+ NEUROLINK_OTLP_HTTP_PORT=14318
13
+ NEUROLINK_OTEL_HEALTH_PORT=14333
14
+ NEUROLINK_OTEL_METRICS_PORT=18888
15
+ NEUROLINK_OTEL_PPROF_PORT=11777
16
+
17
+ # Optional names if you want multiple local stacks in parallel.
18
+ # NEUROLINK_OBSERVABILITY_PROJECT_NAME=neurolink-proxy-observability
19
+ # NEUROLINK_OPENOBSERVE_VOLUME_NAME=neurolink-openobserve-data
20
+
21
+ # The collector exports proxy telemetry into this OpenObserve stream header.
22
+ # OpenObserve normalizes the logs and traces stream name to "neurolink_proxy".
23
+ # NEUROLINK_PROXY_STREAM_HEADER=neurolink-proxy