import * as Clock from "effect/Clock";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Metric from "effect/Metric";import { dual } from "effect/Function"; import { compactMetricAttributes, normalizeModelMetricLabel, outcomeFromExit,} from "./Attributes.ts"; export const rpcRequestsTotal = Metric.counter("t3_rpc_requests_total", { description: "Total RPC requests handled by the websocket RPC server.",}); export const rpcRequestDuration = Metric.timer("t3_rpc_request_duration", { description: "RPC request handling duration.",}); export const orchestrationCommandsTotal = Metric.counter("t3_orchestration_commands_total", { description: "Total orchestration commands dispatched.",}); export const orchestrationCommandDuration = Metric.timer("t3_orchestration_command_duration", { description: "Orchestration command dispatch duration.",}); export const orchestrationCommandAckDuration = Metric.timer( "t3_orchestration_command_ack_duration", { description: "Time from orchestration command dispatch to the first committed domain event emitted for that command.", },); export const orchestrationEventsProcessedTotal = Metric.counter( "t3_orchestration_events_processed_total", { description: "Total orchestration intent events processed by runtime reactors.", },); export const providerSessionsTotal = Metric.counter("t3_provider_sessions_total", { description: "Total provider session lifecycle operations.",}); export const providerTurnsTotal = Metric.counter("t3_provider_turns_total", { description: "Total provider turn lifecycle operations.",}); export const providerTurnDuration = Metric.timer("t3_provider_turn_duration", { description: "Provider turn request duration.",}); export const providerRuntimeEventsTotal = Metric.counter("t3_provider_runtime_events_total", { description: "Total canonical provider runtime events processed.",}); export const gitCommandsTotal = Metric.counter("t3_git_commands_total", { description: "Total git commands executed by the server runtime.",}); export const gitCommandDuration = Metric.timer("t3_git_command_duration", { description: "Git command execution duration.",}); export const terminalSessionsTotal = Metric.counter("t3_terminal_sessions_total", { description: "Total terminal sessions started.",}); export const terminalRestartsTotal = Metric.counter("t3_terminal_restarts_total", { description: "Total terminal restart requests handled.",}); export const metricAttributes = ( attributes: Readonly<Record<string, unknown>>,): ReadonlyArray<[string, string]> => Object.entries(compactMetricAttributes(attributes)); export const increment = ( metric: Metric.Metric<number, unknown>, attributes: Readonly<Record<string, unknown>>, amount = 1,) => Metric.update(Metric.withAttributes(metric, metricAttributes(attributes)), amount); export interface WithMetricsOptions { readonly counter?: Metric.Metric<number, unknown>; readonly timer?: Metric.Metric<Duration.Duration, unknown>; readonly attributes?: | Readonly<Record<string, unknown>> | (() => Readonly<Record<string, unknown>>); readonly outcomeAttributes?: ( outcome: ReturnType<typeof outcomeFromExit>, ) => Readonly<Record<string, unknown>>;} const withMetricsImpl = <A, E, R>( effect: Effect.Effect<A, E, R>, options: WithMetricsOptions,): Effect.Effect<A, E, R> => Effect.gen(function* () { const startedAt = yield* Clock.currentTimeNanos; const exit = yield* Effect.exit(effect); const endedAt = yield* Clock.currentTimeNanos; const elapsedNanos = endedAt > startedAt ? endedAt - startedAt : 0n; const duration = Duration.nanos(elapsedNanos); const baseAttributes = typeof options.attributes === "function" ? options.attributes() : (options.attributes ?? {}); if (options.timer) { yield* Metric.update( Metric.withAttributes(options.timer, metricAttributes(baseAttributes)), duration, ); } if (options.counter) { const outcome = outcomeFromExit(exit); yield* Metric.update( Metric.withAttributes( options.counter, metricAttributes({ ...baseAttributes, outcome, ...(options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}), }), ), 1, ); } if (Exit.isSuccess(exit)) { return exit.value; } return yield* Effect.failCause(exit.cause); }); export const withMetrics: { <A, E, R>( options: WithMetricsOptions, ): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>; <A, E, R>(effect: Effect.Effect<A, E, R>, options: WithMetricsOptions): Effect.Effect<A, E, R>;} = dual(2, withMetricsImpl); export const providerMetricAttributes = ( provider: string, extra?: Readonly<Record<string, unknown>>,) => compactMetricAttributes({ provider, ...extra, }); export const providerTurnMetricAttributes = (input: { readonly provider: string; readonly model: string | null | undefined; readonly extra?: Readonly<Record<string, unknown>>;}) => { const modelFamily = normalizeModelMetricLabel(input.model); return compactMetricAttributes({ provider: input.provider, ...(modelFamily ? { modelFamily } : {}), ...input.extra, });};
import * as Clock from "effect/Clock";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Exit from "effect/Exit";
import * as Metric from "effect/Metric";
import { dual } from "effect/Function";
import {
compactMetricAttributes,
normalizeModelMetricLabel,
outcomeFromExit,
} from "./Attributes.ts";
export const rpcRequestsTotal = Metric.counter("t3_rpc_requests_total", {
description: "Total RPC requests handled by the websocket RPC server.",
});
export const rpcRequestDuration = Metric.timer("t3_rpc_request_duration", {
description: "RPC request handling duration.",
});
export const orchestrationCommandsTotal = Metric.counter("t3_orchestration_commands_total", {
description: "Total orchestration commands dispatched.",
});
export const orchestrationCommandDuration = Metric.timer("t3_orchestration_command_duration", {
description: "Orchestration command dispatch duration.",
});
export const orchestrationCommandAckDuration = Metric.timer(
"t3_orchestration_command_ack_duration",
{
description:
"Time from orchestration command dispatch to the first committed domain event emitted for that command.",
},
);
export const orchestrationEventsProcessedTotal = Metric.counter(
"t3_orchestration_events_processed_total",
{
description: "Total orchestration intent events processed by runtime reactors.",
},
);
export const providerSessionsTotal = Metric.counter("t3_provider_sessions_total", {
description: "Total provider session lifecycle operations.",
});
export const providerTurnsTotal = Metric.counter("t3_provider_turns_total", {
description: "Total provider turn lifecycle operations.",
});
export const providerTurnDuration = Metric.timer("t3_provider_turn_duration", {
description: "Provider turn request duration.",
});
export const providerRuntimeEventsTotal = Metric.counter("t3_provider_runtime_events_total", {
description: "Total canonical provider runtime events processed.",
});
export const gitCommandsTotal = Metric.counter("t3_git_commands_total", {
description: "Total git commands executed by the server runtime.",
});
export const gitCommandDuration = Metric.timer("t3_git_command_duration", {
description: "Git command execution duration.",
});
export const terminalSessionsTotal = Metric.counter("t3_terminal_sessions_total", {
description: "Total terminal sessions started.",
});
export const terminalRestartsTotal = Metric.counter("t3_terminal_restarts_total", {
description: "Total terminal restart requests handled.",
});
export const metricAttributes = (
attributes: Readonly<Record<string, unknown>>,
): ReadonlyArray<[string, string]> => Object.entries(compactMetricAttributes(attributes));
export const increment = (
metric: Metric.Metric<number, unknown>,
attributes: Readonly<Record<string, unknown>>,
amount = 1,
) => Metric.update(Metric.withAttributes(metric, metricAttributes(attributes)), amount);
export interface WithMetricsOptions {
readonly counter?: Metric.Metric<number, unknown>;
readonly timer?: Metric.Metric<Duration.Duration, unknown>;
readonly attributes?:
| Readonly<Record<string, unknown>>
| (() => Readonly<Record<string, unknown>>);
readonly outcomeAttributes?: (
outcome: ReturnType<typeof outcomeFromExit>,
) => Readonly<Record<string, unknown>>;
}
const withMetricsImpl = <A, E, R>(
effect: Effect.Effect<A, E, R>,
options: WithMetricsOptions,
): Effect.Effect<A, E, R> =>
Effect.gen(function* () {
const startedAt = yield* Clock.currentTimeNanos;
const exit = yield* Effect.exit(effect);
const endedAt = yield* Clock.currentTimeNanos;
const elapsedNanos = endedAt > startedAt ? endedAt - startedAt : 0n;
const duration = Duration.nanos(elapsedNanos);
const baseAttributes =
typeof options.attributes === "function" ? options.attributes() : (options.attributes ?? {});
if (options.timer) {
yield* Metric.update(
Metric.withAttributes(options.timer, metricAttributes(baseAttributes)),
duration,
);
}
if (options.counter) {
const outcome = outcomeFromExit(exit);
yield* Metric.update(
Metric.withAttributes(
options.counter,
metricAttributes({
...baseAttributes,
outcome,
...(options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}),
}),
),
1,
);
}
if (Exit.isSuccess(exit)) {
return exit.value;
}
return yield* Effect.failCause(exit.cause);
});
export const withMetrics: {
<A, E, R>(
options: WithMetricsOptions,
): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
<A, E, R>(effect: Effect.Effect<A, E, R>, options: WithMetricsOptions): Effect.Effect<A, E, R>;
} = dual(2, withMetricsImpl);
export const providerMetricAttributes = (
provider: string,
extra?: Readonly<Record<string, unknown>>,
) =>
compactMetricAttributes({
provider,
...extra,
});
export const providerTurnMetricAttributes = (input: {
readonly provider: string;
readonly model: string | null | undefined;
readonly extra?: Readonly<Record<string, unknown>>;
}) => {
const modelFamily = normalizeModelMetricLabel(input.model);
return compactMetricAttributes({
provider: input.provider,
...(modelFamily ? { modelFamily } : {}),
...input.extra,
});
};