Files
anything-llm/server/models/telemetry.js
Timothy Carambat 0fb33736da Workspace Chat with documents overhaul (#4261)
* Create parse endpoint in collector (#4212)

* create parse endpoint in collector

* revert cleanup temp util call

* lint

* remove unused cleanupTempDocuments function

* revert slug change
minor change for destinations

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* Add parsed files table and parse server endpoints (#4222)

* add workspace_parsed_files table + parse endpoints/models

* remove dev api parse endpoint

* remove unneeded imports

* iterate over all files + remove unneeded update function + update telemetry debounce

* Upload UI/UX context window check + frontend alert (#4230)

* prompt user to embed if exceeds prompt window + handle embed + handle cancel

* add tokenCountEstimate to workspace_parsed_files + optimizations

* use util for path locations + use safeJsonParse

* add modal for user decision on overflow of context window

* lint

* dynamic fetching of provider/model combo + inject parsed documents

* remove unneeded comments

* popup ui for attaching/removing files + warning to embed + wip fetching states on update

* remove prop drilling, fetch files/limits directly in attach files popup

* rework ux of FE + BE optimizations

* fix ux of FE + BE optimizations

* Implement bidirectional sync for parsed file states
linting
small changes and comments

* move parse support to another endpoint file
simplify calls and loading of records

* button borders

* enable default users to upload parsed files but NOT embed

* delete cascade on user/workspace/thread deletion to remove parsedFileRecord

* enable bgworker with "always" jobs and optional document sync jobs
orphan document job: Will find any broken reference files to prevent overpollution of the storage folder. This will run 10s after boot and every 12hr after

* change run timeout for orphan job to 1m to allow settling before spawning a worker

* linting and cleanup pr

---------

Co-authored-by: Timothy Carambat <rambat1010@gmail.com>

* dev build

* fix tooltip hiding during embedding overflow files

* prevent crash log from ERRNO on parse files

* unused import

* update docs link

* Migrate parsed-files to GET endpoint
patch logic for grabbing models names from utils
better handling for undetermined context windows (null instead of Pos_INIFI)
UI placeholder for null context windows

* patch URL

---------

Co-authored-by: Sean Hatfield <seanhatfield5@gmail.com>
2025-08-11 09:26:19 -07:00

148 lines
4.2 KiB
JavaScript

const { v4 } = require("uuid");
const { SystemSettings } = require("./systemSettings");
// Map of events and last sent time to check if the event is on cooldown
// This will be cleared on server restart - but that is fine since it is mostly to just
// prevent spamming the logs.
const TelemetryCooldown = new Map();
const Telemetry = {
// Write-only key. It can't read events or any of your other data, so it's safe to use in public apps.
pubkey: "phc_9qu7QLpV8L84P3vFmEiZxL020t2EqIubP7HHHxrSsqS",
stubDevelopmentEvents: true, // [DO NOT TOUCH] Core team only.
label: "telemetry_id",
/*
Key value pairs of events that should be debounced to prevent spamming the logs.
This should be used for events that could be triggered in rapid succession that are not useful to atomically log.
The value is the number of seconds to debounce the event
*/
debounced: {
sent_chat: 1800,
agent_chat_sent: 1800,
agent_chat_started: 1800,
agent_tool_call: 1800,
// Document mgmt events
document_uploaded: 30,
documents_embedded_in_workspace: 30,
link_uploaded: 30,
raw_document_uploaded: 30,
document_parsed: 30,
},
id: async function () {
const result = await SystemSettings.get({ label: this.label });
return result?.value || null;
},
connect: async function () {
const client = this.client();
const distinctId = await this.findOrCreateId();
return { client, distinctId };
},
isDev: function () {
return process.env.NODE_ENV === "development" && this.stubDevelopmentEvents;
},
client: function () {
if (process.env.DISABLE_TELEMETRY === "true" || this.isDev()) return null;
const { PostHog } = require("posthog-node");
return new PostHog(this.pubkey);
},
runtime: function () {
if (process.env.ANYTHING_LLM_RUNTIME === "docker") return "docker";
if (process.env.NODE_ENV === "production") return "production";
return "other";
},
/**
* Checks if the event is on cooldown
* @param {string} event - The event to check
* @returns {boolean} - True if the event is on cooldown, false otherwise
*/
isOnCooldown: function (event) {
// If the event is not debounced, return false
if (!this.debounced[event]) return false;
// If the event is not in the cooldown map, return false
const lastSent = TelemetryCooldown.get(event);
if (!lastSent) return false;
// If the event is in the cooldown map, check if it has expired
const now = Date.now();
const cooldown = this.debounced[event] * 1000;
return now - lastSent < cooldown;
},
/**
* Marks the event as on cooldown - will check if the event is debounced first
* @param {string} event - The event to mark
*/
markOnCooldown: function (event) {
if (!this.debounced[event]) return;
TelemetryCooldown.set(event, Date.now());
},
sendTelemetry: async function (
event,
eventProperties = {},
subUserId = null,
silent = false
) {
try {
const { client, distinctId: systemId } = await this.connect();
if (!client) return;
const distinctId = !!subUserId ? `${systemId}::${subUserId}` : systemId;
const properties = { ...eventProperties, runtime: this.runtime() };
// If the event is on cooldown, return
if (this.isOnCooldown(event)) return;
// Silence some events to keep logs from being too messy in production
// eg: Tool calls from agents spamming the logs.
if (!silent) {
console.log(`\x1b[32m[TELEMETRY SENT]\x1b[0m`, {
event,
distinctId,
properties,
});
}
client.capture({
event,
distinctId,
properties,
});
} catch {
return;
} finally {
// Mark the event as on cooldown if needed
this.markOnCooldown(event);
}
},
flush: async function () {
const client = this.client();
if (!client) return;
await client.shutdownAsync();
},
setUid: async function () {
const newId = v4();
await SystemSettings._updateSettings({ [this.label]: newId });
return newId;
},
findOrCreateId: async function () {
let currentId = await this.id();
if (currentId) return currentId;
currentId = await this.setUid();
return currentId;
},
};
module.exports = { Telemetry };