@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.
- package/CHANGELOG.md +6 -0
- package/README.md +7 -1
- package/dist/auth/anthropicOAuth.d.ts +18 -3
- package/dist/auth/anthropicOAuth.js +137 -4
- package/dist/auth/providers/firebase.js +5 -1
- package/dist/auth/providers/jwt.js +5 -1
- package/dist/auth/providers/workos.js +5 -1
- package/dist/auth/sessionManager.d.ts +1 -1
- package/dist/auth/sessionManager.js +58 -27
- package/dist/browser/neurolink.min.js +337 -318
- package/dist/cli/commands/mcp.js +3 -0
- package/dist/cli/commands/proxy.d.ts +2 -1
- package/dist/cli/commands/proxy.js +279 -16
- package/dist/cli/commands/task.js +3 -0
- package/dist/cli/factories/commandFactory.d.ts +2 -0
- package/dist/cli/factories/commandFactory.js +38 -0
- package/dist/cli/parser.js +4 -3
- package/dist/client/aiSdkAdapter.js +3 -0
- package/dist/client/streamingClient.js +30 -10
- package/dist/core/modules/GenerationHandler.js +3 -2
- package/dist/core/redisConversationMemoryManager.js +7 -3
- package/dist/evaluation/BatchEvaluator.js +4 -1
- package/dist/evaluation/hooks/observabilityHooks.js +5 -3
- package/dist/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
- package/dist/evaluation/pipeline/evaluationPipeline.js +20 -8
- package/dist/evaluation/pipeline/strategies/batchStrategy.js +6 -3
- package/dist/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
- package/dist/lib/auth/anthropicOAuth.d.ts +18 -3
- package/dist/lib/auth/anthropicOAuth.js +137 -4
- package/dist/lib/auth/providers/firebase.js +5 -1
- package/dist/lib/auth/providers/jwt.js +5 -1
- package/dist/lib/auth/providers/workos.js +5 -1
- package/dist/lib/auth/sessionManager.d.ts +1 -1
- package/dist/lib/auth/sessionManager.js +58 -27
- package/dist/lib/client/aiSdkAdapter.js +3 -0
- package/dist/lib/client/streamingClient.js +30 -10
- package/dist/lib/core/modules/GenerationHandler.js +3 -2
- package/dist/lib/core/redisConversationMemoryManager.js +7 -3
- package/dist/lib/evaluation/BatchEvaluator.js +4 -1
- package/dist/lib/evaluation/hooks/observabilityHooks.js +5 -3
- package/dist/lib/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
- package/dist/lib/evaluation/pipeline/evaluationPipeline.js +20 -8
- package/dist/lib/evaluation/pipeline/strategies/batchStrategy.js +6 -3
- package/dist/lib/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
- package/dist/lib/neurolink.d.ts +3 -2
- package/dist/lib/neurolink.js +260 -494
- package/dist/lib/observability/otelBridge.d.ts +2 -2
- package/dist/lib/observability/otelBridge.js +12 -3
- package/dist/lib/providers/amazonBedrock.js +2 -4
- package/dist/lib/providers/anthropic.d.ts +9 -5
- package/dist/lib/providers/anthropic.js +19 -14
- package/dist/lib/providers/anthropicBaseProvider.d.ts +3 -3
- package/dist/lib/providers/anthropicBaseProvider.js +5 -4
- package/dist/lib/providers/azureOpenai.d.ts +1 -1
- package/dist/lib/providers/azureOpenai.js +5 -4
- package/dist/lib/providers/googleAiStudio.js +30 -1
- package/dist/lib/providers/googleVertex.js +28 -6
- package/dist/lib/providers/huggingFace.d.ts +3 -3
- package/dist/lib/providers/huggingFace.js +6 -8
- package/dist/lib/providers/litellm.js +41 -29
- package/dist/lib/providers/mistral.js +2 -1
- package/dist/lib/providers/ollama.js +80 -23
- package/dist/lib/providers/openAI.js +3 -2
- package/dist/lib/providers/openRouter.js +2 -1
- package/dist/lib/providers/openaiCompatible.d.ts +4 -4
- package/dist/lib/providers/openaiCompatible.js +4 -4
- package/dist/lib/proxy/claudeFormat.d.ts +3 -2
- package/dist/lib/proxy/claudeFormat.js +25 -20
- package/dist/lib/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
- package/dist/lib/proxy/cloaking/plugins/sessionIdentity.js +9 -33
- package/dist/lib/proxy/modelRouter.js +3 -0
- package/dist/lib/proxy/oauthFetch.d.ts +1 -1
- package/dist/lib/proxy/oauthFetch.js +65 -72
- package/dist/lib/proxy/proxyConfig.js +44 -24
- package/dist/lib/proxy/proxyEnv.d.ts +19 -0
- package/dist/lib/proxy/proxyEnv.js +73 -0
- package/dist/lib/proxy/proxyFetch.js +50 -4
- package/dist/lib/proxy/proxyTracer.d.ts +133 -0
- package/dist/lib/proxy/proxyTracer.js +645 -0
- package/dist/lib/proxy/rawStreamCapture.d.ts +10 -0
- package/dist/lib/proxy/rawStreamCapture.js +83 -0
- package/dist/lib/proxy/requestLogger.d.ts +32 -5
- package/dist/lib/proxy/requestLogger.js +406 -37
- package/dist/lib/proxy/sseInterceptor.d.ts +97 -0
- package/dist/lib/proxy/sseInterceptor.js +402 -0
- package/dist/lib/proxy/usageStats.d.ts +4 -3
- package/dist/lib/proxy/usageStats.js +25 -12
- package/dist/lib/rag/chunkers/MarkdownChunker.js +13 -5
- package/dist/lib/rag/chunking/markdownChunker.js +15 -6
- package/dist/lib/server/routes/claudeProxyRoutes.d.ts +7 -2
- package/dist/lib/server/routes/claudeProxyRoutes.js +1737 -508
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +7 -1
- package/dist/lib/services/server/ai/observability/instrumentation.js +240 -40
- package/dist/lib/tasks/backends/bullmqBackend.d.ts +1 -0
- package/dist/lib/tasks/backends/bullmqBackend.js +14 -7
- package/dist/lib/tasks/store/redisTaskStore.d.ts +1 -0
- package/dist/lib/tasks/store/redisTaskStore.js +34 -26
- package/dist/lib/tasks/taskManager.d.ts +3 -0
- package/dist/lib/tasks/taskManager.js +63 -30
- package/dist/lib/telemetry/index.d.ts +2 -1
- package/dist/lib/telemetry/index.js +2 -1
- package/dist/lib/telemetry/telemetryService.d.ts +3 -0
- package/dist/lib/telemetry/telemetryService.js +65 -5
- package/dist/lib/types/cli.d.ts +10 -0
- package/dist/lib/types/proxyTypes.d.ts +37 -5
- package/dist/lib/types/streamTypes.d.ts +25 -3
- package/dist/lib/utils/messageBuilder.js +3 -2
- package/dist/lib/utils/providerHealth.d.ts +18 -0
- package/dist/lib/utils/providerHealth.js +240 -9
- package/dist/lib/utils/providerUtils.js +14 -8
- package/dist/lib/utils/toolChoice.d.ts +4 -0
- package/dist/lib/utils/toolChoice.js +7 -0
- package/dist/neurolink.d.ts +3 -2
- package/dist/neurolink.js +260 -494
- package/dist/observability/otelBridge.d.ts +2 -2
- package/dist/observability/otelBridge.js +12 -3
- package/dist/providers/amazonBedrock.js +2 -4
- package/dist/providers/anthropic.d.ts +9 -5
- package/dist/providers/anthropic.js +19 -14
- package/dist/providers/anthropicBaseProvider.d.ts +3 -3
- package/dist/providers/anthropicBaseProvider.js +5 -4
- package/dist/providers/azureOpenai.d.ts +1 -1
- package/dist/providers/azureOpenai.js +5 -4
- package/dist/providers/googleAiStudio.js +30 -1
- package/dist/providers/googleVertex.js +28 -6
- package/dist/providers/huggingFace.d.ts +3 -3
- package/dist/providers/huggingFace.js +6 -7
- package/dist/providers/litellm.js +41 -29
- package/dist/providers/mistral.js +2 -1
- package/dist/providers/ollama.js +80 -23
- package/dist/providers/openAI.js +3 -2
- package/dist/providers/openRouter.js +2 -1
- package/dist/providers/openaiCompatible.d.ts +4 -4
- package/dist/providers/openaiCompatible.js +4 -3
- package/dist/proxy/claudeFormat.d.ts +3 -2
- package/dist/proxy/claudeFormat.js +25 -20
- package/dist/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
- package/dist/proxy/cloaking/plugins/sessionIdentity.js +9 -33
- package/dist/proxy/modelRouter.js +3 -0
- package/dist/proxy/oauthFetch.d.ts +1 -1
- package/dist/proxy/oauthFetch.js +65 -72
- package/dist/proxy/proxyConfig.js +44 -24
- package/dist/proxy/proxyEnv.d.ts +19 -0
- package/dist/proxy/proxyEnv.js +72 -0
- package/dist/proxy/proxyFetch.js +50 -4
- package/dist/proxy/proxyTracer.d.ts +133 -0
- package/dist/proxy/proxyTracer.js +644 -0
- package/dist/proxy/rawStreamCapture.d.ts +10 -0
- package/dist/proxy/rawStreamCapture.js +82 -0
- package/dist/proxy/requestLogger.d.ts +32 -5
- package/dist/proxy/requestLogger.js +406 -37
- package/dist/proxy/sseInterceptor.d.ts +97 -0
- package/dist/proxy/sseInterceptor.js +401 -0
- package/dist/proxy/usageStats.d.ts +4 -3
- package/dist/proxy/usageStats.js +25 -12
- package/dist/rag/chunkers/MarkdownChunker.js +13 -5
- package/dist/rag/chunking/markdownChunker.js +15 -6
- package/dist/server/routes/claudeProxyRoutes.d.ts +7 -2
- package/dist/server/routes/claudeProxyRoutes.js +1737 -508
- package/dist/services/server/ai/observability/instrumentation.d.ts +7 -1
- package/dist/services/server/ai/observability/instrumentation.js +240 -40
- package/dist/tasks/backends/bullmqBackend.d.ts +1 -0
- package/dist/tasks/backends/bullmqBackend.js +14 -7
- package/dist/tasks/store/redisTaskStore.d.ts +1 -0
- package/dist/tasks/store/redisTaskStore.js +34 -26
- package/dist/tasks/taskManager.d.ts +3 -0
- package/dist/tasks/taskManager.js +63 -30
- package/dist/telemetry/index.d.ts +2 -1
- package/dist/telemetry/index.js +2 -1
- package/dist/telemetry/telemetryService.d.ts +3 -0
- package/dist/telemetry/telemetryService.js +65 -5
- package/dist/types/cli.d.ts +10 -0
- package/dist/types/proxyTypes.d.ts +37 -5
- package/dist/types/streamTypes.d.ts +25 -3
- package/dist/utils/messageBuilder.js +3 -2
- package/dist/utils/providerHealth.d.ts +18 -0
- package/dist/utils/providerHealth.js +240 -9
- package/dist/utils/providerUtils.js +14 -8
- package/dist/utils/toolChoice.d.ts +4 -0
- package/dist/utils/toolChoice.js +6 -0
- package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +6609 -0
- package/docs/changelog.md +252 -0
- package/package.json +17 -1
- package/scripts/observability/check-proxy-telemetry.mjs +235 -0
- package/scripts/observability/docker-compose.proxy-observability.yaml +55 -0
- package/scripts/observability/import-openobserve-dashboard.mjs +240 -0
- package/scripts/observability/manage-local-openobserve.sh +184 -0
- package/scripts/observability/otel-collector.proxy-observability.yaml +78 -0
- 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
|