feat: Add top_k Input Control to Search UI for Adjustable Graph Exploration Depth (#1202)

Fixes #1194 
Summary:
This PR introduces a new "Max results" (top_k) input control to the
search UI, allowing users to specify how many results to return for each
search. This directly controls the graph exploration depth, enabling
both focused and broad explorations.
Changes
UI Enhancement:
Added a number input labeled "Max results" (default: 10, min: 1, max:
100) to the search form, with validation and a tooltip explaining its
impact.
Placed the input between the search type dropdown and the submit button
in SearchView.tsx
State Management:
Managed top_k value in component state.
Included top_k in the form submission.
API Integration:
Updated useChat.ts to accept and send the top_k parameter in API calls
to the backend.
@Vasilije1990
This commit is contained in:
Vasilije 2025-08-19 16:22:46 +02:00 committed by GitHub
commit bf1970b679
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 46 additions and 16 deletions

View file

@ -14,7 +14,7 @@ const fetchMessages = () => {
.then(response => response.json());
};
const sendMessage = (message: string, searchType: string) => {
const sendMessage = (message: string, searchType: string, topK: number) => {
return fetch("/v1/search/", {
method: "POST",
headers: {
@ -24,6 +24,7 @@ const sendMessage = (message: string, searchType: string) => {
query: message,
searchType,
datasets: ["main_dataset"],
top_k: topK,
}),
})
.then(response => response.json());
@ -45,7 +46,7 @@ export default function useChat(dataset: Dataset) {
return setMessages(data);
}, []);
const handleMessageSending = useCallback((message: string, searchType: string) => {
const handleMessageSending = useCallback((message: string, searchType: string, topK: number) => {
const sentMessageId = v4();
setMessages((messages) => [
@ -59,7 +60,7 @@ export default function useChat(dataset: Dataset) {
disableSearchRun();
return sendMessage(message, searchType)
return sendMessage(message, searchType, topK)
.then(newMessages => {
setMessages((messages) => [
...messages,

View file

@ -4,7 +4,7 @@ import classNames from "classnames";
import { useCallback, useEffect, useRef, useState } from "react";
import { LoadingIndicator } from "@/ui/App";
import { CTAButton, Select, TextArea } from "@/ui/elements";
import { CTAButton, Select, TextArea, Input } from "@/ui/elements";
import useChat from "@/modules/chat/hooks/useChat";
import styles from "./SearchView.module.css";
@ -59,17 +59,28 @@ export default function SearchView() {
}, [refreshChat, scrollToBottom]);
const [searchInputValue, setSearchInputValue] = useState("");
// Add state for top_k
const [topK, setTopK] = useState(10);
const handleSearchInputChange = useCallback((value: string) => {
setSearchInputValue(value);
}, []);
// Add handler for top_k input
const handleTopKChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
let value = parseInt(e.target.value, 10);
if (isNaN(value)) value = 10;
if (value < 1) value = 1;
if (value > 100) value = 100;
setTopK(value);
}, []);
const handleChatMessageSubmit = useCallback((event: React.FormEvent<SearchFormPayload>) => {
event.preventDefault();
const formElements = event.currentTarget;
const searchType = formElements.searchType.value;
const chatInput = searchInputValue.trim();
if (chatInput === "") {
@ -79,10 +90,11 @@ export default function SearchView() {
scrollToBottom();
setSearchInputValue("");
sendMessage(chatInput, searchType)
// Pass topK to sendMessage
sendMessage(chatInput, searchType, topK)
.then(scrollToBottom)
}, [scrollToBottom, sendMessage, searchInputValue]);
}, [scrollToBottom, sendMessage, searchInputValue, topK]);
const chatFormRef = useRef<HTMLFormElement>(null);
@ -125,13 +137,30 @@ export default function SearchView() {
className="resize-none min-h-14 max-h-96 overflow-y-auto"
/>
<div className="flex flex-row items-center justify-between gap-4">
<div className="flex flex-row items-center gap-2">
<label className="text-gray-600 whitespace-nowrap">Search type:</label>
<Select name="searchType" defaultValue={searchOptions[0].value} className="max-w-2xs">
{searchOptions.map((option) => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</Select>
<div className="flex flex-row items-center gap-4">
<div className="flex flex-row items-center gap-2">
<label className="text-gray-600 whitespace-nowrap">Search type:</label>
<Select name="searchType" defaultValue={searchOptions[0].value} className="max-w-2xs">
{searchOptions.map((option) => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</Select>
</div>
<div className="flex flex-row items-center gap-2">
<label className="text-gray-600 whitespace-nowrap" title="Controls how many results to return. Smaller = focused, larger = broader graph exploration.">
Max results:
</label>
<Input
type="number"
name="topK"
min={1}
max={100}
value={topK}
onChange={handleTopKChange}
className="w-20"
title="Controls how many results to return. Smaller = focused, larger = broader graph exploration."
/>
</div>
</div>
<CTAButton disabled={isSearchRunning} type="submit">
{isSearchRunning? "Searching..." : "Search"}
@ -142,4 +171,4 @@ export default function SearchView() {
</form>
</div>
);
}
}