Skip to content

Commit f7c4ef2

Browse files
authored
Merge branch 'main' into fix/sharepoint-folders
2 parents b2d9320 + 5598705 commit f7c4ef2

28 files changed

Lines changed: 1642 additions & 502 deletions

.coderabbit.yaml

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,8 @@ reviews:
3333
instructions: "Apply when the PR only changes docs, README, AGENTS.md, or CLAUDE.md."
3434
- label: "refactor"
3535
instructions: "Apply when the PR restructures existing code without changing behaviour."
36-
- label: "performance"
37-
instructions: "Apply when the PR improves runtime speed, memory usage, or resource efficiency."
38-
- label: "test"
36+
- label: "tests"
3937
instructions: "Apply when the PR only adds or updates tests."
40-
- label: "chore"
41-
instructions: "Apply for maintenance tasks: dependency bumps, config changes, tooling updates."
42-
- label: "build"
43-
instructions: "Apply when the PR changes the build system, Dockerfiles, CI workflows, or release process."
4438
path_filters: []
4539
path_instructions:
4640
- path: "src/**/*.py"
@@ -156,6 +150,19 @@ chat:
156150
usage: auto
157151
linear:
158152
usage: auto
153+
issue_enrichment:
154+
auto_enrich:
155+
enabled: true
156+
planning:
157+
enabled: true
158+
auto_planning:
159+
enabled: true
160+
labels:
161+
- "enhancement"
162+
- "bug"
163+
- "refactor"
164+
- "!documentation"
165+
- "!tests"
159166
knowledge_base:
160167
opt_out: false
161168
code_guidelines:

frontend/app/chat/_components/assistant-message.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ const MarkdownRenderer = dynamic(
1111
{ ssr: false },
1212
);
1313

14+
import { trackButton } from "@/lib/analytics";
1415
import { cn } from "@/lib/utils";
1516
import type {
1617
FunctionCall,
1718
TokenUsage as TokenUsageType,
1819
} from "../_types/types";
1920
import { FunctionCalls } from "./function-calls";
2021
import { Message } from "./message";
22+
import MessageActions from "./message-actions";
2123
import { TokenUsage } from "./token-usage";
2224

