All files / src/providers logging.ts

100% Statements 47/47
88.88% Branches 8/9
100% Functions 6/6
100% Lines 47/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75                                          1x 1x 1x 1x   1x 9x 9x 9x   1x 5x 5x 5x 5x 5x 5x   1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x   1x 8x 8x 8x 3x 3x 3x 3x 3x 8x 1x  
import type {
  ModelProvider,
  ModelTier,
  AIInteraction,
  WorkoutGenerationContext,
  WorkoutAdaptationContext,
  ReviewContext,
  NarrativeContext,
  WorkoutPlan,
  AdaptedWorkout,
  ReviewPrompt,
} from '../model-provider.js';
 
/**
 * LoggingModelProvider is a decorator that wraps any ModelProvider and
 * persists AIInteraction records after each call.
 *
 * Persist errors are non-fatal: a warning is logged and the result is
 * returned normally. This ensures the AI call never fails due to
 * logging infrastructure issues.
 */
export class LoggingModelProvider implements ModelProvider {
  get tier(): ModelTier {
    return this.inner.tier;
  }
 
  constructor(
    private readonly inner: ModelProvider,
    private readonly persist: (interaction: AIInteraction) => Promise<void>,
  ) {}
 
  async generateWorkout(
    context: WorkoutGenerationContext,
  ): Promise<{ result: WorkoutPlan; interaction: AIInteraction }> {
    const { result, interaction } = await this.inner.generateWorkout(context);
    await this.#persistSafely(interaction);
    return { result, interaction };
  }
 
  async adaptWorkout(
    context: WorkoutAdaptationContext,
  ): Promise<{ result: AdaptedWorkout; interaction: AIInteraction }> {
    const { result, interaction } = await this.inner.adaptWorkout(context);
    await this.#persistSafely(interaction);
    return { result, interaction };
  }
 
  async generateReviewQuestions(
    context: ReviewContext,
  ): Promise<{ result: ReviewPrompt[]; interaction: AIInteraction }> {
    const { result, interaction } = await this.inner.generateReviewQuestions(context);
    await this.#persistSafely(interaction);
    return { result, interaction };
  }
 
  async generateNarrative(
    context: NarrativeContext,
  ): Promise<{ result: string; interaction: AIInteraction }> {
    const { result, interaction } = await this.inner.generateNarrative(context);
    await this.#persistSafely(interaction);
    return { result, interaction };
  }
 
  async #persistSafely(interaction: AIInteraction): Promise<void> {
    try {
      await this.persist(interaction);
    } catch (err) {
      console.warn(
        '[LoggingModelProvider] Failed to persist AIInteraction:',
        err instanceof Error ? err.message : err,
      );
    }
  }
}