-
Notifications
You must be signed in to change notification settings - Fork 8
Feat/tickets work 4 #193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/tickets work 4 #193
Changes from all commits
14a7017
b1528e5
186e84f
f81bcb4
a4700eb
944ede9
c02574d
d5c0f51
1d6687f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -12,26 +12,22 @@ | |||
|
|
||||
| import Link from "next/link"; | ||||
| import Image from "next/image"; | ||||
| import { useRouter, usePathname } from "next/navigation"; | ||||
| import { useState, useEffect } from "react"; | ||||
| import { useRouter } from "next/navigation"; | ||||
|
||||
| import { useRouter } from "next/navigation"; |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ElizaAvatar import is no longer used after removing the avatar display from the sidebar. This import should be removed.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -177,6 +177,8 @@ export async function sendStreamingMessage({ | |
| const decoder = new TextDecoder(); | ||
| let buffer = ""; | ||
|
|
||
| let completeCalled = false; | ||
|
|
||
| try { | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
|
|
@@ -191,13 +193,20 @@ export async function sendStreamingMessage({ | |
| onChunk, | ||
| onReasoning, | ||
| onError, | ||
| onComplete, | ||
| () => { | ||
| completeCalled = true; | ||
| onComplete?.(); | ||
| }, | ||
| ); | ||
| } catch (err) { | ||
| console.error("[Stream] Error processing final buffer:", err); | ||
| onError?.("Stream ended unexpectedly"); | ||
| } | ||
| } | ||
| // Always call onComplete when stream ends, if not already called | ||
| if (!completeCalled) { | ||
| onComplete?.(); | ||
| } | ||
|
Comment on lines
+196
to
+209
|
||
| break; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,10 @@ | ||||||||||||||||||||||
| import { roomsRepository, memoriesRepository } from "@/db/repositories"; | ||||||||||||||||||||||
| import { generateText } from "ai"; | ||||||||||||||||||||||
| import { gateway } from "@ai-sdk/gateway"; | ||||||||||||||||||||||
| import { logger } from "@/lib/utils/logger"; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate a title for a room based on the first user message. | ||||||||||||||||||||||
| * Generate an AI-powered title for a room based on the first user message. | ||||||||||||||||||||||
| * Only generates if room currently has default title ("New Chat"). | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * @param roomId - The room ID to generate title for | ||||||||||||||||||||||
|
|
@@ -19,6 +21,7 @@ export async function generateRoomTitle( | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (room.name && room.name !== "New Chat") { | ||||||||||||||||||||||
| logger.info(`[RoomTitle] Room already has title: ${room.name}`); | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -45,25 +48,97 @@ export async function generateRoomTitle( | |||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Create title from first user message | ||||||||||||||||||||||
| const title = text | ||||||||||||||||||||||
| .replace(/\n/g, " ") // Remove newlines | ||||||||||||||||||||||
| .replace(/\s+/g, " ") // Collapse whitespace | ||||||||||||||||||||||
| .trim() | ||||||||||||||||||||||
| .substring(0, 40) // Limit to 40 chars | ||||||||||||||||||||||
| .trim(); | ||||||||||||||||||||||
| // Generate AI title | ||||||||||||||||||||||
| let title: string; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| const prompt = `Create a brief 3-5 word title summarizing this message topic. Output ONLY the title, no quotes or explanation. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const finalTitle = text.length > 40 ? `${title}...` : title; | ||||||||||||||||||||||
| Message: ${text.slice(0, 300)} | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (!finalTitle || finalTitle.length < 3) { | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| Title:`; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| logger.info(`[RoomTitle] Generating AI title for room ${roomId}`); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const result = await generateText({ | ||||||||||||||||||||||
| model: gateway.languageModel("openai/gpt-4o-mini"), | ||||||||||||||||||||||
|
Comment on lines
+62
to
+64
|
||||||||||||||||||||||
| const result = await generateText({ | |
| model: gateway.languageModel("openai/gpt-4o-mini"), | |
| const roomTitleModel = | |
| process.env.ROOM_TITLE_MODEL || "openai/gpt-4o-mini"; | |
| const result = await generateText({ | |
| model: gateway.languageModel(roomTitleModel), |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fallback title generation logic at line 111 slices to 5 words but checks if words.length >= 3. This means messages with exactly 3 or 4 words will have all words included without ellipsis, but 5-word messages will also have all words without ellipsis (line 139 adds ellipsis only for messages with more than 5 words). Consider clarifying this logic or adjusting the threshold for consistency.
| const words = message.trim().split(/\s+/).slice(0, 6); | |
| if (words.length >= 3) { | |
| return capitalizeFirst(words.slice(0, 5).join(" ")); | |
| const words = message.trim().split(/\s+/); | |
| if (words.length >= 3) { | |
| const titleWords = words.slice(0, 5); | |
| const baseTitle = capitalizeFirst( | |
| titleWords.join(" ").replace(/[.!?]+$/, ""), | |
| ); | |
| return words.length > 5 ? baseTitle + "..." : baseTitle; |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The capitalizeFirst helper function doesn't handle empty strings safely. If an empty string is passed, charAt(0) will return an empty string and slice(1) will also return empty, but this could be made more explicit with a guard check to improve robustness.
| function capitalizeFirst(str: string): string { | |
| function capitalizeFirst(str: string): string { | |
| if (str.length === 0) { | |
| return str; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The frontend now requires username to be non-empty before saving (line 171-174), but the database schema and backend service allow username to be null/undefined. This creates an inconsistency - while new characters created through the UI will have usernames, the backend doesn't enforce this constraint. Consider adding backend validation in the character creation/update service to ensure username is always provided, or update the database schema to make username NOT NULL if it's truly required.