feat(backend): add 11 telemetry events for Smart Actions
This commit is contained in:
parent
3f903a7a70
commit
e9f389a8b7
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { llm } from './llm.js';
|
import { llm } from './llm.js';
|
||||||
|
import { trackEvent } from './telemetry.js';
|
||||||
|
|
||||||
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
@ -72,7 +73,10 @@ export async function runCopilotTransform(action: CopilotAction, text: string):
|
|||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
}), 60_000);
|
}), 60_000);
|
||||||
const out = result.content.trim();
|
const out = result.content.trim();
|
||||||
if (out.length > 0) return out;
|
if (out.length > 0) {
|
||||||
|
trackEvent('copilot_transform', 'system', { action, durationMs: String(Date.now()) });
|
||||||
|
return out;
|
||||||
|
}
|
||||||
break; // empty response — fall through to heuristic
|
break; // empty response — fall through to heuristic
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const isRateLimit = err instanceof Error && (err.message.includes('429') || err.message.includes('rate'));
|
const isRateLimit = err instanceof Error && (err.message.includes('429') || err.message.includes('rate'));
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { z } from 'zod';
|
|||||||
import { getUserId, getRequestProductId } from '../../lib/request-context.js';
|
import { getUserId, getRequestProductId } from '../../lib/request-context.js';
|
||||||
import { BadRequestError, NotFoundError } from '@bytelyst/errors';
|
import { BadRequestError, NotFoundError } from '@bytelyst/errors';
|
||||||
import { isFeatureEnabled } from '../../lib/feature-flags.js';
|
import { isFeatureEnabled } from '../../lib/feature-flags.js';
|
||||||
|
import { trackEvent } from '../../lib/telemetry.js';
|
||||||
import { embedText, cosineSimilarity, stripHtmlForEmbedding } from '../../lib/embeddings.js';
|
import { embedText, cosineSimilarity, stripHtmlForEmbedding } from '../../lib/embeddings.js';
|
||||||
import { llm } from '../../lib/llm.js';
|
import { llm } from '../../lib/llm.js';
|
||||||
import {
|
import {
|
||||||
@ -46,6 +47,7 @@ export async function notePromptRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
const userId = getUserId(req);
|
const userId = getUserId(req);
|
||||||
const input = CreatePromptTemplateSchema.parse(req.body);
|
const input = CreatePromptTemplateSchema.parse(req.body);
|
||||||
const created = await repo.createPromptTemplate(userId, input);
|
const created = await repo.createPromptTemplate(userId, input);
|
||||||
|
trackEvent('smart_action_template_created', userId, { category: input.category ?? 'transform', inputType: input.inputType ?? 'text' });
|
||||||
reply.code(201);
|
reply.code(201);
|
||||||
return created;
|
return created;
|
||||||
});
|
});
|
||||||
@ -381,6 +383,7 @@ Return ONLY valid JSON, no other text.`,
|
|||||||
content = lines.slice(1).join('\n').trim();
|
content = lines.slice(1).join('\n').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackEvent('url_extract_completed', userId, { domain: new URL(input.url).hostname, wordCount: String(rawText.split(/\s+/).length) });
|
||||||
return { title, content, url: input.url, summarized: true, model: result.model, usage: result.usage };
|
return { title, content, url: input.url, summarized: true, model: result.model, usage: result.usage };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import { llm } from '../../lib/llm.js';
|
import { llm } from '../../lib/llm.js';
|
||||||
import { config } from '../../lib/config.js';
|
import { config } from '../../lib/config.js';
|
||||||
|
import { trackEvent } from '../../lib/telemetry.js';
|
||||||
import {
|
import {
|
||||||
buildVisionMessage,
|
buildVisionMessage,
|
||||||
hasVisionContent,
|
hasVisionContent,
|
||||||
@ -101,10 +102,23 @@ export async function executePrompt(
|
|||||||
output.approvalState = 'applied';
|
output.approvalState = 'applied';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackEvent('smart_action_run', 'system', {
|
||||||
|
templateSlug: template.slug,
|
||||||
|
inputType: template.inputType,
|
||||||
|
model: result.model,
|
||||||
|
totalTokens: String(result.usage.totalTokens),
|
||||||
|
});
|
||||||
|
trackEvent('smart_action_result_saved', 'system', {
|
||||||
|
outputAction: template.outputType,
|
||||||
|
resultType: output.approvalState ?? 'applied',
|
||||||
|
});
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
lastError = err;
|
lastError = err;
|
||||||
const isRateLimit = err instanceof Error && (err.message.includes('429') || err.message.includes('rate'));
|
const errorType = err instanceof Error ? (err.message.includes('429') ? 'rate_limit' : err.message.includes('timed out') ? 'timeout' : 'llm_error') : 'unknown';
|
||||||
|
trackEvent('smart_action_error', 'system', { errorType, templateSlug: template.slug });
|
||||||
|
const isRateLimit = errorType === 'rate_limit';
|
||||||
if (isRateLimit && attempt < maxRetries - 1) {
|
if (isRateLimit && attempt < maxRetries - 1) {
|
||||||
await new Promise((r) => setTimeout(r, baseDelayMs * Math.pow(2, attempt)));
|
await new Promise((r) => setTimeout(r, baseDelayMs * Math.pow(2, attempt)));
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { BadRequestError, NotFoundError } from '@bytelyst/errors';
|
|||||||
import { getCollection } from '../../lib/datastore.js';
|
import { getCollection } from '../../lib/datastore.js';
|
||||||
import { PRODUCT_ID } from '../../lib/product-config.js';
|
import { PRODUCT_ID } from '../../lib/product-config.js';
|
||||||
import { isFeatureEnabled } from '../../lib/feature-flags.js';
|
import { isFeatureEnabled } from '../../lib/feature-flags.js';
|
||||||
|
import { trackEvent } from '../../lib/telemetry.js';
|
||||||
import { llm } from '../../lib/llm.js';
|
import { llm } from '../../lib/llm.js';
|
||||||
import * as noteRepo from '../notes/repository.js';
|
import * as noteRepo from '../notes/repository.js';
|
||||||
import * as promptRepo from './repository.js';
|
import * as promptRepo from './repository.js';
|
||||||
@ -212,6 +213,7 @@ export async function runSchedulerTick(): Promise<number> {
|
|||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
trackEvent('scheduled_action_fired', schedule.userId, { scheduleId: schedule.id, templateSlug: template?.slug ?? schedule.templateId });
|
||||||
ran++;
|
ran++;
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const msg = err instanceof Error ? err.message : 'Unknown scheduler error';
|
const msg = err instanceof Error ? err.message : 'Unknown scheduler error';
|
||||||
@ -413,6 +415,7 @@ export async function promptSchedulerRoutes(app: FastifyInstance): Promise<void>
|
|||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
trackEvent('webhook_triggered', userId, { webhookId: id, triggerEvent: webhook.triggerEvent });
|
||||||
return { triggered: true, webhookId: id, result };
|
return { triggered: true, webhookId: id, result };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user