feat(ai) - add observability (#20850)
**AI Chat - Tool Executions (counters, tagged with model)** ai-chat/tool-execution-succeeded: number of tool calls invoked by the AI that completed without error ai-chat/tool-execution-failed: number of tool calls invoked by the AI that threw an error **AI Chat - Token Usage (counters, tagged with model)** ai-chat/input-tokens: total input tokens sent to the model across all turns ai-chat/output-tokens: total output tokens generated by the model ai-chat/cache-read-tokens: input tokens served from the model's prompt cache (cheaper) ai-chat/cache-write-tokens: input tokens written into the prompt cache for future reuse **AI Chat - Latency (histograms in ms, tagged with model)** ai-chat/turn-latency-ms: total duration of a full chat turn (from stream start to stream end) ai-chat/step-latency-ms: duration of a single reasoning/tool-call step within a turn ai-chat/ttft-ms: time-to-first-token, i.e. how long until the model starts streaming output **MCP - Tool Executions (counters)** mcp/tool-execution-succeeded: number of MCP tool calls that completed successfully mcp/tool-execution-failed: number of MCP tool calls that threw an error
This commit is contained in:
+1
-1
@@ -86,7 +86,7 @@ const createSignInUpServiceForTests = () => {
|
||||
markEmailAsVerified: jest.fn(),
|
||||
} as any,
|
||||
{
|
||||
incrementCounter: jest.fn(),
|
||||
incrementCounterForEvent: jest.fn(),
|
||||
} as any,
|
||||
{
|
||||
invalidateAndRecompute: jest.fn(),
|
||||
|
||||
@@ -384,7 +384,7 @@ export class SignInUpService {
|
||||
undefined,
|
||||
);
|
||||
|
||||
void this.metricsService.incrementCounter({
|
||||
void this.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.SignUpSuccess,
|
||||
shouldStoreInCache: false,
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ export class CaptchaGuard implements CanActivate {
|
||||
if (result.success) {
|
||||
return true;
|
||||
} else {
|
||||
await this.metricsService.incrementCounter({
|
||||
await this.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.InvalidCaptcha,
|
||||
eventId: token || '',
|
||||
...(result.error ? { attributes: { error: result.error } } : {}),
|
||||
|
||||
+5
-5
@@ -116,7 +116,7 @@ export const useGraphQLErrorHandlerHook = <
|
||||
setResult,
|
||||
}) => {
|
||||
if (!result.errors || result.errors.length === 0) {
|
||||
void options.metricsService.incrementCounter({
|
||||
void options.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.GraphqlOperation200,
|
||||
});
|
||||
|
||||
@@ -178,11 +178,11 @@ export const useGraphQLErrorHandlerHook = <
|
||||
}
|
||||
|
||||
if (metricKey) {
|
||||
void options.metricsService.incrementCounter({
|
||||
void options.metricsService.incrementCounterForEvent({
|
||||
key: metricKey,
|
||||
});
|
||||
} else {
|
||||
void options.metricsService.incrementCounter({
|
||||
void options.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.GraphqlOperationUnknown,
|
||||
});
|
||||
}
|
||||
@@ -282,7 +282,7 @@ export const useGraphQLErrorHandlerHook = <
|
||||
isDefined(currentMetadataVersion) &&
|
||||
requestMetadataVersion !== `${currentMetadataVersion}`
|
||||
) {
|
||||
void options.metricsService.incrementCounter({
|
||||
void options.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.SchemaVersionMismatch,
|
||||
});
|
||||
|
||||
@@ -312,7 +312,7 @@ export const useGraphQLErrorHandlerHook = <
|
||||
isDefined(backendMajor) &&
|
||||
frontEndMajor < backendMajor
|
||||
) {
|
||||
void options.metricsService.incrementCounter({
|
||||
void options.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.AppVersionMismatch,
|
||||
});
|
||||
throw new GraphQLError(APP_VERSION_MISMATCH_ERROR, {
|
||||
|
||||
+2
-2
@@ -136,7 +136,7 @@ export class BullMQDriver
|
||||
);
|
||||
|
||||
this.workerMap[queueName].on('completed', (job) => {
|
||||
void this.metricsService.incrementCounter({
|
||||
void this.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.JobCompleted,
|
||||
attributes: { queue: queueName, job_name: job?.name ?? '' },
|
||||
shouldStoreInCache: false,
|
||||
@@ -148,7 +148,7 @@ export class BullMQDriver
|
||||
return;
|
||||
}
|
||||
|
||||
void this.metricsService.incrementCounter({
|
||||
void this.metricsService.incrementCounterForEvent({
|
||||
key: MetricsKeys.JobFailed,
|
||||
attributes: {
|
||||
queue: queueName,
|
||||
|
||||
@@ -80,7 +80,7 @@ export class MetricsService {
|
||||
return gauge;
|
||||
}
|
||||
|
||||
async incrementCounter({
|
||||
async incrementCounterForEvent({
|
||||
key,
|
||||
eventId,
|
||||
attributes,
|
||||
@@ -110,7 +110,7 @@ export class MetricsService {
|
||||
}
|
||||
}
|
||||
|
||||
async batchIncrementCounter({
|
||||
async incrementCounterForEvents({
|
||||
key,
|
||||
eventIds,
|
||||
attributes,
|
||||
@@ -130,6 +130,32 @@ export class MetricsService {
|
||||
}
|
||||
}
|
||||
|
||||
incrementCounterBy({
|
||||
key,
|
||||
amount,
|
||||
attributes,
|
||||
}: {
|
||||
key: MetricsKeys;
|
||||
amount: number;
|
||||
attributes?: Attributes;
|
||||
}): void {
|
||||
this.getMeter().createCounter(key).add(amount, attributes);
|
||||
}
|
||||
|
||||
recordHistogram({
|
||||
key,
|
||||
value,
|
||||
unit,
|
||||
attributes,
|
||||
}: {
|
||||
key: MetricsKeys;
|
||||
value: number;
|
||||
unit?: string;
|
||||
attributes?: Attributes;
|
||||
}): void {
|
||||
this.getMeter().createHistogram(key, { unit }).record(value, attributes);
|
||||
}
|
||||
|
||||
async groupMetrics(
|
||||
metrics: { name: string; cacheKey: MetricsKeys }[],
|
||||
): Promise<Record<string, number>> {
|
||||
|
||||
@@ -23,8 +23,14 @@ export enum MetricsKeys {
|
||||
WorkflowRunThrottled = 'workflow-run/throttled',
|
||||
WorkflowRunFailedToEnqueue = 'workflow-run/failed/to-enqueue',
|
||||
WorkflowRunSystemError = 'workflow-run/system-error',
|
||||
AiToolExecutionFailed = 'ai-tool-execution/failed',
|
||||
AiToolExecutionSucceeded = 'ai-tool-execution/succeeded',
|
||||
AiChatToolExecutionSucceeded = 'ai-chat/tool-execution-succeeded',
|
||||
AiChatToolExecutionFailed = 'ai-chat/tool-execution-failed',
|
||||
McpToolExecutionSucceeded = 'mcp/tool-execution-succeeded',
|
||||
McpToolExecutionFailed = 'mcp/tool-execution-failed',
|
||||
AiChatInputTokens = 'ai-chat/input-tokens',
|
||||
AiChatOutputTokens = 'ai-chat/output-tokens',
|
||||
AiChatCacheReadTokens = 'ai-chat/cache-read-tokens',
|
||||
AiChatCacheWriteTokens = 'ai-chat/cache-write-tokens',
|
||||
SchemaVersionMismatch = 'schema-version/mismatch',
|
||||
AppVersionMismatch = 'app-version/mismatch',
|
||||
CronJobDeletedWorkspace = 'cron-job/deleted-workspace',
|
||||
@@ -34,4 +40,7 @@ export enum MetricsKeys {
|
||||
JobCompleted = 'job/completed',
|
||||
JobFailed = 'job/failed',
|
||||
JobWaiting = 'job/waiting',
|
||||
AiChatTurnLatencyMs = 'ai-chat/turn-latency-ms',
|
||||
AiChatStepLatencyMs = 'ai-chat/step-latency-ms',
|
||||
AiChatTtftMs = 'ai-chat/ttft-ms',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user