fix(quotas): unifier le décompte IA (BYOK, rollback) et combler les fuites
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m55s
CI / Deploy production (on server) (push) Successful in 36s

Centralise la réserve via ai-quota, corrige admin unavailable (-1), brancher les routes sans quota et le host-pays brainstorm, avec usage-meter élargi, noms de clusters, MCP et ajustements dashboard/insights.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-07-15 20:42:25 +00:00
parent 30da592ba2
commit 4fe31ebc99
75 changed files with 2949 additions and 785 deletions

View File

@@ -10,6 +10,7 @@ import { clusteringService } from './clustering.service'
import { getChatProvider } from '@/lib/ai/factory'
import { getSystemConfig } from '@/lib/config'
import { calculateNextRun } from '@/lib/agents/schedule'
import { withAiQuota } from '@/lib/ai-quota'
const MIN_CLUSTER_NOTES = 3
const MAX_SUGGESTIONS_PER_RUN = 3
@@ -51,6 +52,7 @@ function clusterCoveredByAgent(
}
async function buildSuggestionWithLlm(
userId: string,
topic: string,
noteTitles: string[],
noteCount: number,
@@ -63,10 +65,6 @@ async function buildSuggestionWithLlm(
}
try {
const config = await getSystemConfig()
const provider = getChatProvider(config)
if (!provider) return fallback
const prompt = `Tu proposes un agent IA pour un second cerveau (prise de notes).
Thème détecté: "${topic}"
Notes liées (titres): ${noteTitles.slice(0, 5).join(' | ') || 'sans titre'}
@@ -75,7 +73,14 @@ Nombre de notes: ${noteCount}
Retourne UNIQUEMENT du JSON valide:
{"reason":"1 phrase pourquoi un agent est utile","suggestedRole":"prompt système de l'agent (2-3 phrases, français)","suggestedType":"researcher|monitor","suggestedFrequency":"daily|weekly"}`
const raw = await provider.generateText(prompt)
const raw = await withAiQuota(userId, 'reformulate', async () => {
const config = await getSystemConfig()
const provider = getChatProvider(config)
if (!provider) return null
return provider.generateText(prompt)
}, { lane: 'chat' })
if (!raw) return fallback
const match = raw.match(/\{[\s\S]*\}/)
if (!match) return fallback
const parsed = JSON.parse(match[0])
@@ -122,6 +127,7 @@ export class AgentSuggestionService {
select: { title: true },
})
const llm = await buildSuggestionWithLlm(
userId,
topic,
notes.map(n => n.title || 'Sans titre'),
cluster.noteIds.length,