forked from admin/zeropost-engine
AI config migration to app_settings + ai_usage logging
* config/index.js: добавлен reloadAi() — асинхронно подтягивает значения из app_settings (категория ai_providers), мутирует config.ai in-place. Сервисы продолжают использовать config.ai.* синхронно. 3-уровневый fallback: app_settings.value → process.env (новое имя) → process.env (старое имя) → дефолт. * index.js: добавлен await config.reloadAi() в startup после migrate(). Добавлен middleware AsyncLocalStorage для проброса service/userId в AI-сервисы. Сервис определяется по URL-префиксу (zeropost-blog vs zeropost-tool). Подмонтирован роут /api/usage. * routes/settings.js: PUT и invalidate вызывают config.reloadAi() после изменения настройки категории ai_providers — горячая перезагрузка без рестарта PM2. * routes/usage.js (новый): GET /api/usage/summary?range&group_by — сводка расходов с разбивкой по сервису/провайдеру/модели. GET /api/usage/recent — последние вызовы. * lib/aiContext.js (новый): обёртка над AsyncLocalStorage для service/userId. * services/aiUsage.js (новый): log() с расчётом cost_rub по справочнику цен Anthropic/OpenAI (USD/1M токенов или USD/картинку) × markup × usd_rub. Никогда не бросает наружу — ошибки логирования не валят генерацию. * services/ai.js: chat() и image() обёрнуты в try/catch с aiUsage.log(). Все вышестоящие функции (generatePost, transformPost, generateTopics, generateArticle) идут через chat() — покрытие 100%. * services/covers.js: 3 call sites хукнуты (generateCoverViaResponses, generateCoverViaImagesEndpoint, generateCoverViaImageGenerations). Заодно поправлен process.env.AI_MODEL_IMAGE_VIA_RESPONSES → config.ai.imageModelViaResponses. Удалён мёртвый код после throw. * services/postImages.js: 1 call site (/responses + image_generation tool). Тот же fix env-ref. * services/articleAutoSeries.js: 1 call site (/messages, Anthropic-формат). usage.input_tokens / output_tokens корректно парсится. Verify: * Все 11 строк app_settings ai_providers заполнены значениями из .env. * Горячая перезагрузка: PUT /api/settings/admin/AI_TEXT_MODEL_POST -> runtime обновился без рестарта PM2. * Live test: ai.chat() через aiprimetech, 118+49 токенов, 0.0414 ₽ — запись в ai_usage с правильным service/provider/model. * GET /api/usage/summary возвращает корректные totals + breakdown.
This commit is contained in:
@@ -19,6 +19,7 @@ const scheduledPostsRoutes = require('./src/routes/scheduledPosts');
|
||||
const channelStatsRoutes = require('./src/routes/channelStats');
|
||||
const calendarRoutes = require('./src/routes/calendar');
|
||||
const metricsRoutes = require('./src/routes/metrics');
|
||||
const usageRoutes = require('./src/routes/usage');
|
||||
|
||||
// Start queue worker
|
||||
require('./src/workers/generation');
|
||||
@@ -46,6 +47,24 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
// AI usage context — приклеивает к каждому запросу service + user_id,
|
||||
// чтобы сервисы (ai.js, covers.js, postImages.js, articleAutoSeries.js)
|
||||
// логировали расход без явного проброса параметров.
|
||||
const aiContext = require('./src/lib/aiContext');
|
||||
app.use((req, res, next) => {
|
||||
let service = 'zeropost-other';
|
||||
// Блог-сторона zeropost.ru: статьи, серии, авто-публикация, темы.
|
||||
if (/^\/api\/(articles|autogen|series|notes|categories|stats|posts|scheduled-posts|generate)/.test(req.path)) {
|
||||
service = 'zeropost-blog';
|
||||
// SaaS-сторона app.zeropost.ru: пользовательские посты, каналы, календарь, аналитика.
|
||||
} else if (/^\/api\/(user-posts|calendar|channels|channel-stats|metrics|photo-search)/.test(req.path)) {
|
||||
service = 'zeropost-tool';
|
||||
}
|
||||
const userIdRaw = req.headers['x-user-id'];
|
||||
const userId = userIdRaw ? parseInt(userIdRaw, 10) || null : null;
|
||||
aiContext.run({ service, userId }, () => next());
|
||||
});
|
||||
|
||||
app.use('/api/generate', generateRoutes);
|
||||
app.use('/api/channels', channelsRoutes);
|
||||
app.use('/api/posts', postsRoutes);
|
||||
@@ -62,6 +81,7 @@ app.use('/api/scheduled-posts', scheduledPostsRoutes);
|
||||
app.use('/api/channel-stats', channelStatsRoutes);
|
||||
app.use('/api/calendar', calendarRoutes);
|
||||
app.use('/api/metrics', metricsRoutes);
|
||||
app.use('/api/usage', usageRoutes);
|
||||
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ ok: true, service: 'zeropost-engine', time: new Date() });
|
||||
@@ -69,6 +89,8 @@ app.get('/health', (req, res) => {
|
||||
|
||||
const start = async () => {
|
||||
await migrate();
|
||||
await config.reloadAi();
|
||||
console.log('[Engine] AI config loaded from app_settings: text=' + config.ai.baseUrl + ', images=' + config.ai.imageBaseUrl);
|
||||
app.listen(config.port, () => {
|
||||
console.log(`[Engine] Running on port ${config.port}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user