| 88 | } |
| 89 | |
| 90 | class PgCache implements PromptCache { |
| 91 | async get(key: string): Promise<CacheEntry | null> { |
| 92 | // Lazy import — keeps tests that don't exercise PG mode free of pg dependency. |
| 93 | // eslint-disable-next-line @typescript-eslint/no-require-imports |
| 94 | const { query } = require("../db") as { query: (sql: string, args: unknown[]) => Promise<Record<string, unknown>[]> }; |
| 95 | const rows = await query( |
| 96 | `UPDATE prompt_cache SET hits = hits + 1, last_hit_at = NOW() |
| 97 | WHERE cache_key = $1 AND expires_at > NOW() RETURNING *`, |
| 98 | [key], |
| 99 | ); |
| 100 | if (rows.length === 0) return null; |
| 101 | const r = rows[0] as { output_value: unknown; prompt_tokens: number | null; completion_tokens: number | null; cost_usd: string | null; created_at: Date; expires_at: Date }; |
| 102 | return { |
| 103 | value: r.output_value, |
| 104 | prompt_tokens: r.prompt_tokens, |
| 105 | completion_tokens: r.completion_tokens, |
| 106 | cost_usd: r.cost_usd === null ? null : Number(r.cost_usd), |
| 107 | cached_at: new Date(r.created_at).getTime(), |
| 108 | expires_at: new Date(r.expires_at).getTime(), |
| 109 | }; |
| 110 | } |
| 111 | |
| 112 | async put(key: string, prompt_name: string, model_id: string, value: unknown, ttl_ms: number, usage: { prompt_tokens?: number; completion_tokens?: number; cost_usd?: number }): Promise<void> { |
| 113 | // eslint-disable-next-line @typescript-eslint/no-require-imports |
| 114 | const { query } = require("../db") as { query: (sql: string, args: unknown[]) => Promise<unknown> }; |
| 115 | await query( |
| 116 | `INSERT INTO prompt_cache (cache_key, prompt_name, model_id, output_value, prompt_tokens, completion_tokens, cost_usd, expires_at) |
| 117 | VALUES ($1, $2, $3, $4, $5, $6, $7, NOW() + ($8 || ' milliseconds')::interval) |
| 118 | ON CONFLICT (cache_key) DO UPDATE SET |
| 119 | output_value = EXCLUDED.output_value, |
| 120 | expires_at = EXCLUDED.expires_at, |
| 121 | last_hit_at = NULL, |
| 122 | hits = 0`, |
| 123 | [key, prompt_name, model_id, JSON.stringify(value), usage.prompt_tokens ?? null, usage.completion_tokens ?? null, usage.cost_usd ?? null, ttl_ms], |
| 124 | ); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | function chooseBackend(): PromptCache { |
| 129 | const mode = process.env.LLM_CACHE_MODE || "memory"; |
nothing calls this directly
no outgoing calls
no test coverage detected