Files
neta/app/api/chat/route.ts
T

155 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { streamText, tool } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { z } from 'zod';
import { createClient } from '@/lib/supabase/server';
export const maxDuration = 30; // Allow longer execution for tool calls
export async function POST(req: Request) {
try {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return new Response('Yetkisiz erişim', { status: 401 });
}
const { messages, sessionId, provider: clientProvider, apiKey: clientApiKey } = await req.json();
// Read App Settings for Provider/API Key
const { data: appSettings } = await supabase
.from("app_settings")
.select("*")
.eq("user_id", user.id)
.single();
const provider = clientProvider || appSettings?.ai_provider || "openai";
const apiKey = clientApiKey || appSettings?.api_key || "";
let model;
if (provider === 'gemini') {
const google = createGoogleGenerativeAI({ apiKey });
model = google('gemini-1.5-pro-latest');
} else if (provider === 'groq') {
const groq = createOpenAI({ apiKey, baseURL: 'https://api.groq.com/openai/v1' });
model = groq('llama-3.1-8b-instant');
} else {
const openai = createOpenAI({ apiKey });
model = openai('gpt-4o');
}
// Identify the latest user message to save to Supabase
const latestMessage = messages[messages.length - 1];
if (sessionId && latestMessage && latestMessage.role === 'user') {
// Sadece metin varsa kaydediyoruz
if (latestMessage.content) {
await supabase.from("chat_messages").insert({
session_id: sessionId,
role: "user",
content: latestMessage.content,
});
}
}
const systemPrompt = `Sen kullanıcının kişisel Freelancer İş Asistanı ve Danışmanısın. Cognis Freelancer OS içinde yaşıyorsun.
Kullanıcının iş süreçlerini, projelerini ve finansal durumunu organize etmesine yardımcı oluyorsun.
Gerektiğinde araçları (tools) kullanarak sistemden güncel verileri çek ve doğrudan veri ekle.
Aşağıdaki yeteneklere sahipsin:
- Finansal verileri listeleyebilir ve yeni finans kaydı (gelir/gider) girebilirsin.
- Görevleri okuyabilir ve yeni görevler ekleyebilirsin.
- Projeleri sorgulayabilir ve projelerin detaylarını/tasarım sistemini çekebilirsin.
Kullanıcıya her zaman proaktif, kısa ve profesyonel yanıtlar ver. Türkçe dilinde cevapla.`;
const result = streamText({
model,
system: systemPrompt,
messages,
tools: {
getFinancialSummary: tool({
description: 'Son X gündeki gelir ve gider işlemlerinin listesini getirir.',
parameters: z.object({ days: z.number().default(30) }),
execute: async ({ days }) => {
const pastDate = new Date();
pastDate.setDate(pastDate.getDate() - days);
const { data } = await supabase.from('finance_transactions')
.select('type, amount, category, transaction_date')
.gte('transaction_date', pastDate.toISOString());
return data || [];
}
}),
listTasks: tool({
description: 'Kullanıcının mevcut görevlerini belirli bir duruma göre listeler.',
parameters: z.object({ status: z.enum(['todo', 'in_progress', 'completed', 'all']).default('all') }),
execute: async ({ status }) => {
let query = supabase.from('tasks').select('id, title, status, due_at');
if (status !== 'all') query = query.eq('status', status);
const { data } = await query.order('created_at', { ascending: false }).limit(20);
return data || [];
}
}),
createTask: tool({
description: 'Sisteme yeni bir görev ekler.',
parameters: z.object({
title: z.string().describe('Görev başlığı'),
description: z.string().optional().describe('Görevin detayı')
}),
execute: async ({ title, description }) => {
const { data, error } = await supabase.from('tasks')
.insert({ user_id: user.id, title, description, status: 'todo' })
.select().single();
if (error) return { success: false, error: error.message };
return { success: true, task: data };
}
}),
searchProjects: tool({
description: 'Projeleri isimlerine veya durumlarına göre listeler',
parameters: z.object({ status: z.enum(['active', 'planning', 'completed', 'paused', 'all']).default('active') }),
execute: async ({ status }) => {
let query = supabase.from('projects').select('id, name, status, progress, budget');
if (status !== 'all') query = query.eq('status', status);
const { data } = await query.limit(10);
return data || [];
}
}),
addFinanceTransaction: tool({
description: 'Sisteme yeni bir finansal kayıt (gelir veya gider) ekler.',
parameters: z.object({
type: z.enum(['income', 'expense']).describe('income (gelir) veya expense (gider)'),
amount: z.number().describe('Tutar'),
category: z.string().describe('Kategori örn. Yazılım, Yemek, Vergi vb.')
}),
execute: async ({ type, amount, category }) => {
const { data, error } = await supabase.from('finance_transactions')
.insert({
user_id: user.id,
type,
amount,
category,
transaction_date: new Date().toISOString()
})
.select().single();
if (error) return { success: false, error: error.message };
return { success: true, transaction: data };
}
})
},
onFinish: async ({ text }) => {
// Save assistant response to DB
if (sessionId && text) {
await supabase.from("chat_messages").insert({
session_id: sessionId,
role: "assistant",
content: text,
});
}
}
});
return result.toDataStreamResponse();
} catch (error: any) {
console.error("Chat API error:", error);
return new Response(error.message || "Internal Server Error", { status: 500 });
}
}