UNPKG

@copilotkit/shared

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

1 lines 9.08 kB
{"version":3,"file":"telemetry-client.mjs","names":["uuidv4"],"sources":["../../src/telemetry/telemetry-client.ts"],"sourcesContent":["import { Analytics } from \"@segment/analytics-node\";\nimport type { AnalyticsEvents } from \"./events\";\nimport { flattenObject } from \"./utils\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { lambdaClient, parseAndWarnTelemetryId } from \"./lambda-client\";\n\n/**\n * Checks if telemetry is disabled via environment variables.\n * Users can opt out by setting:\n * - COPILOTKIT_TELEMETRY_DISABLED=true or COPILOTKIT_TELEMETRY_DISABLED=1\n * - DO_NOT_TRACK=true or DO_NOT_TRACK=1\n */\nexport function isTelemetryDisabled(): boolean {\n return (\n (process.env as Record<string, string | undefined>)\n .COPILOTKIT_TELEMETRY_DISABLED === \"true\" ||\n (process.env as Record<string, string | undefined>)\n .COPILOTKIT_TELEMETRY_DISABLED === \"1\" ||\n (process.env as Record<string, string | undefined>).DO_NOT_TRACK ===\n \"true\" ||\n (process.env as Record<string, string | undefined>).DO_NOT_TRACK === \"1\"\n );\n}\n\nexport class TelemetryClient {\n segment: Analytics | undefined;\n globalProperties: Record<string, any> = {};\n cloudConfiguration: { publicApiKey: string; baseUrl: string } | null = null;\n // EIP / Intelligence license token (Ed25519-signed JWT). The lambda\n // client decodes its payload to extract telemetry_id. Customer API\n // keys are NOT used here — they flow only into Segment.\n private licenseToken: string | null = null;\n // Parsed telemetry_id from the license-token JWT payload. Cached at\n // setLicenseToken time so `capture()` can branch on identified vs\n // anonymous without re-parsing per event. Null when the token is\n // absent or yielded no telemetry_id.\n private telemetryId: string | null = null;\n packageName: string;\n packageVersion: string;\n private telemetryDisabled: boolean = false;\n // Client-side sampling rate for anonymous events. Identified events\n // (those whose license token yielded a telemetry_id) bypass the gate\n // entirely. Applied uniformly to both the lambda sink and Segment —\n // one dice roll per capture, both sinks see the same decision.\n private sampleRate: number = 0.05;\n private anonymousId = `anon_${uuidv4()}`;\n\n constructor({\n packageName,\n packageVersion,\n telemetryDisabled,\n telemetryBaseUrl,\n sampleRate,\n }: {\n packageName: string;\n packageVersion: string;\n telemetryDisabled?: boolean;\n telemetryBaseUrl?: string;\n sampleRate?: number;\n }) {\n this.packageName = packageName;\n this.packageVersion = packageVersion;\n this.telemetryDisabled = telemetryDisabled || isTelemetryDisabled();\n\n if (this.telemetryDisabled) {\n return;\n }\n\n this.setSampleRate(sampleRate);\n\n // eslint-disable-next-line\n const writeKey =\n process.env.COPILOTKIT_SEGMENT_WRITE_KEY ||\n \"n7XAZtQCGS2v1vvBy3LgBCv2h3Y8whja\";\n\n this.segment = new Analytics({\n writeKey,\n });\n\n this.setGlobalProperties({\n \"copilotkit.package.name\": packageName,\n \"copilotkit.package.version\": packageVersion,\n });\n }\n\n private shouldSendEvent() {\n const randomNumber = Math.random();\n return randomNumber < this.sampleRate;\n }\n\n async capture<K extends keyof AnalyticsEvents>(\n event: K,\n properties: AnalyticsEvents[K],\n ) {\n if (this.telemetryDisabled) {\n return;\n }\n\n // Anonymous callers (no telemetry_id) are gated by sampleRate.\n // Identified callers (license token with telemetry_id) always send —\n // the volume is bounded by paying-customer count and full fidelity\n // per identified customer is worth the marginal cost.\n if (!this.telemetryId && !this.shouldSendEvent()) {\n return;\n }\n\n // Identified events ship at 100% effective rate, anonymous events at\n // sampleRate. Compute per-event so downstream weight-based extrapolation\n // (sampleWeight = 1 / effectiveRate) is correct for both populations;\n // a single global sampleWeight would overweight identified-customer\n // counts by 1/sampleRate.\n const effectiveSampleRate = this.telemetryId ? 1 : this.sampleRate;\n const samplingMeta = {\n sampleRate: effectiveSampleRate,\n sampleRateAdjustmentFactor: 1 - effectiveSampleRate,\n sampleWeight: 1 / effectiveSampleRate,\n };\n\n const flattenedProperties = flattenObject(properties);\n const propertiesWithGlobal: Record<string, any> = {\n ...this.globalProperties,\n ...samplingMeta,\n ...flattenedProperties,\n };\n const orderedPropertiesWithGlobal = Object.keys(propertiesWithGlobal)\n .sort()\n .reduce(\n (obj, key) => {\n obj[key] = propertiesWithGlobal[key];\n return obj;\n },\n {} as Record<string, any>,\n );\n\n await lambdaClient.send({\n event,\n properties: flattenedProperties,\n globalProperties: { ...this.globalProperties, ...samplingMeta },\n packageName: this.packageName,\n packageVersion: this.packageVersion,\n licenseToken: this.licenseToken ?? undefined,\n });\n\n if (this.segment) {\n this.segment.track({\n anonymousId: this.anonymousId,\n event,\n properties: { ...orderedPropertiesWithGlobal },\n });\n }\n }\n\n setGlobalProperties(properties: Record<string, any>) {\n const flattenedProperties = flattenObject(properties);\n this.globalProperties = {\n ...this.globalProperties,\n ...flattenedProperties,\n };\n }\n\n setCloudConfiguration(properties: { publicApiKey: string; baseUrl: string }) {\n this.cloudConfiguration = properties;\n\n this.setGlobalProperties({\n cloud: {\n publicApiKey: properties.publicApiKey,\n baseUrl: properties.baseUrl,\n },\n });\n }\n\n // The license token isn't added to globalProperties — we don't want\n // the JWT itself shipped on every event. Only its decoded telemetry_id\n // travels, in the X-CopilotKit-Telemetry-Id header set by lambda-client.\n setLicenseToken(licenseToken: string) {\n this.licenseToken = licenseToken;\n this.telemetryId = parseAndWarnTelemetryId(licenseToken);\n }\n\n private setSampleRate(sampleRate: number | undefined) {\n let _sampleRate: number;\n\n _sampleRate = sampleRate ?? 0.05;\n\n // eslint-disable-next-line\n if (process.env.COPILOTKIT_TELEMETRY_SAMPLE_RATE) {\n // eslint-disable-next-line\n _sampleRate = parseFloat(process.env.COPILOTKIT_TELEMETRY_SAMPLE_RATE);\n }\n\n // Number.isNaN guards against parseFloat(\"nonsense\") slipping past the\n // range check (all NaN comparisons are false), which would silently\n // drop every anonymous event with no signal — especially important\n // since the default is now 0.05, making env-var overrides more common.\n if (Number.isNaN(_sampleRate) || _sampleRate < 0 || _sampleRate > 1) {\n throw new Error(\"Sample rate must be between 0 and 1\");\n }\n\n this.sampleRate = _sampleRate;\n // Per-event sampling metadata (sampleRate/sampleRateAdjustmentFactor/\n // sampleWeight) is computed in capture() so identified events get\n // their own effectiveSampleRate=1 weight instead of the anonymous\n // population's 1/sampleRate.\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,SAAgB,sBAA+B;AAC7C,QACG,QAAQ,IACN,kCAAkC,UACpC,QAAQ,IACN,kCAAkC,OACpC,QAAQ,IAA2C,iBAClD,UACD,QAAQ,IAA2C,iBAAiB;;AAIzE,IAAa,kBAAb,MAA6B;CAuB3B,YAAY,EACV,aACA,gBACA,mBACA,kBACA,cAOC;0BAjCqC,EAAE;4BAC6B;sBAIjC;qBAKD;2BAGA;oBAKR;qBACP,QAAQA,IAAQ;AAepC,OAAK,cAAc;AACnB,OAAK,iBAAiB;AACtB,OAAK,oBAAoB,qBAAqB,qBAAqB;AAEnE,MAAI,KAAK,kBACP;AAGF,OAAK,cAAc,WAAW;AAO9B,OAAK,UAAU,IAAI,UAAU,EAC3B,UAJA,QAAQ,IAAI,gCACZ,oCAID,CAAC;AAEF,OAAK,oBAAoB;GACvB,2BAA2B;GAC3B,8BAA8B;GAC/B,CAAC;;CAGJ,AAAQ,kBAAkB;AAExB,SADqB,KAAK,QAAQ,GACZ,KAAK;;CAG7B,MAAM,QACJ,OACA,YACA;AACA,MAAI,KAAK,kBACP;AAOF,MAAI,CAAC,KAAK,eAAe,CAAC,KAAK,iBAAiB,CAC9C;EAQF,MAAM,sBAAsB,KAAK,cAAc,IAAI,KAAK;EACxD,MAAM,eAAe;GACnB,YAAY;GACZ,4BAA4B,IAAI;GAChC,cAAc,IAAI;GACnB;EAED,MAAM,sBAAsB,cAAc,WAAW;EACrD,MAAM,uBAA4C;GAChD,GAAG,KAAK;GACR,GAAG;GACH,GAAG;GACJ;EACD,MAAM,8BAA8B,OAAO,KAAK,qBAAqB,CAClE,MAAM,CACN,QACE,KAAK,QAAQ;AACZ,OAAI,OAAO,qBAAqB;AAChC,UAAO;KAET,EAAE,CACH;AAEH,QAAM,aAAa,KAAK;GACtB;GACA,YAAY;GACZ,kBAAkB;IAAE,GAAG,KAAK;IAAkB,GAAG;IAAc;GAC/D,aAAa,KAAK;GAClB,gBAAgB,KAAK;GACrB,cAAc,KAAK,gBAAgB;GACpC,CAAC;AAEF,MAAI,KAAK,QACP,MAAK,QAAQ,MAAM;GACjB,aAAa,KAAK;GAClB;GACA,YAAY,EAAE,GAAG,6BAA6B;GAC/C,CAAC;;CAIN,oBAAoB,YAAiC;EACnD,MAAM,sBAAsB,cAAc,WAAW;AACrD,OAAK,mBAAmB;GACtB,GAAG,KAAK;GACR,GAAG;GACJ;;CAGH,sBAAsB,YAAuD;AAC3E,OAAK,qBAAqB;AAE1B,OAAK,oBAAoB,EACvB,OAAO;GACL,cAAc,WAAW;GACzB,SAAS,WAAW;GACrB,EACF,CAAC;;CAMJ,gBAAgB,cAAsB;AACpC,OAAK,eAAe;AACpB,OAAK,cAAc,wBAAwB,aAAa;;CAG1D,AAAQ,cAAc,YAAgC;EACpD,IAAI;AAEJ,gBAAc,cAAc;AAG5B,MAAI,QAAQ,IAAI,iCAEd,eAAc,WAAW,QAAQ,IAAI,iCAAiC;AAOxE,MAAI,OAAO,MAAM,YAAY,IAAI,cAAc,KAAK,cAAc,EAChE,OAAM,IAAI,MAAM,sCAAsC;AAGxD,OAAK,aAAa"}