Files
OpenSquawk/server/models/UsageEvent.ts
leubeem 58f9bfad4c ci: make typecheck a real blocking gate; bump actions to Node 24 majors
The previous `vue-tsc --noEmit` step was a no-op: the root tsconfig uses
`files: []` with project references, so without `--build` it checks zero files
and always passes. Switch to `vue-tsc --build` (new `yarn typecheck` script)
and make the job blocking.

Fix the one error this surfaced: UsageEventDocument extended mongoose.Document,
whose `model` method collides with the `model: string` field. Use the
recommended pattern — a plain attrs interface passed to the Schema/Model
generics (hydrated docs still expose Document methods). Typecheck is now clean.

Bump actions/checkout@v5 and actions/setup-node@v5 to silence the Node.js 20
runtime deprecation (forced to Node 24 from 2026-06-16).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:28:27 +02:00

50 lines
1.8 KiB
TypeScript

import mongoose from 'mongoose'
const { Schema } = mongoose
export type UsageKind = 'stt' | 'tts' | 'llm'
export type UsageProvider = 'openai' | 'speaches' | 'piper' | 'cache'
// Plain attribute shape (not extending mongoose.Document). Extending Document
// collides on `model` (Document.model is a method); passing the attrs type to
// the Schema/Model generics is the recommended pattern and still yields
// hydrated documents with all Document methods from queries/create.
export interface UsageEventAttrs {
user?: mongoose.Types.ObjectId
sessionId?: string
kind: UsageKind
provider: UsageProvider
model: string
endpoint: string
/** Audio length for STT in seconds */
audioSeconds?: number
/** Synthesized text length for TTS */
characters?: number
inputTokens?: number
outputTokens?: number
/** Estimated cost in USD (0 for self-hosted providers and cache hits) */
costUsd: number
createdAt: Date
}
const usageEventSchema = new mongoose.Schema<UsageEventAttrs>({
user: { type: Schema.Types.ObjectId, ref: 'User', index: true },
sessionId: { type: String },
kind: { type: String, enum: ['stt', 'tts', 'llm'], required: true },
provider: { type: String, enum: ['openai', 'speaches', 'piper', 'cache'], required: true },
model: { type: String, required: true },
endpoint: { type: String, required: true },
audioSeconds: { type: Number },
characters: { type: Number },
inputTokens: { type: Number },
outputTokens: { type: Number },
costUsd: { type: Number, required: true, default: 0 },
createdAt: { type: Date, default: () => new Date(), index: true },
})
usageEventSchema.index({ user: 1, createdAt: -1 })
export const UsageEvent =
(mongoose.models.UsageEvent as mongoose.Model<UsageEventAttrs> | undefined) ||
mongoose.model<UsageEventAttrs>('UsageEvent', usageEventSchema)