Skip to content
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

Research Mode [Part 2]: Improve Prompts, Edit Chat Messages. Set LLM Seed for Reproducibility #954

Merged
merged 36 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2ac840e
Make cursor in chat input take on selected agent color
debanjum Oct 28, 2024
358a6ce
Defer turning cursor color to selected agents color for later
debanjum Oct 29, 2024
d44e68b
Improve handling embedding model config from admin interface
debanjum Oct 29, 2024
b3a6301
Support setting seed for reproducible LLM response generation
debanjum Oct 30, 2024
f64f5b3
Handle add/delete file filter operation on non-existent conversation
debanjum Oct 30, 2024
ba15686
Store turn id with each chat message. Expose API to delete chat turn
debanjum Oct 30, 2024
ca5a683
Add ability to delete messages from the web app
debanjum Oct 30, 2024
cb90abc
Resolve train of thought component needs unique key id error on web app
debanjum Oct 30, 2024
e8e6ead
Fix deleting new messages generated after conversation load
debanjum Oct 31, 2024
e17dc9f
Put train of thought ui before Khoj response on web app
debanjum Oct 31, 2024
a137606
Only show trash can when turnId is present
sabaimran Oct 31, 2024
559601d
Do not exit if/else loop in research loop when notes not found
sabaimran Oct 31, 2024
5b15176
Only add /research prefix in research mode if not already in user query
debanjum Nov 1, 2024
89597ae
Json dump contents in prompt tracer to make structure discernable
debanjum Nov 1, 2024
52163fe
Improve research planner prompt to reduce looping
debanjum Nov 1, 2024
302bd51
Improve online chat actor prompt for research and normal mode
debanjum Nov 1, 2024
1924180
Merge branch 'master' of github.com:khoj-ai/khoj into improve-debug-r…
sabaimran Nov 1, 2024
21858ac
Remove conversation command always in query, filter out inferred quer…
sabaimran Nov 1, 2024
149cbe1
Use bottom anchor for the commandbar popover
sabaimran Nov 1, 2024
3ea94ac
Only include inferred-queries in chat history when present
sabaimran Nov 1, 2024
0145b2a
Set usage limits on the research mode
sabaimran Nov 1, 2024
33d36ee
Add experimental notice to research mode tooltip
sabaimran Nov 1, 2024
1fc280d
Handle case where infer_webpage_url returns no valid urls
sabaimran Nov 1, 2024
ffa7f95
Add template for a code sandbox to the docker-compose configuration
sabaimran Nov 1, 2024
23a49b6
Add documentation for python code execution capability
sabaimran Nov 1, 2024
b3dad1f
Standardize rate limits to 1/6 ratio
sabaimran Nov 1, 2024
2b35790
Merge branch 'master' of github.com:khoj-ai/khoj into improve-debug-r…
sabaimran Nov 1, 2024
ac21b10
Simplify logic to get default search model. Remove unused import
debanjum Nov 1, 2024
9c7b36d
Use standard per minute rate limits across user types
debanjum Nov 1, 2024
b79a9ec
Clarify description of the code evaluation environment: not for docum…
sabaimran Nov 1, 2024
327fcb8
create defiltered query after conversation command is extracted
sabaimran Nov 1, 2024
a213b59
Limit the number of urls the webscraper can extract for scraping
sabaimran Nov 1, 2024
e6eb87b
Merge branch 'improve-debug-reasoning-and-other-misc-fixes' of github…
sabaimran Nov 1, 2024
1a83bbc
Clean API chat router. Move FeedbackData response type to router helper
debanjum Nov 2, 2024
ab321dc
Expect query before tool in response to give think space in research …
debanjum Nov 2, 2024
14e4530
Add prompt tracing, agent personality to infer webpage urls chat actor
debanjum Nov 2, 2024
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
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ services:
interval: 30s
timeout: 10s
retries: 5
sandbox:
image: ghcr.io/khoj-ai/terrarium:latest
ports:
- "8080:8080"
server:
depends_on:
database:
Expand Down
3 changes: 3 additions & 0 deletions documentation/docs/features/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ Slash commands allows you to change what Khoj uses to respond to your query
- **/image**: Generate an image in response to your query.
- **/help**: Use /help to get all available commands and general information about Khoj
- **/summarize**: Can be used to summarize 1 selected file filter for that conversation. Refer to [File Summarization](summarization) for details.
- **/diagram**: Generate a diagram in response to your query. This is built on [Excalidraw](https://excalidraw.com/).
- **/code**: Generate and run very simple Python code snippets. Refer to [Code Generation](code_generation) for details.
- **/research**: Go deeper in a topic for more accurate, in-depth responses.
30 changes: 30 additions & 0 deletions documentation/docs/features/code_execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
---

# Code Execution

Khoj can generate and run very simple Python code snippets as well. This is useful if you want to generate a plot, run a simple calculation, or do some basic data manipulation. LLMs by default aren't skilled at complex quantitative tasks. Code generation & execution can come in handy for such tasks.

Just use `/code` in your chat command.

### Setup (Self-Hosting)
Run [Cohere's Terrarium](https://github.com/cohere-ai/cohere-terrarium) on your machine to enable code generation and execution.

Check the [instructions](https://github.com/cohere-ai/cohere-terrarium?tab=readme-ov-file#development) for running from source.

For running with Docker, you can use our [docker-compose.yml](https://github.com/khoj-ai/khoj/blob/master/docker-compose.yml), or start it manually like this:

```bash
docker pull ghcr.io/khoj-ai/terrarium:latest
docker run -d -p 8080:8080 ghcr.io/khoj-ai/terrarium:latest
```

#### Verify
Verify that it's running, by evaluating a simple Python expression:

```bash
curl -X POST -H "Content-Type: application/json" \
--url http://localhost:8080 \
--data-raw '{"code": "1 + 1"}' \
--no-buffer
```
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ dependencies = [
"django_apscheduler == 0.6.2",
"anthropic == 0.26.1",
"docx2txt == 0.8",
"google-generativeai == 0.7.2"
"google-generativeai == 0.8.3"
]
dynamic = ["version"]

Expand Down
3 changes: 3 additions & 0 deletions src/interface/web/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface ChatBodyDataProps {
onConversationIdChange?: (conversationId: string) => void;
setQueryToProcess: (query: string) => void;
streamedMessages: StreamMessage[];
setStreamedMessages: (messages: StreamMessage[]) => void;
setUploadedFiles: (files: string[]) => void;
isMobileWidth?: boolean;
isLoggedIn: boolean;
Expand Down Expand Up @@ -118,6 +119,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
setAgent={setAgentMetadata}
pendingMessage={processingMessage ? message : ""}
incomingMessages={props.streamedMessages}
setIncomingMessages={props.setStreamedMessages}
customClassName={chatHistoryCustomClassName}
/>
</div>
Expand Down Expand Up @@ -351,6 +353,7 @@ export default function Chat() {
<ChatBodyData
isLoggedIn={authenticatedData !== null}
streamedMessages={messages}
setStreamedMessages={setMessages}
chatOptionsData={chatOptionsData}
setTitle={setTitle}
setQueryToProcess={setQueryToProcess}
Expand Down
8 changes: 8 additions & 0 deletions src/interface/web/app/common/chatFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface RawReferenceData {
codeContext?: CodeContext;
}

export interface MessageMetadata {
conversationId: string;
turnId: string;
}

export interface ResponseWithIntent {
intentType: string;
response: string;
Expand Down Expand Up @@ -90,6 +95,9 @@ export function processMessageChunk(
if (references.onlineContext) onlineContext = references.onlineContext;
if (references.codeContext) codeContext = references.codeContext;
return { context, onlineContext, codeContext };
} else if (chunk.type === "metadata") {
const messageMetadata = chunk.data as MessageMetadata;
currentMessage.turnId = messageMetadata.turnId;
} else if (chunk.type === "message") {
const chunkData = chunk.data;
// Here, handle if the response is a JSON response with an image, but the intentType is excalidraw
Expand Down
7 changes: 7 additions & 0 deletions src/interface/web/app/common/colorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ export function converColorToBgGradient(color: string) {
return `${convertToBGGradientClass(color)} dark:border dark:border-neutral-700`;
}

export function convertColorToCaretClass(color: string | undefined) {
if (color && tailwindColors.includes(color)) {
return `caret-${color}-500`;
}
return `caret-orange-500`;
}

export function convertColorToRingClass(color: string | undefined) {
if (color && tailwindColors.includes(color)) {
return `focus-visible:ring-${color}-500`;
Expand Down
72 changes: 58 additions & 14 deletions src/interface/web/app/components/chatHistory/chatHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ interface ChatHistory {
interface ChatHistoryProps {
conversationId: string;
setTitle: (title: string) => void;
incomingMessages?: StreamMessage[];
pendingMessage?: string;
incomingMessages?: StreamMessage[];
setIncomingMessages?: (incomingMessages: StreamMessage[]) => void;
publicConversationSlug?: string;
setAgent: (agent: AgentData) => void;
customClassName?: string;
Expand All @@ -45,7 +46,7 @@ interface TrainOfThoughtComponentProps {
trainOfThought: string[];
lastMessage: boolean;
agentColor: string;
key: string;
keyId: string;
completed?: boolean;
}

Expand All @@ -56,7 +57,7 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) {
return (
<div
className={`${!collapsed ? styles.trainOfThought + " shadow-sm" : ""}`}
key={props.key}
key={props.keyId}
>
{!props.completed && <InlineLoading className="float-right" />}
{props.completed &&
Expand Down Expand Up @@ -97,6 +98,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
const [data, setData] = useState<ChatHistoryData | null>(null);
const [currentPage, setCurrentPage] = useState(0);
const [hasMoreMessages, setHasMoreMessages] = useState(true);
const [currentTurnId, setCurrentTurnId] = useState<string | null>(null);
const sentinelRef = useRef<HTMLDivElement | null>(null);
const scrollAreaRef = useRef<HTMLDivElement | null>(null);
const latestUserMessageRef = useRef<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -177,6 +179,10 @@ export default function ChatHistory(props: ChatHistoryProps) {
if (lastMessage && !lastMessage.completed) {
setIncompleteIncomingMessageIndex(props.incomingMessages.length - 1);
props.setTitle(lastMessage.rawQuery);
// Store the turnId when we get it
if (lastMessage.turnId) {
setCurrentTurnId(lastMessage.turnId);
}
}
}
}, [props.incomingMessages]);
Expand Down Expand Up @@ -278,6 +284,25 @@ export default function ChatHistory(props: ChatHistoryProps) {
return data.agent?.persona;
}

const handleDeleteMessage = (turnId?: string) => {
if (!turnId) return;

setData((prevData) => {
if (!prevData || !turnId) return prevData;
return {
...prevData,
chat: prevData.chat.filter((msg) => msg.turnId !== turnId),
};
});

// Update incoming messages if they exist
if (props.incomingMessages && props.setIncomingMessages) {
props.setIncomingMessages(
props.incomingMessages.filter((msg) => msg.turnId !== turnId),
);
}
};

if (!props.conversationId && !props.publicConversationSlug) {
return null;
}
Expand All @@ -293,6 +318,18 @@ export default function ChatHistory(props: ChatHistoryProps) {
data.chat &&
data.chat.map((chatMessage, index) => (
<>
{chatMessage.trainOfThought && chatMessage.by === "khoj" && (
<TrainOfThoughtComponent
trainOfThought={chatMessage.trainOfThought?.map(
(train) => train.data,
)}
lastMessage={false}
agentColor={data?.agent?.color || "orange"}
key={`${index}trainOfThought`}
keyId={`${index}trainOfThought`}
completed={true}
/>
)}
<ChatMessage
key={`${index}fullHistory`}
ref={
Expand All @@ -312,22 +349,14 @@ export default function ChatHistory(props: ChatHistoryProps) {
customClassName="fullHistory"
borderLeftColor={`${data?.agent?.color}-500`}
isLastMessage={index === data.chat.length - 1}
onDeleteMessage={handleDeleteMessage}
conversationId={props.conversationId}
/>
{chatMessage.trainOfThought && chatMessage.by === "khoj" && (
<TrainOfThoughtComponent
trainOfThought={chatMessage.trainOfThought?.map(
(train) => train.data,
)}
lastMessage={false}
agentColor={data?.agent?.color || "orange"}
key={`${index}trainOfThought`}
completed={true}
/>
)}
</>
))}
{props.incomingMessages &&
props.incomingMessages.map((message, index) => {
const messageTurnId = message.turnId ?? currentTurnId ?? undefined;
return (
<React.Fragment key={`incomingMessage${index}`}>
<ChatMessage
Expand All @@ -342,16 +371,22 @@ export default function ChatHistory(props: ChatHistoryProps) {
by: "you",
automationId: "",
images: message.images,
conversationId: props.conversationId,
turnId: messageTurnId,
}}
customClassName="fullHistory"
borderLeftColor={`${data?.agent?.color}-500`}
onDeleteMessage={handleDeleteMessage}
conversationId={props.conversationId}
turnId={messageTurnId}
/>
{message.trainOfThought && (
<TrainOfThoughtComponent
trainOfThought={message.trainOfThought}
lastMessage={index === incompleteIncomingMessageIndex}
agentColor={data?.agent?.color || "orange"}
key={`${index}trainOfThought`}
keyId={`${index}trainOfThought`}
completed={message.completed}
/>
)}
Expand All @@ -373,7 +408,12 @@ export default function ChatHistory(props: ChatHistoryProps) {
"memory-type": "",
"inferred-queries": message.inferredQueries || [],
},
conversationId: props.conversationId,
turnId: messageTurnId,
}}
conversationId={props.conversationId}
turnId={messageTurnId}
onDeleteMessage={handleDeleteMessage}
customClassName="fullHistory"
borderLeftColor={`${data?.agent?.color}-500`}
isLastMessage={true}
Expand All @@ -393,7 +433,11 @@ export default function ChatHistory(props: ChatHistoryProps) {
created: new Date().getTime().toString(),
by: "you",
automationId: "",
conversationId: props.conversationId,
turnId: undefined,
}}
conversationId={props.conversationId}
onDeleteMessage={handleDeleteMessage}
customClassName="fullHistory"
borderLeftColor={`${data?.agent?.color}-500`}
isLastMessage={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
}

let messageToSend = message.trim();
if (useResearchMode) {
if (useResearchMode && !messageToSend.startsWith("/research")) {
messageToSend = `/research ${messageToSend}`;
}

Expand Down Expand Up @@ -398,7 +398,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
<PopoverContent
onOpenAutoFocus={(e) => e.preventDefault()}
className={`${props.isMobileWidth ? "w-[100vw]" : "w-full"} rounded-md`}
side="top"
side="bottom"
align="center"
/* Offset below text area on home page (i.e where conversationId is unset) */
sideOffset={props.conversationId ? 0 : 80}
Expand Down Expand Up @@ -590,8 +590,8 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((pr
</Button>
</TooltipTrigger>
<TooltipContent className="text-xs">
Research Mode allows you to get more deeply researched, detailed
responses. Response times may be longer.
(Experimental) Research Mode allows you to get more deeply researched,
detailed responses. Response times may be longer.
</TooltipContent>
</Tooltip>
</TooltipProvider>
Expand Down
40 changes: 40 additions & 0 deletions src/interface/web/app/components/chatMessage/chatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
Check,
Code,
Shapes,
Trash,
} from "@phosphor-icons/react";

import DOMPurify from "dompurify";
Expand Down Expand Up @@ -146,6 +147,8 @@ export interface SingleChatMessage {
intent?: Intent;
agent?: AgentData;
images?: string[];
conversationId: string;
turnId?: string;
}

export interface StreamMessage {
Expand All @@ -161,6 +164,7 @@ export interface StreamMessage {
images?: string[];
intentType?: string;
inferredQueries?: string[];
turnId?: string;
}

export interface ChatHistoryData {
Expand Down Expand Up @@ -242,6 +246,9 @@ interface ChatMessageProps {
borderLeftColor?: string;
isLastMessage?: boolean;
agent?: AgentData;
onDeleteMessage: (turnId?: string) => void;
conversationId: string;
turnId?: string;
}

interface TrainOfThoughtProps {
Expand Down Expand Up @@ -654,6 +661,27 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
});
}

const deleteMessage = async (message: SingleChatMessage) => {
const turnId = message.turnId || props.turnId;
const response = await fetch("/api/chat/conversation/message", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
conversation_id: props.conversationId,
turn_id: turnId,
}),
});

if (response.ok) {
// Update the UI after successful deletion
props.onDeleteMessage(turnId);
} else {
console.error("Failed to delete message");
}
};

const allReferences = constructAllReferences(
props.chatMessage.context,
props.chatMessage.onlineContext,
Expand Down Expand Up @@ -716,6 +744,18 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
/>
</button>
))}
{props.chatMessage.turnId && (
<button
title="Delete"
className={`${styles.deleteButton}`}
onClick={() => deleteMessage(props.chatMessage)}
>
<Trash
alt="Delete Message"
className="hsl(var(--muted-foreground)) hover:text-red-500"
/>
</button>
)}
<button
title="Copy"
className={`${styles.copyButton}`}
Expand Down
Loading
Loading