fix(ops): gemini-block i summary-byggaren, inte routen
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
import { timingSafeEqual } from "node:crypto";
|
import { timingSafeEqual } from "node:crypto";
|
||||||
import { gte, sql } from "drizzle-orm";
|
|
||||||
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||||
import BRAND from "../../../../brand.config.json" with { type: "json" };
|
import BRAND from "../../../../brand.config.json" with { type: "json" };
|
||||||
import { geminiUsage } from "@app/database";
|
|
||||||
import { errors } from "../lib/errors.js";
|
import { errors } from "../lib/errors.js";
|
||||||
|
|
||||||
const CACHE_KEY = "ops:summary:cache";
|
const CACHE_KEY = "ops:summary:cache";
|
||||||
@@ -63,54 +61,6 @@ export async function opsRoutes(app: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const enriched = structuredClone(summary);
|
const enriched = structuredClone(summary);
|
||||||
const geminiDefaults = {
|
|
||||||
anrop_24h: null,
|
|
||||||
prompt_tokens_24h: null,
|
|
||||||
output_tokens_24h: null,
|
|
||||||
total_tokens_24h: null,
|
|
||||||
total_tokens_manad: null,
|
|
||||||
kostnad_usd_mikrocent_24h: null,
|
|
||||||
andel_pa_aamos: 0.0,
|
|
||||||
aamos_agreement: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const now = new Date();
|
|
||||||
const since24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
||||||
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
||||||
|
|
||||||
const [dayAgg, monthAgg] = await Promise.all([
|
|
||||||
app.db
|
|
||||||
.select({
|
|
||||||
count: sql<number>`count(*)`,
|
|
||||||
promptTokens: sql<number>`coalesce(sum(${geminiUsage.promptTokens}), 0)`,
|
|
||||||
outputTokens: sql<number>`coalesce(sum(${geminiUsage.outputTokens}), 0)`,
|
|
||||||
totalTokens: sql<number>`coalesce(sum(${geminiUsage.totalTokens}), 0)`,
|
|
||||||
costMicrocents: sql<number>`coalesce(sum(${geminiUsage.costMicrocents}), 0)`,
|
|
||||||
})
|
|
||||||
.from(geminiUsage)
|
|
||||||
.where(gte(geminiUsage.createdAt, since24h)),
|
|
||||||
app.db
|
|
||||||
.select({
|
|
||||||
totalTokens: sql<number>`coalesce(sum(${geminiUsage.totalTokens}), 0)`,
|
|
||||||
})
|
|
||||||
.from(geminiUsage)
|
|
||||||
.where(gte(geminiUsage.createdAt, startOfMonth)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
enriched.gemini = {
|
|
||||||
anrop_24h: Number(dayAgg[0]?.count ?? 0),
|
|
||||||
prompt_tokens_24h: Number(dayAgg[0]?.promptTokens ?? 0),
|
|
||||||
output_tokens_24h: Number(dayAgg[0]?.outputTokens ?? 0),
|
|
||||||
total_tokens_24h: Number(dayAgg[0]?.totalTokens ?? 0),
|
|
||||||
total_tokens_manad: Number(monthAgg[0]?.totalTokens ?? 0),
|
|
||||||
kostnad_usd_mikrocent_24h: Number(dayAgg[0]?.costMicrocents ?? 0),
|
|
||||||
andel_pa_aamos: 0.0,
|
|
||||||
aamos_agreement: null,
|
|
||||||
};
|
|
||||||
} catch {
|
|
||||||
enriched.gemini = geminiDefaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
enriched.wall &&
|
enriched.wall &&
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { sql } from "drizzle-orm";
|
import { gte, sql } from "drizzle-orm";
|
||||||
import type {
|
import type {
|
||||||
OpsAppBlock,
|
OpsAppBlock,
|
||||||
OpsActivationBlock,
|
OpsActivationBlock,
|
||||||
@@ -17,10 +17,12 @@ import type {
|
|||||||
OpsWallTile,
|
OpsWallTile,
|
||||||
OpsAlarm,
|
OpsAlarm,
|
||||||
OpsAlarmBlock,
|
OpsAlarmBlock,
|
||||||
|
OpsGeminiBlock,
|
||||||
} from "@app/shared-types";
|
} from "@app/shared-types";
|
||||||
import { SUBSCRIPTION_PLANS, type SubscriptionPlan } from "@app/shared-types";
|
import { SUBSCRIPTION_PLANS, type SubscriptionPlan } from "@app/shared-types";
|
||||||
import type { Database } from "./client.js";
|
import type { Database } from "./client.js";
|
||||||
import { schema } from "./index.js";
|
import { schema } from "./index.js";
|
||||||
|
import { geminiUsage } from "./schema/index.js";
|
||||||
|
|
||||||
const BUDGET_KEY_PREFIX = "gemini:daily:budget:";
|
const BUDGET_KEY_PREFIX = "gemini:daily:budget:";
|
||||||
|
|
||||||
@@ -652,6 +654,57 @@ async function feedbackBlock(db: Database): Promise<OpsFeedbackBlock> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function geminiBlock(db: Database): Promise<OpsGeminiBlock> {
|
||||||
|
const defaults: OpsGeminiBlock = {
|
||||||
|
anrop_24h: null,
|
||||||
|
prompt_tokens_24h: null,
|
||||||
|
output_tokens_24h: null,
|
||||||
|
total_tokens_24h: null,
|
||||||
|
total_tokens_manad: null,
|
||||||
|
kostnad_usd_mikrocent_24h: null,
|
||||||
|
andel_pa_aamos: 0.0,
|
||||||
|
aamos_agreement: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const now = new Date();
|
||||||
|
const since24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||||
|
|
||||||
|
const [dayAgg, monthAgg] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
count: sql<number>`count(*)`,
|
||||||
|
promptTokens: sql<number>`coalesce(sum(${geminiUsage.promptTokens}), 0)`,
|
||||||
|
outputTokens: sql<number>`coalesce(sum(${geminiUsage.outputTokens}), 0)`,
|
||||||
|
totalTokens: sql<number>`coalesce(sum(${geminiUsage.totalTokens}), 0)`,
|
||||||
|
costMicrocents: sql<number>`coalesce(sum(${geminiUsage.costMicrocents}), 0)`,
|
||||||
|
})
|
||||||
|
.from(geminiUsage)
|
||||||
|
.where(gte(geminiUsage.createdAt, since24h)),
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
totalTokens: sql<number>`coalesce(sum(${geminiUsage.totalTokens}), 0)`,
|
||||||
|
})
|
||||||
|
.from(geminiUsage)
|
||||||
|
.where(gte(geminiUsage.createdAt, startOfMonth)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
anrop_24h: Number(dayAgg[0]?.count ?? 0),
|
||||||
|
prompt_tokens_24h: Number(dayAgg[0]?.promptTokens ?? 0),
|
||||||
|
output_tokens_24h: Number(dayAgg[0]?.outputTokens ?? 0),
|
||||||
|
total_tokens_24h: Number(dayAgg[0]?.totalTokens ?? 0),
|
||||||
|
total_tokens_manad: Number(monthAgg[0]?.totalTokens ?? 0),
|
||||||
|
kostnad_usd_mikrocent_24h: Number(dayAgg[0]?.costMicrocents ?? 0),
|
||||||
|
andel_pa_aamos: 0.0,
|
||||||
|
aamos_agreement: null,
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function alarmBlock(budgetUsd: number, dailySpendUsd: number | null | undefined): OpsAlarmBlock {
|
function alarmBlock(budgetUsd: number, dailySpendUsd: number | null | undefined): OpsAlarmBlock {
|
||||||
const alarms: OpsAlarm[] = [];
|
const alarms: OpsAlarm[] = [];
|
||||||
if (budgetUsd > 0 && dailySpendUsd != null && dailySpendUsd >= budgetUsd) {
|
if (budgetUsd > 0 && dailySpendUsd != null && dailySpendUsd >= budgetUsd) {
|
||||||
@@ -669,7 +722,7 @@ function alarmBlock(budgetUsd: number, dailySpendUsd: number | null | undefined)
|
|||||||
export async function computeOpsSummary(options: ComputeOpsSummaryOptions): Promise<OpsSummary> {
|
export async function computeOpsSummary(options: ComputeOpsSummaryOptions): Promise<OpsSummary> {
|
||||||
const { db, budgetUsd, dailySpendUsd, queueSummary, planPrices: planPricesOverride } = options;
|
const { db, budgetUsd, dailySpendUsd, queueSummary, planPrices: planPricesOverride } = options;
|
||||||
const planPrices = { ...getPlanPrices(), ...(planPricesOverride ?? {}) };
|
const planPrices = { ...getPlanPrices(), ...(planPricesOverride ?? {}) };
|
||||||
const [app, economy, users, subscriptions, butik, feedbackData, ai, activation, engagement, payment, safety, larm] =
|
const [app, economy, users, subscriptions, butik, feedbackData, ai, activation, engagement, payment, safety, larm, gemini] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
Promise.resolve(appBlock()),
|
Promise.resolve(appBlock()),
|
||||||
economyBlock(db, planPrices),
|
economyBlock(db, planPrices),
|
||||||
@@ -683,6 +736,7 @@ export async function computeOpsSummary(options: ComputeOpsSummaryOptions): Prom
|
|||||||
paymentBlock(db),
|
paymentBlock(db),
|
||||||
safetyBlock(db),
|
safetyBlock(db),
|
||||||
Promise.resolve(alarmBlock(budgetUsd, dailySpendUsd)),
|
Promise.resolve(alarmBlock(budgetUsd, dailySpendUsd)),
|
||||||
|
geminiBlock(db),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const base: OpsSummary = {
|
const base: OpsSummary = {
|
||||||
@@ -700,6 +754,7 @@ export async function computeOpsSummary(options: ComputeOpsSummaryOptions): Prom
|
|||||||
sakerhet: safety,
|
sakerhet: safety,
|
||||||
larm,
|
larm,
|
||||||
wall: { boards: [] },
|
wall: { boards: [] },
|
||||||
|
gemini,
|
||||||
as_of: new Date().toISOString(),
|
as_of: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,17 @@ export interface OpsAlarmBlock {
|
|||||||
larm: OpsAlarm[];
|
larm: OpsAlarm[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OpsGeminiBlock {
|
||||||
|
anrop_24h: number | null;
|
||||||
|
prompt_tokens_24h: number | null;
|
||||||
|
output_tokens_24h: number | null;
|
||||||
|
total_tokens_24h: number | null;
|
||||||
|
total_tokens_manad: number | null;
|
||||||
|
kostnad_usd_mikrocent_24h: number | null;
|
||||||
|
andel_pa_aamos: number;
|
||||||
|
aamos_agreement: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface OpsSummary {
|
export interface OpsSummary {
|
||||||
app: OpsAppBlock;
|
app: OpsAppBlock;
|
||||||
ekonomi: OpsEconomyBlock;
|
ekonomi: OpsEconomyBlock;
|
||||||
@@ -142,5 +153,6 @@ export interface OpsSummary {
|
|||||||
sakerhet: OpsSafetyBlock;
|
sakerhet: OpsSafetyBlock;
|
||||||
larm: OpsAlarmBlock;
|
larm: OpsAlarmBlock;
|
||||||
wall: OpsWallBlock;
|
wall: OpsWallBlock;
|
||||||
|
gemini: OpsGeminiBlock;
|
||||||
as_of: string;
|
as_of: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user