2325
interface AssistantMessageProps {
@@ -35,6 +37,8 @@ interface AssistantMessageProps {
3537
delay?: number;
3638
isInitialGreeting?: boolean;
3739
usage?: TokenUsageType;
40+
timestamp?: Date;
41+
showFeedback?: boolean;
3842
}
3943

4044
export function AssistantMessage({
@@ -52,7 +56,19 @@ export function AssistantMessage({
5256
delay = 0.2,
5357
isInitialGreeting = false,
5458
usage,
59+
timestamp,
60+
showFeedback = true,
5561
}: AssistantMessageProps) {
62+
const trackFeedback = (feedback: "like" | "dislike") => {
63+
trackButton({
64+
action: feedback,
65+
elementId: "message-feedback",
66+
namespace: "chat",
67+
CTA: feedback === "like" ? "Like Message" : "Dislike Message",
68+
timestamp: timestamp?.getTime(),
69+
});
70+
};
71+
5672
return (
5773
<motion.div
5874
initial={animate ? { opacity: 0, y: -20 } : { opacity: 1, y: 0 }}
@@ -151,6 +167,9 @@ export function AssistantMessage({
151167
}
152168
/>
153169
{usage && !isStreaming && <TokenUsage usage={usage} />}
170+
{!isInitialGreeting && showFeedback && !isStreaming && (
171+
<MessageActions trackFeedback={trackFeedback} />
172+
)}
154173
</motion.div>
155174
</div>
156175
</Message>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { ThumbsDown, ThumbsUp } from "lucide-react";
2+
import { useState } from "react";
3+
import { Button } from "@/components/ui/button";
4+
5+
interface MessageActionsProps {
6+
trackFeedback: (feedback: "like" | "dislike") => void;
7+
}
8+
9+
const MessageActions = ({ trackFeedback }: MessageActionsProps) => {
10+
const [feedbackSelected, setFeedbackSelected] = useState<
11+
"like" | "dislike" | null
12+
>(null);
13+
14+
const handleFeedback = (feedback: "like" | "dislike") => {
15+
if (feedbackSelected === feedback) return; // Prevent multiple tracking events for the same feedback
16+
trackFeedback(feedback);
17+
setFeedbackSelected(feedback);
18+
};
19+
20+
return (
21+
<div className="flex space-x-2 mt-2">
22+
<Button
23+
variant="ghost"
24+
size="icon"
25+
aria-label="Like"
26+
aria-pressed={feedbackSelected === "like"}
27+
className={
28+
feedbackSelected !== "like"
29+
? "text-muted-foreground hover:text-foreground"
30+
: ""
31+
}
32+
onClick={() => handleFeedback("like")}
33+
>
34+
<ThumbsUp
35+
className={`h-4 w-4 ${feedbackSelected === "like" ? "fill-current" : ""}`}
36+
/>
37+
</Button>
38+
<Button
39+
variant="ghost"
40+
size="icon"
41+
aria-label="Dislike"
42+
aria-pressed={feedbackSelected === "dislike"}
43+
className={
44+
feedbackSelected !== "dislike"
45+
? "text-muted-foreground hover:text-foreground"
46+
: ""
47+
}
48+
onClick={() => handleFeedback("dislike")}
49+
>
50+
<ThumbsDown
51+
className={`h-4 w-4 ${feedbackSelected === "dislike" ? "fill-current" : ""}`}
52+
/>
53+
</Button>
54+
</div>
55+
);
56+
};
57+
58+
export default MessageActions;

frontend/app/chat/_types/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ export interface Message {
2121
usage?: TokenUsage;
2222
}
2323

24+
export const INITIAL_ASSISTANT_MESSAGE: Message = {
25+
role: "assistant",
26+
content: "How can I assist?",
27+
timestamp: new Date(),
28+
};
29+
2430
export interface FunctionCall {
2531
name: string;
2632
arguments?: Record<string, unknown>;

frontend/app/chat/page.tsx

Lines changed: 14 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { trackLLMCall } from "@/lib/analytics";
1313
import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants";
1414
import { buildSearchPayloadFilters } from "@/lib/filter-normalization";
1515
import { cn } from "@/lib/utils";
16-
import { useLoadingStore } from "@/stores/loadingStore";
1716
import { useGetConversationsQuery } from "../api/queries/useGetConversationsQuery";
1817
import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery";
1918
import { useGetSettingsQuery } from "../api/queries/useGetSettingsQuery";
@@ -29,10 +28,10 @@ import type {
2928
RequestBody,
3029
ToolCallResult,
3130
} from "./_types/types";
31+
import { INITIAL_ASSISTANT_MESSAGE } from "./_types/types";
3232

3333
function ChatPage() {
3434
const isDebugMode = process.env.NEXT_PUBLIC_OPENRAG_DEBUG === "true";
35-
const isCloudBrand = useIsCloudBrand();
3635
const {
3736
endpoint,
3837
setEndpoint,
@@ -50,16 +49,13 @@ function ChatPage() {
5049
placeholderConversation,
5150
conversationFilter,
5251
setConversationFilter,
52+
loading,
53+
setLoading,
5354
} = useChat();
5455
const [messages, setMessages] = useState<Message[]>([
55-
{
56-
role: "assistant",
57-
content: "How can I assist?",
58-
timestamp: new Date(),
59-
},
56+
INITIAL_ASSISTANT_MESSAGE,
6057
]);
6158
const [input, setInput] = useState("");
62-
const { loading, setLoading } = useLoadingStore();
6359
const { setChatError } = useChat();
6460
const [asyncMode, setAsyncMode] = useState(true);
6561
const [expandedFunctionCalls, setExpandedFunctionCalls] = useState<
@@ -109,7 +105,7 @@ function ChatPage() {
109105
streamingMessage,
110106
sendMessage: sendStreamingMessage,
111107
abortStream,
112-
isLoading: isStreamLoading,
108+
isLoading: isChatStreaming,
113109
} = useChatStreaming({
114110
endpoint: apiEndpoint,
115111
onComplete: (message, responseId) => {
@@ -169,7 +165,7 @@ function ChatPage() {
169165
useEffect(() => {
170166
let timeoutId: NodeJS.Timeout | null = null;
171167

172-
if (isStreamLoading && !streamingMessage) {
168+
if (isChatStreaming && !streamingMessage) {
173169
timeoutId = setTimeout(() => {
174170
setWaitingTooLong(true);
175171
}, 20000); // 20 seconds
@@ -180,7 +176,7 @@ function ChatPage() {
180176
return () => {
181177
if (timeoutId) clearTimeout(timeoutId);
182178
};
183-
}, [isStreamLoading, streamingMessage]);
179+
}, [isChatStreaming, streamingMessage]);
184180

185181
const handleEndpointChange = (newEndpoint: EndpointType) => {
186182
setEndpoint(newEndpoint);
@@ -328,13 +324,7 @@ function ChatPage() {
328324
// Abort any in-flight streaming so it doesn't bleed into new chat
329325
abortStream();
330326
// Reset chat UI even if context state was already 'new'
331-
setMessages([
332-
{
333-
role: "assistant",
334-
content: "How can I assist?",
335-
timestamp: new Date(),
336-
},
337-
]);
327+
setMessages([INITIAL_ASSISTANT_MESSAGE]);
338328
setInput("");
339329
setExpandedFunctionCalls(new Set());
340330
setIsFilterHighlighted(false);
@@ -372,7 +362,7 @@ function ChatPage() {
372362

373363
if (
374364
conversationData?.messages &&
375-
(isNewConversation || (!isStreamLoading && hasMessageCountChanged)) &&
365+
(isNewConversation || (!isChatStreaming && hasMessageCountChanged)) &&
376366
!isUserInteracting &&
377367
!isForkingInProgress
378368
) {
@@ -564,21 +554,15 @@ function ChatPage() {
564554
isUserInteracting,
565555
isForkingInProgress,
566556
setPreviousResponseIds,
567-
isStreamLoading,
557+
isChatStreaming,
568558
messages.length,
569559
]);
570560

571561
// Handle new conversation creation - only reset messages when placeholderConversation is set
572562
useEffect(() => {
573563
if (placeholderConversation && currentConversationId === null) {
574564
console.log("Starting new conversation");
575-
setMessages([
576-
{
577-
role: "assistant",
578-
content: "How can I assist?",
579-
timestamp: new Date(),
580-
},
581-
]);
565+
setMessages([INITIAL_ASSISTANT_MESSAGE]);
582566
lastLoadedConversationRef.current = null;
583567

584568
// Focus input when starting a new conversation
@@ -588,93 +572,6 @@ function ChatPage() {
588572
}
589573
}, [placeholderConversation, currentConversationId]);
590574

591-
// Listen for file upload events from navigation
592-
useEffect(() => {
593-
const handleFileUploadStart = (event: CustomEvent) => {
594-
const { filename } = event.detail;
595-
console.log("Chat page received file upload start event:", filename);
596-
597-
setLoading(true);
598-
setIsUploading(true);
599-
setUploadedFile(null); // Clear previous file
600-
};
601-
602-
const handleFileUploaded = (event: CustomEvent) => {
603-
const { result } = event.detail;
604-
console.log("Chat page received file upload event:", result);
605-
606-
setUploadedFile(null); // Clear file after upload
607-
608-
// Update the response ID for this endpoint
609-
if (result.response_id) {
610-
setPreviousResponseIds((prev) => ({
611-
...prev,
612-
[endpoint]: result.response_id,
613-
}));
614-
}
615-
};
616-
617-
const handleFileUploadComplete = () => {
618-
console.log("Chat page received file upload complete event");
619-
setLoading(false);
620-
setIsUploading(false);
621-
};
622-
623-
const handleFileUploadError = (event: CustomEvent) => {
624-
const { filename, error } = event.detail;
625-
console.log(
626-
"Chat page received file upload error event:",
627-
filename,
628-
error,
629-
);
630-
631-
// Replace the last message with error message
632-
const errorMessage: Message = {
633-
role: "assistant",
634-
content: `❌ Upload failed for **${filename}**: ${error}`,
635-
timestamp: new Date(),
636-
};
637-
setMessages((prev) => [...prev.slice(0, -1), errorMessage]);
638-
setUploadedFile(null); // Clear file on error
639-
};
640-
641-
window.addEventListener(
642-
"fileUploadStart",
643-
handleFileUploadStart as EventListener,
644-
);
645-
window.addEventListener(
646-
"fileUploaded",
647-
handleFileUploaded as EventListener,
648-
);
649-
window.addEventListener(
650-
"fileUploadComplete",
651-
handleFileUploadComplete as EventListener,
652-
);
653-
window.addEventListener(
654-
"fileUploadError",
655-
handleFileUploadError as EventListener,
656-
);
657-
658-
return () => {
659-
window.removeEventListener(
660-
"fileUploadStart",
661-
handleFileUploadStart as EventListener,
662-
);
663-
window.removeEventListener(
664-
"fileUploaded",
665-
handleFileUploaded as EventListener,
666-
);
667-
window.removeEventListener(
668-
"fileUploadComplete",
669-
handleFileUploadComplete as EventListener,
670-
);
671-
window.removeEventListener(
672-
"fileUploadError",
673-
handleFileUploadError as EventListener,
674-
);
675-
};
676-
}, [endpoint, setPreviousResponseIds, setLoading]);
677-
678575
// Check onboarding completion
679576

680577
// Check if onboarding is complete (current_step >= 4 means complete)
@@ -1142,9 +1039,11 @@ function ChatPage() {
11421039
isInitialGreeting={
11431040
index === 0 &&
11441041
messages.length === 1 &&
1145-
message.content === "How can I assist?"
1042+
message.content ===
1043+
INITIAL_ASSISTANT_MESSAGE.content
11461044
}
11471045
usage={message.usage}
1046+
timestamp={message.timestamp}
11481047
/>
11491048
)}
11501049
</div>

frontend/app/onboarding/_components/onboarding-content.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ export function OnboardingContent({
287287
onToggle={() => {}}
288288
isStreaming={!!streamingMessage}
289289
isCompleted={currentStep > 3}
290+
showFeedback={false}
290291
/>
291292
)}
292293

0 commit comments

Comments
 (0)