feat: phase1 platform upgrade with moderation, dev payments, admin panel and landing updates

This commit is contained in:
root
2026-02-06 17:26:53 +00:00
parent 4ca66ea896
commit 979adb9d3d
54 changed files with 2687 additions and 318 deletions

View File

@ -137,53 +137,50 @@ export class OpenRouterProvider {
model: string
): Promise<ClarifyingQuestions> {
log.request('generateClarifyingQuestions', model);
log.info(`User prompt: "${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}"`);
const systemPrompt = `Ты - эксперт по созданию образовательных курсов.
Пользователь хочет создать курс. Твоя задача - задать уточняющие вопросы,
чтобы лучше понять его потребности и создать максимально релевантный курс.
log.info(`Using structured onboarding quiz for prompt: "${prompt.substring(0, 120)}${prompt.length > 120 ? '...' : ''}"`);
Сгенерируй 3-5 вопросов. Обязательно включи вопрос про объём курса (важно для длины):
- Короткий (3-4 главы, по 2-4 урока — только введение в тему)
- Средний (5-7 глав, по 4-6 уроков — полноценное покрытие)
- Длинный / полный (7-12 глав, по 5-8 уроков — глубокое погружение, рекомендуемый вариант для серьёзного обучения)
const structured = {
questions: [
{
id: 'q_audience',
question: 'Для кого курс?',
type: 'single_choice',
options: ['Новички', 'Middle', 'Продвинутые'],
required: true,
},
{
id: 'q_format',
question: 'Формат курса?',
type: 'single_choice',
options: ['Теория', 'Практика', 'Смешанный'],
required: true,
},
{
id: 'q_goal',
question: 'Основная цель курса?',
type: 'single_choice',
options: ['Освоить профессию', 'Подготовиться к экзамену', 'Для себя'],
required: true,
},
{
id: 'q_volume',
question: 'Какой объём курса нужен?',
type: 'single_choice',
options: ['Короткий (3-4 главы)', 'Средний (5-7 глав)', 'Полный (7-12 глав)'],
required: true,
},
{
id: 'q_notes',
question: 'Есть ли дополнительные пожелания по структуре, заданиям и кейсам?',
type: 'text',
required: false,
},
],
};
Остальные вопросы: целевая аудитория, глубина материала, специфические темы.
Ответь в формате JSON.`;
return this.withRetry(async () => {
const response = await this.client.chat.completions.create({
model,
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: `Запрос пользователя: "${prompt}"` },
],
response_format: { type: 'json_object' },
temperature: 0.7,
});
log.response('generateClarifyingQuestions', {
prompt: response.usage?.prompt_tokens,
completion: response.usage?.completion_tokens,
});
const content = response.choices[0].message.content;
log.debug('Raw AI response:', content);
if (!content) {
log.error('Empty response from AI');
throw new Error('Empty response from AI');
}
const parsed = JSON.parse(content);
const validated = ClarifyingQuestionsSchema.parse(parsed);
log.success(`Generated ${validated.questions.length} clarifying questions`);
log.info('Questions:', validated.questions.map(q => q.question));
return validated;
}, 'generateClarifyingQuestions');
const validated = ClarifyingQuestionsSchema.parse(structured);
log.success(`Generated ${validated.questions.length} structured onboarding questions`);
return validated;
}
async generateCourseOutline(
@ -225,9 +222,22 @@ export class OpenRouterProvider {
"tags": ["тег1", "тег2"]
}`;
const audience = String(answers.q_audience || '').trim();
const format = String(answers.q_format || '').trim();
const goal = String(answers.q_goal || '').trim();
const volume = String(answers.q_volume || '').trim();
const notes = String(answers.q_notes || '').trim();
const userMessage = `Запрос: "${prompt}"
Ответы пользователя на уточняющие вопросы:
Структурированные ответы:
- Аудитория: ${audience || 'не указано'}
- Формат: ${format || 'не указано'}
- Цель: ${goal || 'не указано'}
- Объём: ${volume || 'не указано'}
- Доп. пожелания: ${notes || 'нет'}
Сырой набор ответов:
${Object.entries(answers)
.map(([key, value]) => `- ${key}: ${Array.isArray(value) ? value.join(', ') : value}`)
.join('\n')}`;