feat: add case-level AI summary endpoint
POST /api/cases/:caseId/summarize combines extracted text from all of a case's documents and runs the existing AI analysis prompt, storing the result as a case-level analysis (document_id: null). Powers the "Özet Hazırla" button on the case detail screen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5ddf6ce0cf
commit
9f5c5c1e45
@@ -1,6 +1,7 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { supabase } from '../lib/supabase';
|
import { supabase } from '../lib/supabase';
|
||||||
import { AuthenticatedRequest } from '../middleware/auth';
|
import { AuthenticatedRequest } from '../middleware/auth';
|
||||||
|
import { runDocumentAnalysis } from './document.controller';
|
||||||
|
|
||||||
export const createCase = async (req: AuthenticatedRequest, res: Response) => {
|
export const createCase = async (req: AuthenticatedRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
@@ -26,6 +27,51 @@ export const createCase = async (req: AuthenticatedRequest, res: Response) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Dava dosyasındaki TÜM belgelerin çıkarılmış metnini birleştirip tek bir AI
|
||||||
|
// özeti üretir (Dosya Ayrıntı ekranındaki "Özet Hazırla" butonu). uploadDocument'ın
|
||||||
|
// aksine tek bir belgeye değil, dosyanın o anki tüm belgelerine bakıyor.
|
||||||
|
export const summarizeCase = async (req: AuthenticatedRequest, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { caseId } = req.params;
|
||||||
|
if (!caseId) {
|
||||||
|
return res.status(400).json({ error: 'Geçersiz caseId' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: docs, error: docsError } = await supabase
|
||||||
|
.from('documents')
|
||||||
|
.select('filename, extracted_text')
|
||||||
|
.eq('case_id', caseId)
|
||||||
|
.order('uploaded_at', { ascending: true });
|
||||||
|
if (docsError) throw docsError;
|
||||||
|
|
||||||
|
const withText = (docs || []).filter((d) => d.extracted_text && String(d.extracted_text).trim());
|
||||||
|
if (withText.length === 0) {
|
||||||
|
return res.status(400).json({ error: 'Bu dosyada metni çıkarılmış belge yok, özet hazırlanamıyor.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tüm belgeleri modele gönderiyoruz; runDocumentAnalysis toplamı ~15000 karaktere
|
||||||
|
// kırpıyor, o yüzden belge sayısına göre kabaca dengeli bir pay ayırıyoruz.
|
||||||
|
const perDocLimit = Math.max(1000, Math.floor(15000 / withText.length));
|
||||||
|
const combinedText = withText
|
||||||
|
.map((d) => `--- ${d.filename} ---\n${String(d.extracted_text).slice(0, perDocLimit)}`)
|
||||||
|
.join('\n\n');
|
||||||
|
|
||||||
|
const { summaryJson, aiModel } = await runDocumentAnalysis(combinedText);
|
||||||
|
|
||||||
|
const { data: analysisRecord, error: analysisError } = await supabase
|
||||||
|
.from('analyses')
|
||||||
|
.insert([{ case_id: caseId, document_id: null, summary_json: summaryJson, model_used: aiModel }])
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
if (analysisError) throw analysisError;
|
||||||
|
|
||||||
|
res.json({ analysis: analysisRecord });
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Summarize Case Error:', error);
|
||||||
|
res.status(500).json({ error: error.message || 'Internal Server Error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const deleteCase = async (req: AuthenticatedRequest, res: Response) => {
|
export const deleteCase = async (req: AuthenticatedRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { caseId } = req.params;
|
const { caseId } = req.params;
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ const ANALYSIS_SYSTEM_PROMPT = `
|
|||||||
Sadece ve sadece JSON döndür, başka hiçbir açıklama ekleme.
|
Sadece ve sadece JSON döndür, başka hiçbir açıklama ekleme.
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// uploadDocument ve registerLocalDocument'in ortak kullandığı AI analiz adımı:
|
// uploadDocument, registerLocalDocument ve summarizeCase'in (case.controller.ts)
|
||||||
// çıkarılmış metni modele gönderip yapılandırılmış özet JSON'u üretir.
|
// ortak kullandığı AI analiz adımı: çıkarılmış metni modele gönderip yapılandırılmış
|
||||||
async function runDocumentAnalysis(extractedText: string): Promise<{ summaryJson: any; aiModel: string }> {
|
// özet JSON'u üretir.
|
||||||
|
export async function runDocumentAnalysis(extractedText: string): Promise<{ summaryJson: any; aiModel: string }> {
|
||||||
const aiModel = process.env.AI_MODEL || 'mizan-fixed';
|
const aiModel = process.env.AI_MODEL || 'mizan-fixed';
|
||||||
const runpodUrl = process.env.RUNPOD_API_BASE_URL || 'https://q190env94stwis-11434.proxy.runpod.net';
|
const runpodUrl = process.env.RUNPOD_API_BASE_URL || 'https://q190env94stwis-11434.proxy.runpod.net';
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import { requireAuth } from '../middleware/auth';
|
import { requireAuth } from '../middleware/auth';
|
||||||
import { createCase, deleteCase } from '../controllers/case.controller';
|
import { createCase, deleteCase, summarizeCase } from '../controllers/case.controller';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/', requireAuth, createCase);
|
router.post('/', requireAuth, createCase);
|
||||||
router.delete('/:caseId', requireAuth, deleteCase);
|
router.delete('/:caseId', requireAuth, deleteCase);
|
||||||
|
router.post('/:caseId/summarize', requireAuth, summarizeCase);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
Reference in New Issue
Block a user