everydaycats commited on
Commit
fdb5d73
Β·
verified Β·
1 Parent(s): c696f2f

Update apps/format.js

Browse files
Files changed (1) hide show
  1. apps/format.js +58 -24
apps/format.js CHANGED
@@ -1,3 +1,4 @@
 
1
  import express from 'express';
2
  import { generateCompletion } from '../ai_engine.js';
3
  import { supabase } from '../config/supabaseClient.js';
@@ -8,22 +9,28 @@ const router = express.Router();
8
  // 🧠 DYNAMIC SYSTEM PROMPT BUILDER
9
  // ==========================================
10
  const buildSystemPrompt = (personaName, personaPrompt) => `
11
- You are an elite communication strategist, negotiator, and ghostwriter.
12
- The user will provide either a text message they received OR a screenshot of a conversation.
13
- Your goal is to generate the PERFECT response for the user to send back.
 
 
 
 
 
 
14
 
15
- CRITICAL INSTRUCTION - ADOPT THE FOLLOWING PERSONA:
16
- Persona Identity: ${personaName}
17
  Behavior & Tone: ${personaPrompt}
18
 
19
  STRICT RULES:
20
- 1. NEVER break character. Act exactly as the persona dictates.
21
  2. NEVER start with "As an AI..." or "Here is a response...".
22
- 3. Provide ONLY the exact text the user should copy and paste. No explanations, no labels, no quotes.
23
- 4. Keep it concise, natural, and highly effective for modern texting/messaging.
24
- 5. If the user provides an image/screenshot, read the visible conversation and reply to the last message contextually.
25
- 6. Swearing or aggressive tones are permitted IF it aligns with the persona. Do not filter yourself.
26
- 7. OUTPUT ONLY THE REPLY TEXT. Nothing else.
27
  `;
28
 
29
  // ==========================================
@@ -33,6 +40,24 @@ let formatsCache = null;
33
  let lastFormatsUpdate = 0;
34
  const CACHE_DURATION = 3600000;
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  // ==========================================
37
  // 🌐 ENDPOINTS
38
  // ==========================================
