@intelli-1113/stratos-sdk 1.2.2 → 1.3.1

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.
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * stratos-inspect — check LangChain version compatibility for Stratos metrics.
4
+ *
5
+ * npx stratos-inspect
6
+ * npx stratos-inspect --json
7
+ * npx stratos-inspect --fix
8
+ * npx stratos-inspect --fix --yes
9
+ * npx stratos-inspect --fix --dry-run
10
+ */
11
+
12
+ import {
13
+ inspectLangChain,
14
+ fixLangChain,
15
+ printInspectReport,
16
+ } from "./inspect-langchain.js";
17
+
18
+ function parseArgs(argv) {
19
+ const flags = new Set(argv);
20
+ return {
21
+ json: flags.has("--json"),
22
+ fix: flags.has("--fix") || flags.has("--repair"),
23
+ yes: flags.has("--yes") || flags.has("-y"),
24
+ dryRun: flags.has("--dry-run"),
25
+ help: flags.has("--help") || flags.has("-h"),
26
+ };
27
+ }
28
+
29
+ function printHelp() {
30
+ console.log(`
31
+ stratos-inspect — LangChain compatibility for @intelli-1113/stratos-sdk
32
+
33
+ Usage:
34
+ stratos-inspect Inspect current project (cwd)
35
+ stratos-inspect --json Machine-readable report
36
+ stratos-inspect --fix Uninstall old LangChain (<1.0) and install 1.x
37
+ stratos-inspect --fix --yes Same, no confirmation prompt
38
+ stratos-inspect --fix --dry-run Show plan only
39
+
40
+ Why:
41
+ Stratos LangChain metrics need @langchain/core >=1.0.0 <2.0.0
42
+ (OpenLLMetry / @traceloop/instrumentation-langchain peer range).
43
+
44
+ Exit codes:
45
+ 0 compatible (or no LangChain) / fix succeeded
46
+ 1 incompatible or fix failed / cancelled
47
+ `);
48
+ }
49
+
50
+ async function main() {
51
+ const opts = parseArgs(process.argv.slice(2));
52
+ if (opts.help) {
53
+ printHelp();
54
+ process.exit(0);
55
+ }
56
+
57
+ const report = inspectLangChain();
58
+
59
+ if (!opts.fix) {
60
+ printInspectReport(report, { json: opts.json });
61
+ process.exit(report.compatible ? 0 : 1);
62
+ }
63
+
64
+ if (!opts.json) {
65
+ printInspectReport(report, { json: false });
66
+ }
67
+
68
+ const result = await fixLangChain({
69
+ yes: opts.yes,
70
+ dryRun: opts.dryRun,
71
+ });
72
+
73
+ if (opts.json) {
74
+ console.log(JSON.stringify(result, null, 2));
75
+ } else if (result.dryRun) {
76
+ console.log("[stratos-inspect] Dry run plan:");
77
+ console.log(JSON.stringify(result.plan, null, 2));
78
+ } else if (result.cancelled) {
79
+ console.log("[stratos-inspect] Cancelled — no changes made.");
80
+ } else if (result.skipped) {
81
+ console.log(`[stratos-inspect] Skipped (${result.reason}). ${result.message || ""}`);
82
+ } else if (result.ok) {
83
+ console.log("[stratos-inspect] Done. LangChain should now be on 1.x.");
84
+ if (result.reportAfter) printInspectReport(result.reportAfter);
85
+ } else {
86
+ console.error(`[stratos-inspect] Fix failed: ${result.message || result.step || "unknown"}`);
87
+ }
88
+
89
+ process.exit(result.ok || result.skipped ? 0 : 1);
90
+ }
91
+
92
+ main().catch((err) => {
93
+ console.error("[stratos-inspect]", err);
94
+ process.exit(1);
95
+ });
package/src/index.js CHANGED
@@ -1,38 +1,57 @@
1
1
  // @intelli-1113/stratos-sdk — initialize OpenLLMetry and stream telemetry to Stratos AI.
2
2
  //
3
- // Config comes from the environment (so the ingest URL / token can change
4
- // without editing code or re-publishing the SDK):
5
- // STRATOS_TOKEN enrollment token from Stratos "Add agent" (required)
6
- // STRATOS_URL Stratos origin, e.g. https://stratos.lnt.com (default http://localhost:4000)
7
- // STRATOS_APP_NAME display name for this agent (optional)
3
+ // Config (environment):
4
+ // STRATOS_TOKEN enrollment token from Stratos "Add agent" (required)
5
+ // STRATOS_URL Stratos origin (default http://localhost:4000)
6
+ // STRATOS_APP_NAME display name for this agent (optional)
7
+ // STRATOS_VERCEL_AI set 0 to disable auto Vercel AI patching (default on)
8
+ // STRATOS_FRAMEWORK override auto framework detection (optional)
8
9
  //
