feath: save questions generated with AI
This commit is contained in:
@@ -615,9 +615,10 @@ pub struct ApplyTemplatePayload {
|
|||||||
|
|
||||||
// ==================== RAG Question Generation ====================
|
// ==================== RAG Question Generation ====================
|
||||||
|
|
||||||
/// POST /api/test-templates/generate-with-rag - Generate questions using RAG from MySQL question bank
|
/// POST /test-templates/generate-with-rag - Generate questions using RAG from MySQL question bank
|
||||||
pub async fn generate_questions_with_rag(
|
pub async fn generate_questions_with_rag(
|
||||||
Org(org_ctx): Org,
|
Org(org_ctx): Org,
|
||||||
|
claims: Claims,
|
||||||
State(pool): State<PgPool>,
|
State(pool): State<PgPool>,
|
||||||
Json(payload): Json<RagGenerationPayload>,
|
Json(payload): Json<RagGenerationPayload>,
|
||||||
) -> Result<Json<Vec<TestTemplateQuestion>>, (StatusCode, String)> {
|
) -> Result<Json<Vec<TestTemplateQuestion>>, (StatusCode, String)> {
|
||||||
@@ -701,6 +702,10 @@ pub async fn generate_questions_with_rag(
|
|||||||
|
|
||||||
tracing::info!("Calling Ollama at {} with model {}", url, model);
|
tracing::info!("Calling Ollama at {} with model {}", url, model);
|
||||||
|
|
||||||
|
// Save topic for later use
|
||||||
|
let topic = payload.topic.clone().unwrap_or_else(|| "English grammar".to_string());
|
||||||
|
let num_questions = payload.num_questions.unwrap_or(5);
|
||||||
|
|
||||||
// Simplified system prompt for better performance on slower machines
|
// Simplified system prompt for better performance on slower machines
|
||||||
let system_prompt = format!(
|
let system_prompt = format!(
|
||||||
r#"You are an English Teacher creating quiz questions.
|
r#"You are an English Teacher creating quiz questions.
|
||||||
@@ -725,8 +730,8 @@ pub async fn generate_questions_with_rag(
|
|||||||
|
|
||||||
Skills: reading, listening, speaking, writing. Distribute across all 4."#,
|
Skills: reading, listening, speaking, writing. Distribute across all 4."#,
|
||||||
rag_context,
|
rag_context,
|
||||||
payload.num_questions.unwrap_or(5),
|
num_questions,
|
||||||
payload.topic.unwrap_or_else(|| "English grammar".to_string())
|
topic
|
||||||
);
|
);
|
||||||
|
|
||||||
tracing::debug!("System prompt length: {} chars", system_prompt.len());
|
tracing::debug!("System prompt length: {} chars", system_prompt.len());
|
||||||
@@ -811,9 +816,59 @@ pub async fn generate_questions_with_rag(
|
|||||||
if generated_questions.is_empty() {
|
if generated_questions.is_empty() {
|
||||||
return Err((StatusCode::INTERNAL_SERVER_ERROR, "AI failed to generate questions".to_string()));
|
return Err((StatusCode::INTERNAL_SERVER_ERROR, "AI failed to generate questions".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("Generated {} questions using RAG from MySQL bank", generated_questions.len());
|
// Save generated questions to question bank
|
||||||
|
let mut saved_count = 0;
|
||||||
|
for question in &generated_questions {
|
||||||
|
let question_type = match question.question_type.as_str() {
|
||||||
|
"true-false" => common::models::QuestionBankType::TrueFalse,
|
||||||
|
"short-answer" => common::models::QuestionBankType::ShortAnswer,
|
||||||
|
"essay" => common::models::QuestionBankType::Essay,
|
||||||
|
"matching" => common::models::QuestionBankType::Matching,
|
||||||
|
"ordering" => common::models::QuestionBankType::Ordering,
|
||||||
|
_ => common::models::QuestionBankType::MultipleChoice,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = sqlx::query(
|
||||||
|
r#"
|
||||||
|
INSERT INTO question_bank (
|
||||||
|
organization_id, created_by, question_text, question_type,
|
||||||
|
options, correct_answer, explanation, points, difficulty,
|
||||||
|
source, source_metadata, is_active
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, true)
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.bind(org_ctx.id)
|
||||||
|
.bind(claims.sub)
|
||||||
|
.bind(&question.question_text)
|
||||||
|
.bind(&question_type)
|
||||||
|
.bind(&question.options)
|
||||||
|
.bind(&question.correct_answer)
|
||||||
|
.bind(&question.explanation)
|
||||||
|
.bind(question.points)
|
||||||
|
.bind("medium")
|
||||||
|
.bind("rag-ai")
|
||||||
|
.bind(&json!({
|
||||||
|
"generated_by": "rag-ai",
|
||||||
|
"source": "mysql-bank",
|
||||||
|
"topic": topic,
|
||||||
|
"generated_at": chrono::Utc::now().to_rfc3339(),
|
||||||
|
}))
|
||||||
|
.execute(&pool)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if result.is_ok() {
|
||||||
|
saved_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
"Generated {} questions using RAG from MySQL bank, saved {} to question bank",
|
||||||
|
generated_questions.len(),
|
||||||
|
saved_count
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Json(generated_questions))
|
Ok(Json(generated_questions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -328,14 +328,14 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
|
|||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||||
>
|
>
|
||||||
<option value="beginner">Beginner</option>
|
<option value="beginner">Beginner</option>
|
||||||
<option value="beginner_1">Beginner 1</option>
|
<option value="beginner1">Beginner 1</option>
|
||||||
<option value="beginner_2">Beginner 2</option>
|
<option value="beginner2">Beginner 2</option>
|
||||||
<option value="intermediate">Intermediate</option>
|
<option value="intermediate">Intermediate</option>
|
||||||
<option value="intermediate_1">Intermediate 1</option>
|
<option value="intermediate1">Intermediate 1</option>
|
||||||
<option value="intermediate_2">Intermediate 2</option>
|
<option value="intermediate2">Intermediate 2</option>
|
||||||
<option value="advanced">Advanced</option>
|
<option value="advanced">Advanced</option>
|
||||||
<option value="advanced_1">Advanced 1</option>
|
<option value="advanced1">Advanced 1</option>
|
||||||
<option value="advanced_2">Advanced 2</option>
|
<option value="advanced2">Advanced 2</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -90,14 +90,14 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
const getLevelLabel = (level: CourseLevel) => {
|
const getLevelLabel = (level: CourseLevel) => {
|
||||||
const labels: Record<CourseLevel, string> = {
|
const labels: Record<CourseLevel, string> = {
|
||||||
beginner: 'Beginner',
|
beginner: 'Beginner',
|
||||||
beginner_1: 'Beginner 1',
|
beginner1: 'Beginner 1',
|
||||||
beginner_2: 'Beginner 2',
|
beginner2: 'Beginner 2',
|
||||||
intermediate: 'Intermediate',
|
intermediate: 'Intermediate',
|
||||||
intermediate_1: 'Intermediate 1',
|
intermediate1: 'Intermediate 1',
|
||||||
intermediate_2: 'Intermediate 2',
|
intermediate2: 'Intermediate 2',
|
||||||
advanced: 'Advanced',
|
advanced: 'Advanced',
|
||||||
advanced_1: 'Advanced 1',
|
advanced1: 'Advanced 1',
|
||||||
advanced_2: 'Advanced 2',
|
advanced2: 'Advanced 2',
|
||||||
};
|
};
|
||||||
return labels[level] || level;
|
return labels[level] || level;
|
||||||
};
|
};
|
||||||
@@ -185,14 +185,14 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
>
|
>
|
||||||
<option value="">Todos los niveles</option>
|
<option value="">Todos los niveles</option>
|
||||||
<option value="beginner">Beginner</option>
|
<option value="beginner">Beginner</option>
|
||||||
<option value="beginner_1">Beginner 1</option>
|
<option value="beginner1">Beginner 1</option>
|
||||||
<option value="beginner_2">Beginner 2</option>
|
<option value="beginner2">Beginner 2</option>
|
||||||
<option value="intermediate">Intermediate</option>
|
<option value="intermediate">Intermediate</option>
|
||||||
<option value="intermediate_1">Intermediate 1</option>
|
<option value="intermediate1">Intermediate 1</option>
|
||||||
<option value="intermediate_2">Intermediate 2</option>
|
<option value="intermediate2">Intermediate 2</option>
|
||||||
<option value="advanced">Advanced</option>
|
<option value="advanced">Advanced</option>
|
||||||
<option value="advanced_1">Advanced 1</option>
|
<option value="advanced1">Advanced 1</option>
|
||||||
<option value="advanced_2">Advanced 2</option>
|
<option value="advanced2">Advanced 2</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1135,7 +1135,7 @@ export interface BackgroundTask {
|
|||||||
|
|
||||||
// ==================== Test Templates ====================
|
// ==================== Test Templates ====================
|
||||||
|
|
||||||
export type CourseLevel = 'beginner' | 'beginner_1' | 'beginner_2' | 'intermediate' | 'intermediate_1' | 'intermediate_2' | 'advanced' | 'advanced_1' | 'advanced_2';
|
export type CourseLevel = 'beginner' | 'beginner1' | 'beginner2' | 'intermediate' | 'intermediate1' | 'intermediate2' | 'advanced' | 'advanced1' | 'advanced2';
|
||||||
export type CourseType = 'intensive' | 'regular';
|
export type CourseType = 'intensive' | 'regular';
|
||||||
export type TestType = 'CA' | 'MWT' | 'MOT' | 'FOT' | 'FWT';
|
export type TestType = 'CA' | 'MWT' | 'MOT' | 'FOT' | 'FWT';
|
||||||
export type QuestionType = 'multiple-choice' | 'true-false' | 'short-answer' | 'essay' | 'matching' | 'ordering';
|
export type QuestionType = 'multiple-choice' | 'true-false' | 'short-answer' | 'essay' | 'matching' | 'ordering';
|
||||||
|
|||||||
Reference in New Issue
Block a user