armand0e commited on
Commit
abc7585
·
1 Parent(s): d2427ea

fix: stable message IDs, list markers, node runtime logging, date prompt

Browse files
.gitignore CHANGED
@@ -1,3 +1,4 @@
1
  node_modules
2
  .next
3
  .gradio
 
 
1
  node_modules
2
  .next
3
  .gradio
4
+ chat-logs.jsonl
src/app/api/chat/route.ts CHANGED
@@ -1,4 +1,8 @@
1
  import { NextRequest } from "next/server";
 
 
 
 
2
 
3
  const BASE_URL = process.env.BASE_URL || "https://llama.gptbox.dev/v1";
4
  const API_KEY = process.env.OPENAI_API_KEY || "";
@@ -63,6 +67,17 @@ async function executeWebSearch(query: string): Promise<string> {
63
  }
64
  }
65
 
 
 
 
 
 
 
 
 
 
 
 
66
  // Helper to stream a single completion request
67
  async function streamCompletion(
68
  currentMessages: Array<{ role: string; content: string | null; tool_calls?: unknown; tool_call_id?: string }>,
@@ -169,10 +184,19 @@ export async function POST(req: NextRequest) {
169
  controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
170
  };
171
 
 
 
 
 
 
 
 
 
172
  try {
 
173
  const systemMessage = {
174
  role: "system",
175
- content: "You are a helpful AI assistant.",
176
  };
177
 
178
  const tools = searchEnabled ? [WEB_SEARCH_TOOL] : undefined;
@@ -183,7 +207,17 @@ export async function POST(req: NextRequest) {
183
 
184
  // Loop for tool calls - model reasons, calls tool, then we send results back
185
  for (let round = 0; round < 5; round++) {
186
- const { content, toolCalls } = await streamCompletion(currentMessages, tools, send);
 
 
 
 
 
 
 
 
 
 
187
 
188
  // If no tool calls, we're done
189
  if (toolCalls.length === 0) {
@@ -241,11 +275,20 @@ export async function POST(req: NextRequest) {
241
  send({ type: "done" });
242
  } catch (error) {
243
  console.error("Chat error:", error);
 
244
  send({
245
  type: "error",
246
  message: error instanceof Error ? error.message : "Unknown error",
247
  });
248
  } finally {
 
 
 
 
 
 
 
 
249
  controller.enqueue(encoder.encode("data: [DONE]\n\n"));
250
  controller.close();
251
  }
 
1
  import { NextRequest } from "next/server";
2
+ import { promises as fs } from "fs";
3
+ import path from "path";
4
+
5
+ export const runtime = "nodejs";
6
 
7
  const BASE_URL = process.env.BASE_URL || "https://llama.gptbox.dev/v1";
8
  const API_KEY = process.env.OPENAI_API_KEY || "";
 
67
  }
68
  }
69
 
70
+ const LOG_FILE = process.env.CHAT_LOG_PATH || path.join(process.cwd(), "chat-logs.jsonl");
71
+
72
+ async function appendChatLog(entry: unknown) {
73
+ try {
74
+ const line = JSON.stringify(entry) + "\n";
75
+ await fs.appendFile(LOG_FILE, line, "utf8");
76
+ } catch (error) {
77
+ console.error("Failed to write chat log:", error);
78
+ }
79
+ }
80
+
81
  // Helper to stream a single completion request
82
  async function streamCompletion(
83
  currentMessages: Array<{ role: string; content: string | null; tool_calls?: unknown; tool_call_id?: string }>,
 
184
  controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
185
  };
186
 
187
+ const timestamp = new Date().toISOString();
188
+ const logRounds: Array<{
189
+ content: string;
190
+ reasoning: string;
191
+ toolCalls: Array<{ id: string; name: string; args: string }>;
192
+ }> = [];
193
+ let logError: string | null = null;
194
+
195
  try {
196
+ const today = new Date().toISOString().slice(0, 10);
197
  const systemMessage = {
198
  role: "system",
199
+ content: `You are a helpful AI assistant. Today's date is ${today}.`,
200
  };
201
 
202
  const tools = searchEnabled ? [WEB_SEARCH_TOOL] : undefined;
 
207
 
208
  // Loop for tool calls - model reasons, calls tool, then we send results back
209
  for (let round = 0; round < 5; round++) {
210
+ const { content, reasoning, toolCalls } = await streamCompletion(currentMessages, tools, send);
211
+
212
+ logRounds.push({
213
+ content,
214
+ reasoning,
215
+ toolCalls: toolCalls.map((tc) => ({
216
+ id: tc.id,
217
+ name: tc.function.name,
218
+ args: tc.function.arguments,
219
+ })),
220
+ });
221
 
222
  // If no tool calls, we're done
223
  if (toolCalls.length === 0) {
 
275
  send({ type: "done" });
276
  } catch (error) {
277
  console.error("Chat error:", error);
278
+ logError = error instanceof Error ? error.message : String(error);
279
  send({
280
  type: "error",
281
  message: error instanceof Error ? error.message : "Unknown error",
282
  });
283
  } finally {
284
+ // Fire-and-forget logging of raw chat history for debugging
285
+ void appendChatLog({
286
+ timestamp,
287
+ searchEnabled,
288
+ messages,
289
+ rounds: logRounds,
290
+ error: logError,
291
+ });
292
  controller.enqueue(encoder.encode("data: [DONE]\n\n"));
293
  controller.close();
294
  }
src/app/globals.css CHANGED
@@ -213,6 +213,15 @@ body {
213
  .markdown-content ol {
214
  margin: 0.75em 0;
215
  padding-left: 1.5em;
 
 
 
 
 
 
 
 
 
216
  }
217
 
218
  .markdown-content li {
@@ -344,7 +353,6 @@ body {
344
  font-size: 0.875em;
345
  color: var(--muted-foreground);
346
  line-height: 1.6;
347
- white-space: pre-wrap;
348
  border-top: 1px solid var(--border);
349
  background: var(--background);
350
  }
 
213
  .markdown-content ol {
214
  margin: 0.75em 0;
215
  padding-left: 1.5em;
216
+ list-style-position: outside;
217
+ }
218
+
219
+ .markdown-content ul {
220
+ list-style-type: disc;
221
+ }
222
+
223
+ .markdown-content ol {
224
+ list-style-type: decimal;
225
  }
226
 
227
  .markdown-content li {
 
353
  font-size: 0.875em;
354
  color: var(--muted-foreground);
355
  line-height: 1.6;
 
356
  border-top: 1px solid var(--border);
357
  background: var(--background);
358
  }
src/components/chat.tsx CHANGED
@@ -127,6 +127,14 @@ export function Chat() {
127
  const messagesEndRef = useRef<HTMLDivElement>(null);
128
  const textareaRef = useRef<HTMLTextAreaElement>(null);
129
 
 
 
 
 
 
 
 
 
130
  const scrollToBottom = () => {
131
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
132
  };
@@ -148,7 +156,7 @@ export function Chat() {
148
  if (!input.trim() || isLoading) return;
149
 
150
  const userMessage: Message = {
151
- id: Date.now().toString(),
152
  role: "user",
153
  content: input.trim(),
154
  steps: [],
@@ -159,7 +167,7 @@ export function Chat() {
159
  setIsLoading(true);
160
 
161
  const assistantMessage: Message = {
162
- id: (Date.now() + 1).toString(),
163
  role: "assistant",
164
  content: "",
165
  steps: [],
 
127
  const messagesEndRef = useRef<HTMLDivElement>(null);
128
  const textareaRef = useRef<HTMLTextAreaElement>(null);
129
 
130
+ const makeId = () => {
131
+ try {
132
+ return globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`;
133
+ } catch {
134
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
135
+ }
136
+ };
137
+
138
  const scrollToBottom = () => {
139
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
140
  };
 
156
  if (!input.trim() || isLoading) return;
157
 
158
  const userMessage: Message = {
159
+ id: makeId(),
160
  role: "user",
161
  content: input.trim(),
162
  steps: [],
 
167
  setIsLoading(true);
168
 
169
  const assistantMessage: Message = {
170
+ id: makeId(),
171
  role: "assistant",
172
  content: "",
173
  steps: [],