9
- // Why this exists: traceloop defaults to OTLP *protobuf* at <baseUrl>/v1/traces;
10
- // Stratos ingests OTLP *JSON* at /api/ingest. This wires a JSON exporter to the
11
- // right URL and runs a heartbeat so the agent shows online while it's up.
10
+ // Vercel AI: clients only need this register entry (or --import). No
11
+ // experimental_telemetry in their generateText/streamText calls.
12
12
  import * as traceloop from "@traceloop/node-server-sdk";
13
13
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
14
14
  import { createRequire } from "node:module";
15
15
  import { readFileSync } from "node:fs";
16
16
  import { join } from "node:path";
17
+ import { enableVercelAiTelemetry } from "./vercel-ai.js";
18
+ import { inspectLangChain } from "./inspect-langchain.js";
19
+
20
+ export { inspectLangChain, fixLangChain } from "./inspect-langchain.js";
17
21
 
18
22
  const _require = createRequire(import.meta.url);
19
- const resolvable = (pkg) => { try { _require.resolve(pkg); return true; } catch { return false; } };
23
+ const resolvable = (pkg) => {
24
+ try {
25
+ _require.resolve(pkg);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ };
31
+ const resolvableFromCwd = (pkg) => {
32
+ try {
33
+ createRequire(join(process.cwd(), "package.json")).resolve(pkg);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ };
20
39
 
21
- // The agent's DIRECT dependencies (most reliable signal of the framework in use).
22
- // Resolution alone is unreliable — some packages (e.g. @google/adk) block default
23
- // resolution via "exports", and others appear only transitively.
24
40
  function agentDeps() {
25
41
  try {
26
- const pkg = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf8"));
42
+ const pkg = JSON.parse(
43
+ readFileSync(join(process.cwd(), "package.json"), "utf8")
44
+ );
27
45
  return { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
28
- } catch { return {}; }
46
+ } catch {
47
+ return {};
48
+ }
29
49
  }
30
50
 
31
- // Detect the agent framework from declared deps (preferred) or resolution — no manual input.
32
51
  function detectFramework() {
52
+ if (process.env.STRATOS_FRAMEWORK) return process.env.STRATOS_FRAMEWORK;
33
53
  const deps = agentDeps();
34
- const has = (n) => n in deps || resolvable(n);
35
- // Higher-level agent frameworks FIRST (several pull in @langchain/* transitively).
54
+ const has = (n) => n in deps || resolvableFromCwd(n) || resolvable(n);
36
55
  if (has("@google/adk") || has("google-adk") || has("@iqai/adk")) return "google-adk";
37
56
  if (has("@openai/agents")) return "openai-agents";
38
57
  if (has("llamaindex")) return "llamaindex";
@@ -45,23 +64,44 @@ function detectFramework() {
45
64
  if (has("openai")) return "openai";
46
65
  return null;
47
66
  }
48
- // Best-effort model from common env vars (or opts.model). Spans still refine it.
67
+
49
68
  function detectModel(opts) {
50
- return opts.model || process.env.STRATOS_MODEL || process.env.NVIDIA_MODEL || process.env.OPENAI_MODEL
51
- || process.env.ANTHROPIC_MODEL || process.env.LLM_MODEL || process.env.MODEL || null;
69
+ return (
70
+ opts.model ||
71
+ process.env.STRATOS_MODEL ||
72
+ process.env.NVIDIA_MODEL ||
73
+ process.env.OPENAI_MODEL ||
74
+ process.env.ANTHROPIC_MODEL ||
75
+ process.env.GEMINI_MODEL ||
76
+ process.env.GOOGLE_MODEL ||
77
+ process.env.LLM_MODEL ||
78
+ process.env.MODEL ||
79
+ null
80
+ );
52
81
  }
53
82
 
54
83
  let started = false;
55
84
 
56
85
  export function start(opts = {}) {
57
- if (started) return; // idempotent — safe to import/register more than once
86
+ if (started) return;
58
87
  const token = opts.token || process.env.STRATOS_TOKEN || "";
59
- const origin = (opts.url || process.env.STRATOS_URL || "http://localhost:4000").replace(/\/+$/, "");
60
- const appName = opts.appName || process.env.STRATOS_APP_NAME || process.env.npm_package_name || "agent";
61
- const heartbeatMs = Number(opts.heartbeatMs || process.env.STRATOS_HEARTBEAT_MS || 30000);
88
+ const origin = (opts.url || process.env.STRATOS_URL || "http://localhost:4000").replace(
89
+ /\/+$/,
90
+ ""
91
+ );
92
+ const appName =
93
+ opts.appName ||
94
+ process.env.STRATOS_APP_NAME ||
95
+ process.env.npm_package_name ||
96
+ "agent";
97
+ const heartbeatMs = Number(
98
+ opts.heartbeatMs || process.env.STRATOS_HEARTBEAT_MS || 30000
99
+ );
62
100
 
63
101
  if (!token) {
64
- console.warn("[stratos] STRATOS_TOKEN not set — telemetry disabled. Add the token from Stratos > Add agent.");
102
+ console.warn(
103
+ "[stratos] STRATOS_TOKEN not set — telemetry disabled. Add the token from Stratos > Add agent."
104
+ );
65
105
  return;
66
106
  }
67
107
  started = true;
@@ -72,26 +112,74 @@ export function start(opts = {}) {
72
112
  traceloop.initialize({
73
113
  appName,
74
114
  disableBatch: true,
75
- exporter: new OTLPTraceExporter({ url: ingest, headers: { "x-stratos-token": token } }),
115
+ exporter: new OTLPTraceExporter({
116
+ url: ingest,
117
+ headers: { "x-stratos-token": token },
118
+ }),
76
119
  });
77
120
 
78
- // Auto-detected metadata sent on every heartbeat → Stratos auto-fills the
79
- // agent's framework/model/tools (no manual entry, no reliance on span quality).
80
- const toolsEnv = (opts.tools || (process.env.STRATOS_TOOLS || "").split(",")).map((s) => String(s).trim()).filter(Boolean);
81
- const meta = { framework: detectFramework(), model: detectModel(opts), tools: toolsEnv };
121
+ // LangChain version check (warn only — fix via: npx stratos-inspect --fix)
122
+ try {
123
+ const lc = inspectLangChain();
124
+ if (lc.hasLangChain && lc.needsAttention) {
125
+ console.warn(`[stratos] ${lc.message}`);
126
+ console.warn("[stratos] Fix: npx stratos-inspect --fix");
127
+ if (process.env.STRATOS_STRICT_LANGCHAIN === "1" && lc.needsUpgrade) {
128
+ throw new Error(
129
+ "Incompatible @langchain/core (<1.0). Upgrade with: npx stratos-inspect --fix --yes"
130
+ );
131
+ }
132
+ }
133
+ } catch (err) {
134
+ if (process.env.STRATOS_STRICT_LANGCHAIN === "1") throw err;
135
+ }
136
+
137
+ // Vercel AI v7 optional global register; v5/v6 covered by ESM loader in register.js
138
+ enableVercelAiTelemetry()
139
+ .then((r) => {
140
+ if (r.enabled) {
141
+ console.log(`[stratos] vercel-ai auto-telemetry: ${r.mode}`);
142
+ }
143
+ })
144
+ .catch(() => {});
145
+
146
+ const toolsEnv = (
147
+ opts.tools ||
148
+ (process.env.STRATOS_TOOLS || "").split(",")
149
+ )
150
+ .map((s) => String(s).trim())
151
+ .filter(Boolean);
152
+
153
+ const meta = {
154
+ framework: opts.framework || detectFramework(),
155
+ model: detectModel(opts),
156
+ tools: toolsEnv,
157
+ };
82
158
 
83
- // Keep the agent "online" while the process is alive (telemetry alone only
84
- // fires on LLM calls, which would let liveness lapse between requests).
85
159
  const ping = () =>
86
160
  fetch(heartbeatUrl, {
87
161
  method: "POST",
88
- headers: { "x-stratos-token": token, "Content-Type": "application/json" },
162
+ headers: {
163
+ "x-stratos-token": token,
164
+ "Content-Type": "application/json",
165
+ },
89
166
  body: JSON.stringify(meta),
90
167
  }).catch(() => {});
91
168
  ping();
92
169
  if (heartbeatMs > 0) setInterval(ping, heartbeatMs).unref();
93
170
 
94
- console.log(`[stratos] telemetry → ${ingest} (heartbeat ${heartbeatMs}ms, framework=${meta.framework || "?"})`);
171
+ process.on("beforeExit", () => {
172
+ traceloop.forceFlush().catch(() => {});
173
+ });
174
+ for (const sig of ["SIGINT", "SIGTERM"]) {
175
+ process.on(sig, () => {
176
+ traceloop.forceFlush().finally(() => process.exit(0));
177
+ });
178
+ }
179
+
180
+ console.log(
181
+ `[stratos] telemetry → ${ingest} (heartbeat ${heartbeatMs}ms, framework=${meta.framework || "?"})`
182
+ );
95
183
  }
96
184
 
97
185
  export default { start };