@intelli-1113/stratos-sdk 1.1.0 → 1.2.2

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 (3) hide show
  1. package/README.md +10 -1
  2. package/package.json +1 -1
  3. package/src/index.js +97 -49
package/README.md CHANGED
@@ -35,6 +35,12 @@ start({ token: "...", url: "https://stratos.lnt.com", appName: "my-agent" });
35
35
  ## Why it must load first
36
36
  OpenLLMetry patches `openai`/`langchain` to capture spans — it has to run **before** those modules are imported. The `--import` flag and the `/register` entry both guarantee that.
37
37
 
38
+ ## Auto-detected metadata (no manual entry)
39
+ On each heartbeat the SDK reports, and Stratos auto-fills:
40
+ - **framework** — detected from installed deps (langchain/langgraph, llamaindex, google-adk, vercel-ai, anthropic, openai…).
41
+ - **model** — from `STRATOS_MODEL` / `NVIDIA_MODEL` / `OPENAI_MODEL` / `ANTHROPIC_MODEL` / `LLM_MODEL` / `MODEL` (or `start({ model })`).
42
+ - **tools** — captured from spans automatically; you can also declare them via `STRATOS_TOOLS=a,b,c` or `start({ tools: [...] })`.
43
+
38
44
  ## Changing the URL or token
39
45
  Both come from env, read at startup — change the `.env` value and restart. No code edit, no SDK re-publish.
40
46
 
@@ -48,7 +54,10 @@ npm i -g @intelli-1113/stratos-sdk
48
54
  "weather": {
49
55
  "command": "stratos-mcp-proxy",
50
56
  "args": ["--","npx","-y","@scope/weather-mcp@latest"],
51
- "env": { "STRATOS_TOKEN":"<token>", "STRATOS_URL":"http://localhost:4000", "STRATOS_APP_NAME":"weather" }
57
+ "env": {
58
+ "STRATOS_TOKEN":"<token>",
59
+ "STRATOS_URL":"http://localhost:4000",
60
+ "STRATOS_APP_NAME":"weather" }
52
61
  }
53
62
  ```
54
63
  It forwards the JSON-RPC stream verbatim and reports each **tool call** (name, args, result, latency) + liveness.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intelli-1113/stratos-sdk",
3
- "version": "1.1.0",
3
+ "version": "1.2.2",
4
4
  "description": "One install for Stratos AI: OpenLLMetry agent telemetry (--import) AND the stratos-mcp-proxy for MCP servers.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -1,49 +1,97 @@
1
- // @intelli-1113/stratos-sdk — initialize OpenLLMetry and stream telemetry to Stratos AI.
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)
8
- //
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.
12
- import * as traceloop from "@traceloop/node-server-sdk";
13
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
14
-
15
- let started = false;
16
-
17
- export function start(opts = {}) {
18
- if (started) return; // idempotent — safe to import/register more than once
19
- const token = opts.token || process.env.STRATOS_TOKEN || "";
20
- const origin = (opts.url || process.env.STRATOS_URL || "http://localhost:4000").replace(/\/+$/, "");
21
- const appName = opts.appName || process.env.STRATOS_APP_NAME || process.env.npm_package_name || "agent";
22
- const heartbeatMs = Number(opts.heartbeatMs || process.env.STRATOS_HEARTBEAT_MS || 30000);
23
-
24
- if (!token) {
25
- console.warn("[stratos] STRATOS_TOKEN not set — telemetry disabled. Add the token from Stratos > Add agent.");
26
- return;
27
- }
28
- started = true;
29
-
30
- const ingest = `${origin}/api/ingest`;
31
- const heartbeatUrl = `${origin}/api/heartbeat`;
32
-
33
- traceloop.initialize({
34
- appName,
35
- disableBatch: true,
36
- exporter: new OTLPTraceExporter({ url: ingest, headers: { "x-stratos-token": token } }),
37
- });
38
-
39
- // Keep the agent "online" while the process is alive (telemetry alone only
40
- // fires on LLM calls, which would let liveness lapse between requests).
41
- const ping = () =>
42
- fetch(heartbeatUrl, { method: "POST", headers: { "x-stratos-token": token } }).catch(() => {});
43
- ping();
44
- if (heartbeatMs > 0) setInterval(ping, heartbeatMs).unref();
45
-
46
- console.log(`[stratos] telemetry → ${ingest} (heartbeat ${heartbeatMs}ms)`);
47
- }
48
-
49
- export default { start };
1
+ // @intelli-1113/stratos-sdk — initialize OpenLLMetry and stream telemetry to Stratos AI.
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)
8
+ //
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.
12
+ import * as traceloop from "@traceloop/node-server-sdk";
13
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
14
+ import { createRequire } from "node:module";
15
+ import { readFileSync } from "node:fs";
16
+ import { join } from "node:path";
17
+
18
+ const _require = createRequire(import.meta.url);
19
+ const resolvable = (pkg) => { try { _require.resolve(pkg); return true; } catch { return false; } };
20
+
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
+ function agentDeps() {
25
+ try {
26
+ const pkg = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf8"));
27
+ return { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
28
+ } catch { return {}; }
29
+ }
30
+
31
+ // Detect the agent framework from declared deps (preferred) or resolution — no manual input.
32
+ function detectFramework() {
33
+ const deps = agentDeps();
34
+ const has = (n) => n in deps || resolvable(n);
35
+ // Higher-level agent frameworks FIRST (several pull in @langchain/* transitively).
36
+ if (has("@google/adk") || has("google-adk") || has("@iqai/adk")) return "google-adk";
37
+ if (has("@openai/agents")) return "openai-agents";
38
+ if (has("llamaindex")) return "llamaindex";
39
+ if (has("crewai")) return "crewai";
40
+ if (has("@langchain/langgraph")) return "langgraph";
41
+ if (has("langchain") || has("@langchain/core")) return "langchain";
42
+ if (has("ai")) return "vercel-ai";
43
+ if (has("@google/generative-ai") || has("@google/genai")) return "google-genai";
44
+ if (has("@anthropic-ai/sdk")) return "anthropic";
45
+ if (has("openai")) return "openai";
46
+ return null;
47
+ }
48
+ // Best-effort model from common env vars (or opts.model). Spans still refine it.
49
+ 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;
52
+ }
53
+
54
+ let started = false;
55
+
56
+ export function start(opts = {}) {
57
+ if (started) return; // idempotent — safe to import/register more than once
58
+ 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);
62
+
63
+ if (!token) {
64
+ console.warn("[stratos] STRATOS_TOKEN not set — telemetry disabled. Add the token from Stratos > Add agent.");
65
+ return;
66
+ }
67
+ started = true;
68
+
69
+ const ingest = `${origin}/api/ingest`;
70
+ const heartbeatUrl = `${origin}/api/heartbeat`;
71
+
72
+ traceloop.initialize({
73
+ appName,
74
+ disableBatch: true,
75
+ exporter: new OTLPTraceExporter({ url: ingest, headers: { "x-stratos-token": token } }),
76
+ });
77
+
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 };
82
+
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
+ const ping = () =>
86
+ fetch(heartbeatUrl, {
87
+ method: "POST",
88
+ headers: { "x-stratos-token": token, "Content-Type": "application/json" },
89
+ body: JSON.stringify(meta),
90
+ }).catch(() => {});
91
+ ping();
92
+ if (heartbeatMs > 0) setInterval(ping, heartbeatMs).unref();
93
+
94
+ console.log(`[stratos] telemetry → ${ingest} (heartbeat ${heartbeatMs}ms, framework=${meta.framework || "?"})`);
95
+ }
96
+
97
+ export default { start };