092_UI_core / src /common /clients /fastapi.client.ts
quachtiensinh27
fix: Use form-data package with Readable stream for PDF uploads
5be1f4c
Raw
History Blame Contribute Delete
3.12 kB
import axios, { AxiosInstance } from 'axios';
import { Logger } from '@nestjs/common';
import FormData from 'form-data';
import { Readable } from 'stream';
export interface IndexPdfResponse {
pdf_name: string;
chunks_indexed: number;
conversation_id: string;
}
export interface FastApiQuizResponse {
title?: string;
description?: string;
passing_score?: number;
questions: Array<{
order: number;
topic: string;
difficulty: 'easy' | 'medium' | 'hard';
question_text: string;
options: Array<{ id: string; text: string }>;
correct_answer: string;
explanation?: string;
}>;
metadata?: Record<string, unknown>;
}
export interface AtRiskAnalysisResponse {
students: Array<{
sender_id: string;
sender_name: string;
signals?: {
stuck_phrases?: Array<{ text: string; suggested_points: number }>;
frustration_phrases?: Array<{ text: string; suggested_points: number }>;
unanswered_questions?: Array<{ text: string; suggested_points: number }>;
};
}>;
}
export class FastApiClient {
private readonly client: AxiosInstance;
constructor(logger: Logger, fastApiUrl?: string, private readonly defaultTimeout = 60000) {
const baseUrl = fastApiUrl || process.env.FASTAPI_URL || 'http://localhost:8000/api/v1';
this.client = axios.create({
baseURL: baseUrl,
timeout: this.defaultTimeout,
});
this.logger = logger;
}
private readonly logger: Logger;
async genQuiz(content: string, kQuestion: number): Promise<FastApiQuizResponse> {
this.logger.log(`[FastAPI] genQuiz called: content=${content.length} chars, ${kQuestion} questions`);
try {
const response = await this.client.post('/gen_quiz', {
content,
k_question: kQuestion,
});
this.logger.log(`[FastAPI] genQuiz response: ${JSON.stringify({ hasData: !!response.data, keys: Object.keys(response.data || {}) })}`);
return response.data;
} catch (error: unknown) {
const axiosError = error as { response?: { status?: number; data?: unknown }; message?: string };
this.logger.error(
`[FastAPI] genQuiz failed: ${axiosError.response?.status || 'N/A'} ${JSON.stringify(axiosError.response?.data || {})}`,
axiosError.message || 'Unknown error'
);
throw error;
}
}
async analyzeAtRisk(roomIds: string[], hours: number): Promise<AtRiskAnalysisResponse> {
const response = await this.client.post('/at-risk/analyze', {
room_ids: roomIds,
hours,
});
return response.data;
}
/**
* Index PDF vào Qdrant knowledge base
*/
async indexPdf(conversationId: string, file: Buffer, filename: string): Promise<IndexPdfResponse> {
const form = new FormData();
form.append('conversation_id', conversationId);
// Use form-data package with Readable stream for proper file upload
form.append('file', Readable.from(file), {
filename,
contentType: 'application/pdf'
});
const response = await this.client.post('/index_pdf', form, {
headers: form.getHeaders()
});
return response.data;
}
}