@@ -66,16 +91,16 @@ router.get('/formats', async (req, res) => {
66
 
67
  /**
68
  * 2. GENERATE REPLY
69
- * Expects body: { type: 'text'|'image', content: string, persona: object }
70
- *
71
- * FIX: generateCompletion was called with response_format: json_object but the
72
- * prompt never asked for JSON β†’ provider rejected with 400. We now call the AI
73
- * engine in plain-text mode by passing forceJson: false, and extract the reply
74
- * directly from aiResult.data (a plain string).
75
  */
76
  router.post('/generate', async (req, res) => {
77
  try {
78
- const { type, content, persona } = req.body;
79
 
80
  if (!content) throw new Error('No content provided to analyze.');
81
  if (!persona || !persona.label) throw new Error('Persona context is missing.');
@@ -87,38 +112,47 @@ router.post('/generate', async (req, res) => {
87
  persona.prompt || 'Be highly intelligent, strategic, polite, and persuasive.'
88
  );
89
 
 
 
 
90
  let modelToUse;
91
  let imagesPayload = [];
92
  let userPrompt = '';
93
 
94
  if (type === 'image') {
95
  modelToUse = 'bytedance-1.6';
 
96
  imagesPayload = [content];
97
- userPrompt = 'Analyze this chat screenshot and write my next reply. Output ONLY the reply text.';
 
 
 
 
98
  } else {
99
  modelToUse = 'bytedance-1.6';
100
- userPrompt = `Write my reply to this message. Output ONLY the reply text, nothing else.\n\nMessage:\n"${content}"`;
 
 
 
101
  }
102
 
103
- // βœ… KEY FIX: pass forceJson: false so ai_engine skips response_format enforcement
104
  const aiResult = await generateCompletion({
105
  model: modelToUse,
106
  prompt: userPrompt,
107
  system_prompt: aiSystemPrompt,
108
  images: imagesPayload,
109
- forceJson: false, // <-- plain text reply, no JSON schema
110
  });
111
 
112
  if (!aiResult.success) throw new Error(aiResult.error);
113
 
114
- // aiResult.data is a plain string when forceJson is false
115
  let finalReply = (aiResult.data || '').trim();
116
 
117
  // Strip any wrapping quotes the model might add
118
  if (finalReply.startsWith('"') && finalReply.endsWith('"')) {
119
  finalReply = finalReply.slice(1, -1).trim();
120
  }
121
- // Strip markdown bold/italic the model sometimes adds
122
  finalReply = finalReply.replace(/^\*+|\*+$/g, '').trim();
123
 
124
  if (!finalReply) throw new Error('AI returned an empty reply.');
 
1
+
2
  import express from 'express';
3
  import { generateCompletion } from '../ai_engine.js';
4
  import { supabase } from '../config/supabaseClient.js';
 
9
  // 🧠 DYNAMIC SYSTEM PROMPT BUILDER
10
  // ==========================================
11
  const buildSystemPrompt = (personaName, personaPrompt) => `
12
+ You are an elite communication strategist, negotiator, and ghostwriter.
13
+ The user is in an ONGOING conversation and needs you to write their NEXT reply.
14
+
15
+ You will be given:
16
+ - The recent conversation history (showing what each side has said so far)
17
+ - The most recent message they received (the one they need to reply to)
18
+ - Optionally, a screenshot of the chat
19
+
20
+ Your job: write the PERFECT next message for the user to send.
21
 
22
+ PERSONA TO ADOPT:
23
+ Identity: ${personaName}
24
  Behavior & Tone: ${personaPrompt}
25
 
26
  STRICT RULES:
27
+ 1. NEVER break character.
28
  2. NEVER start with "As an AI..." or "Here is a response...".
29
+ 3. Output ONLY the exact reply text β€” no labels, no quotes, no explanation.
30
+ 4. Keep it concise and natural for modern texting/messaging.
31
+ 5. Use the conversation history to stay contextually relevant. Do NOT reply generically.
32
+ 6. If a screenshot is provided, read it carefully β€” it is the actual chat. Reply to the last visible message.
33
+ 7. The reply must feel like a natural continuation of the conversation, not a cold opener.
34
  `;
35
 
36
  // ==========================================
 
40
  let lastFormatsUpdate = 0;
41
  const CACHE_DURATION = 3600000;
42
 
43
+ // ==========================================
44
+ // πŸ“ CONVERSATION HISTORY FORMATTER
45
+ // ==========================================
46
+ // Converts the last N messages into a readable thread for the AI.
47
+ // Images are noted but not sent (only the triggering image is sent as vision input).
48
+ const formatHistory = (messages = [], maxMessages = 6) => {
49
+ if (!messages.length) return null;
50
+
51
+ const recent = messages.slice(-maxMessages); // last 6 messages for context
52
+ const lines = recent.map(m => {
53
+ const role = m.sender === 'user' ? 'ME' : 'THEM';
54
+ if (m.type === 'image') return `${role}: [sent a screenshot]`;
55
+ return `${role}: ${m.content}`;
56
+ });
57
+
58
+ return lines.join('\n');
59
+ };
60
+
61
  // ==========================================
62
  // 🌐 ENDPOINTS
63
  // ==========================================
 
91
 
92
  /**
93
  * 2. GENERATE REPLY
94
+ * Expects body: {
95
+ * type: 'text' | 'image',
96
+ * content: string, // current message text OR base64 image
97
+ * persona: object,
98
+ * history: Message[] // ← NEW: recent messages from the chat session
99
+ * }
100
  */
101
  router.post('/generate', async (req, res) => {
102
  try {
103
+ const { type, content, persona, history = [] } = req.body;
104
 
105
  if (!content) throw new Error('No content provided to analyze.');
106
  if (!persona || !persona.label) throw new Error('Persona context is missing.');
 
112
  persona.prompt || 'Be highly intelligent, strategic, polite, and persuasive.'
113
  );
114
 
115
+ // Build the conversation thread string (text messages only, images noted as placeholder)
116
+ const conversationHistory = formatHistory(history, 6);
117
+
118
  let modelToUse;
119
  let imagesPayload = [];
120
  let userPrompt = '';
121
 
122
  if (type === 'image') {
123
  modelToUse = 'bytedance-1.6';
124
+ // Only send the triggering image to the vision model β€” prior images stay on device
125
  imagesPayload = [content];
126
+
127
+ userPrompt = conversationHistory
128
+ ? `Here is our conversation so far:\n${conversationHistory}\n\nI've also attached the latest screenshot. Read it and write my next reply. Output ONLY the reply text.`
129
+ : `Analyze this chat screenshot and write my next reply. Output ONLY the reply text.`;
130
+
131
  } else {
132
  modelToUse = 'bytedance-1.6';
133
+
134
+ userPrompt = conversationHistory
135
+ ? `Here is our conversation so far:\n${conversationHistory}\n\nTheir latest message: "${content}"\n\nWrite my next reply. Output ONLY the reply text, nothing else.`
136
+ : `Write my reply to this message. Output ONLY the reply text, nothing else.\n\nMessage:\n"${content}"`;
137
  }
138
 
 
139
  const aiResult = await generateCompletion({
140
  model: modelToUse,
141
  prompt: userPrompt,
142
  system_prompt: aiSystemPrompt,
143
  images: imagesPayload,
144
+ forceJson: false,
145
  });
146
 
147
  if (!aiResult.success) throw new Error(aiResult.error);
148
 
 
149
  let finalReply = (aiResult.data || '').trim();
150
 
151
  // Strip any wrapping quotes the model might add
152
  if (finalReply.startsWith('"') && finalReply.endsWith('"')) {
153
  finalReply = finalReply.slice(1, -1).trim();
154
  }
155
+ // Strip markdown bold/italic wrappers
156
  finalReply = finalReply.replace(/^\*+|\*+$/g, '').trim();
157
 
158
  if (!finalReply) throw new Error('AI returned an empty reply.');