diff --git a/apps/api/src/routes/auth.ts b/apps/api/src/routes/auth.ts index 35c43d9..38ea7b4 100644 --- a/apps/api/src/routes/auth.ts +++ b/apps/api/src/routes/auth.ts @@ -232,7 +232,7 @@ export async function authRoutes(app: FastifyInstance) { // Trial startas först när e-posten är verifierad. En trial per verifierad e-post. const [existingTrial] = await app.db - .select({ id: schema.trials.id }) + .select({ userId: schema.trials.userId }) .from(schema.trials) .where(eq(schema.trials.userId, stored.userId)) .limit(1); diff --git a/packages/ai-contracts/src/gemini.ts b/packages/ai-contracts/src/gemini.ts index 3c4d137..a62ca2d 100644 --- a/packages/ai-contracts/src/gemini.ts +++ b/packages/ai-contracts/src/gemini.ts @@ -326,12 +326,21 @@ export class GeminiAamosClient implements AamosClient { const locale = options.localeContext ?? this.defaultLocale(); const started = Date.now(); + const imageCount = Math.min(input.imageUrls.length, 6); + const estimatedCostUsd = imageCount * COST_ESTIMATE_PER_IMAGE_USD; + if (await this.isOverBudget(estimatedCostUsd)) { + return { + status: "failed", + output: null, + error: "Global Gemini-dagsbudget är förbrukad.", + }; + } + const imageParts = await Promise.all( input.imageUrls.slice(0, 6).map((url) => this.fetchOneImage(url, this.cfg.timeoutMs)), ); - const estimatedCostUsd = imageParts.length * COST_ESTIMATE_PER_IMAGE_USD; - if (await this.isOverBudget(estimatedCostUsd)) { + if (imageParts.length === 0) { return { status: "failed", output: null, @@ -416,8 +425,8 @@ export class GeminiAamosClient implements AamosClient { const locale = options.localeContext ?? this.defaultLocale(); const started = Date.now(); - const imageParts = await this.fetchImageParts(imageUrls, this.cfg.timeoutMs); - const estimatedCostUsd = imageParts.length * costEstimatePerImage; + const imageCount = Math.min(imageUrls.length, 6); + const estimatedCostUsd = imageCount * costEstimatePerImage; if (await this.isOverBudget(estimatedCostUsd)) { return { status: "failed", @@ -426,6 +435,15 @@ export class GeminiAamosClient implements AamosClient { } as AamosResult; } + const imageParts = await this.fetchImageParts(imageUrls, this.cfg.timeoutMs); + if (imageParts.length === 0) { + return { + status: "failed", + output: null, + error: "Global Gemini-dagsbudget är förbrukad.", + } as AamosResult; + } + const payload = { contents: [{ parts: [{ text: prompt }, ...imageParts] }], generationConfig: { diff --git a/packages/database/src/ops-summary.ts b/packages/database/src/ops-summary.ts index 816887b..7c16ea9 100644 --- a/packages/database/src/ops-summary.ts +++ b/packages/database/src/ops-summary.ts @@ -15,6 +15,8 @@ import type { OpsFeedbackBlock, OpsWallBlock, OpsWallTile, + OpsAlarm, + OpsAlarmBlock, } from "@app/shared-types"; import { SUBSCRIPTION_PLANS, type SubscriptionPlan } from "@app/shared-types"; import type { Database } from "./client.js"; diff --git a/packages/database/test/ops-wall.test.ts b/packages/database/test/ops-wall.test.ts index 847beec..dafda56 100644 --- a/packages/database/test/ops-wall.test.ts +++ b/packages/database/test/ops-wall.test.ts @@ -139,13 +139,14 @@ describe("buildWall", () => { expect(brott.tone).toBe("warn"); }); - it("alla sex tiles finns i rätt ordning", () => { + it("alla sju tiles finns i rätt ordning", () => { const tiles = buildWall(baseSummary()).boards[0]!.tiles; expect(tiles.map((t) => t.label)).toEqual([ "MRR", "Aktiva nu", "Betalande", "Trial-konv", + "AI-budget", "Workers", "Allergen-brott", ]);