Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions apps/mail/actions/ai-reply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use server';

import { headers } from 'next/headers';
import { auth } from '@/lib/auth';

// Function to truncate email thread content to fit within token limits
function truncateThreadContent(threadContent: string, maxTokens: number = 12000): string {
// Split the thread into individual emails
const emails = threadContent.split('\n---\n');

// Start with the most recent email (last in the array)
let truncatedContent = emails[emails.length - 1];

// Add previous emails until we reach the token limit
for (let i = emails.length - 2; i >= 0; i--) {
const newContent = `${emails[i]}\n---\n${truncatedContent}`;

// Rough estimation of tokens (1 token ≈ 4 characters)
const estimatedTokens = newContent.length / 4;

if (estimatedTokens > maxTokens) {
break;
}

truncatedContent = newContent;
}

return truncatedContent;
}

export async function generateAIResponse(threadContent: string, originalSender: string): Promise<string> {
const headersList = await headers();
const session = await auth.api.getSession({ headers: headersList });

if (!session?.user) {
throw new Error('Unauthorized');
}

if (!process.env.OPENAI_API_KEY) {
throw new Error('OpenAI API key is not configured');
}

// Truncate the thread content to fit within token limits
const truncatedThreadContent = truncateThreadContent(threadContent);

// Create the prompt for OpenAI
const prompt = `
You are ${session.user.name}, writing an email reply.

Here's the context of the email thread:
${truncatedThreadContent}

Generate a professional, helpful, and concise email reply to ${originalSender}.

Requirements:
- Be concise but thorough (2-3 paragraphs maximum)
- Maintain a professional and friendly tone
- Address the key points from the original email
- Close with an appropriate sign-off
- Don't use placeholder text or mention that you're an AI
- Write as if you are (${session.user.name})
- Don't include the subject line in the reply
- Double space paragraphs (2 newlines)
- Add two spaces bellow the sign-off
`;

try {
// Direct OpenAI API call using fetch
const openaiResponse = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-3.5-turbo',
messages: [
{
role: 'system',
content: 'You are a helpful email assistant that generates concise, professional replies.',
},
{ role: 'user', content: prompt },
],
temperature: 0.7,
max_tokens: 500,
}),
});

if (!openaiResponse.ok) {
const errorData = await openaiResponse.json();
throw new Error(`OpenAI API Error: ${errorData.error?.message || 'Unknown error'}`);
}

const data = await openaiResponse.json();
return data.choices[0]?.message?.content || '';
} catch (error: any) {
console.error('OpenAI API Error:', error);
throw new Error(`OpenAI API Error: ${error.message || 'Unknown error'}`);
}
}
9 changes: 2 additions & 7 deletions apps/mail/components/create/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -398,16 +398,11 @@ export default function Editor({

// Function to focus the editor
const focusEditor = (e: React.MouseEvent<HTMLDivElement>) => {
if (e.target === containerRef.current) {
editorRef.current?.commands.focus('end');
if (e.target === containerRef.current && editorRef.current?.commands) {
editorRef.current.commands.focus('end');
}
};

// Toggle AI menu
const toggleAIMenu = () => {
dispatch({ type: 'TOGGLE_AI', payload: !openAI });
};

// Function to clear editor content
const clearEditorContent = React.useCallback(() => {
if (editorRef.current) {
Expand Down
8 changes: 2 additions & 6 deletions apps/mail/components/create/prosemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
font-size: 1rem;
font-family: 'Inter', sans-serif;
font-weight: 400;
line-height: 1.4;

}

/* Add placeholder styles */
Expand Down Expand Up @@ -220,11 +220,7 @@ mark[style] > strong {
line-height: 1.4; /* Reduced from 1.6 to 1.4 */
}

/* Adjust space between paragraphs */
.ProseMirror p {
margin-bottom: 1.25rem; /* Reduced from 1.5rem to 1.25rem */
line-height: 1.4; /* Reduced from 1.6 to 1.4 */
}


/* Keep the list indentation the same */
.ProseMirror ul {
Expand Down
Loading