mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 20:35:24 +00:00
rename everything to use search scope
This commit is contained in:
parent
f9e0500ef8
commit
3b2dd51795
14 changed files with 200 additions and 225 deletions
|
|
@ -2,13 +2,12 @@
|
|||
|
||||
import { ResizablePanel } from '@/components/ui/resizable';
|
||||
import { ChatThread } from '@/features/chat/components/chatThread';
|
||||
import { LanguageModelInfo, SBChatMessage, SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from '@/features/chat/types';
|
||||
import { LanguageModelInfo, SBChatMessage, SearchScope, SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from '@/features/chat/types';
|
||||
import { RepositoryQuery, SearchContextQuery } from '@/lib/types';
|
||||
import { CreateUIMessage } from 'ai';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useChatId } from '../../useChatId';
|
||||
import { SearchScopeItem } from '@/features/chat/components/chatBox/searchScopeSelector';
|
||||
|
||||
interface ChatThreadPanelProps {
|
||||
languageModels: LanguageModelInfo[];
|
||||
|
|
@ -33,33 +32,12 @@ export const ChatThreadPanel = ({
|
|||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [inputMessage, setInputMessage] = useState<CreateUIMessage<SBChatMessage> | undefined>(undefined);
|
||||
|
||||
|
||||
// Use the last user's last message to determine what repos and contexts we should select by default.
|
||||
const lastUserMessage = messages.findLast((message) => message.role === "user");
|
||||
const defaultSelectedRepos = lastUserMessage?.metadata?.selectedRepos ?? [];
|
||||
const defaultSelectedReposets = lastUserMessage?.metadata?.selectedReposets ?? [];
|
||||
const defaultSelectedSearchScopes = lastUserMessage?.metadata?.selectedSearchScopes ?? [];
|
||||
const [selectedSearchScopes, setSelectedSearchScopes] = useState<SearchScope[]>(defaultSelectedSearchScopes);
|
||||
|
||||
const [selectedItems, setSelectedItems] = useState<SearchScopeItem[]>([
|
||||
...defaultSelectedRepos.map(repoName => {
|
||||
const repoInfo = repos.find(r => r.repoName === repoName);
|
||||
return {
|
||||
type: 'repo' as const,
|
||||
value: repoName,
|
||||
name: repoInfo?.repoDisplayName || repoName.split('/').pop() || repoName,
|
||||
codeHostType: repoInfo?.codeHostType || ''
|
||||
};
|
||||
}),
|
||||
...defaultSelectedReposets.map(reposetName => {
|
||||
const reposet = searchContexts.find(c => c.name === reposetName);
|
||||
return {
|
||||
type: 'reposet' as const,
|
||||
value: reposetName,
|
||||
name: reposetName,
|
||||
repoCount: reposet?.repoNames.length || 0
|
||||
};
|
||||
})
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const setChatState = searchParams.get(SET_CHAT_STATE_QUERY_PARAM);
|
||||
if (!setChatState) {
|
||||
|
|
@ -67,28 +45,9 @@ export const ChatThreadPanel = ({
|
|||
}
|
||||
|
||||
try {
|
||||
const { inputMessage, selectedRepos, selectedReposets } = JSON.parse(setChatState) as SetChatStatePayload;
|
||||
const { inputMessage, selectedSearchScopes } = JSON.parse(setChatState) as SetChatStatePayload;
|
||||
setInputMessage(inputMessage);
|
||||
setSelectedItems([
|
||||
...selectedRepos.map(repoName => {
|
||||
const repoInfo = repos.find(r => r.repoName === repoName);
|
||||
return {
|
||||
type: 'repo' as const,
|
||||
value: repoName,
|
||||
name: repoInfo?.repoDisplayName || repoName.split('/').pop() || repoName,
|
||||
codeHostType: repoInfo?.codeHostType || ''
|
||||
};
|
||||
}),
|
||||
...selectedReposets.map(reposetName => {
|
||||
const reposet = searchContexts.find(c => c.name === reposetName);
|
||||
return {
|
||||
type: 'reposet' as const,
|
||||
value: reposetName,
|
||||
name: reposetName,
|
||||
repoCount: reposet?.repoNames.length || 0
|
||||
};
|
||||
})
|
||||
]);
|
||||
setSelectedSearchScopes(selectedSearchScopes);
|
||||
} catch {
|
||||
console.error('Invalid message in URL');
|
||||
}
|
||||
|
|
@ -97,7 +56,7 @@ export const ChatThreadPanel = ({
|
|||
const newSearchParams = new URLSearchParams(searchParams.toString());
|
||||
newSearchParams.delete(SET_CHAT_STATE_QUERY_PARAM);
|
||||
router.replace(`?${newSearchParams.toString()}`, { scroll: false });
|
||||
}, [searchParams, router, repos, searchContexts]);
|
||||
}, [searchParams, router]);
|
||||
|
||||
return (
|
||||
<ResizablePanel
|
||||
|
|
@ -113,8 +72,8 @@ export const ChatThreadPanel = ({
|
|||
languageModels={languageModels}
|
||||
repos={repos}
|
||||
searchContexts={searchContexts}
|
||||
selectedItems={selectedItems}
|
||||
onSelectedItemsChange={setSelectedItems}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
onSelectedSearchScopesChange={setSelectedSearchScopes}
|
||||
isChatReadonly={isChatReadonly}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@ import { ChatBox } from "@/features/chat/components/chatBox";
|
|||
import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolbar";
|
||||
import { CustomSlateEditor } from "@/features/chat/customSlateEditor";
|
||||
import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread";
|
||||
import { LanguageModelInfo } from "@/features/chat/types";
|
||||
import { LanguageModelInfo, SearchScope } from "@/features/chat/types";
|
||||
import { RepositoryQuery, SearchContextQuery } from "@/lib/types";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Descendant } from "slate";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import { SearchScopeItem } from "@/features/chat/components/chatBox/searchScopeSelector";
|
||||
|
||||
interface NewChatPanelProps {
|
||||
languageModels: LanguageModelInfo[];
|
||||
|
|
@ -25,13 +24,13 @@ export const NewChatPanel = ({
|
|||
searchContexts,
|
||||
order,
|
||||
}: NewChatPanelProps) => {
|
||||
const [selectedItems, setSelectedItems] = useLocalStorage<SearchScopeItem[]>("selectedContextItems", [], { initializeWithValue: false });
|
||||
const [selectedSearchScopes, setSelectedSearchScopes] = useLocalStorage<SearchScope[]>("selectedSearchScopes", [], { initializeWithValue: false });
|
||||
const { createNewChatThread, isLoading } = useCreateNewChatThread();
|
||||
const [isContextSelectorOpen, setIsContextSelectorOpen] = useState(false);
|
||||
|
||||
const onSubmit = useCallback((children: Descendant[]) => {
|
||||
createNewChatThread(children, selectedItems);
|
||||
}, [createNewChatThread, selectedItems]);
|
||||
createNewChatThread(children, selectedSearchScopes);
|
||||
}, [createNewChatThread, selectedSearchScopes]);
|
||||
|
||||
|
||||
return (
|
||||
|
|
@ -50,7 +49,7 @@ export const NewChatPanel = ({
|
|||
preferredSuggestionsBoxPlacement="bottom-start"
|
||||
isRedirecting={isLoading}
|
||||
languageModels={languageModels}
|
||||
selectedItems={selectedItems}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
searchContexts={searchContexts}
|
||||
onContextSelectorOpenChanged={setIsContextSelectorOpen}
|
||||
/>
|
||||
|
|
@ -59,8 +58,8 @@ export const NewChatPanel = ({
|
|||
languageModels={languageModels}
|
||||
repos={repos}
|
||||
searchContexts={searchContexts}
|
||||
selectedItems={selectedItems}
|
||||
onSelectedItemsChange={setSelectedItems}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
onSelectedSearchScopesChange={setSelectedSearchScopes}
|
||||
isContextSelectorOpen={isContextSelectorOpen}
|
||||
onContextSelectorOpenChanged={setIsContextSelectorOpen}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@
|
|||
import { Separator } from "@/components/ui/separator";
|
||||
import { ChatBox } from "@/features/chat/components/chatBox";
|
||||
import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolbar";
|
||||
import { LanguageModelInfo } from "@/features/chat/types";
|
||||
import { LanguageModelInfo, SearchScope } from "@/features/chat/types";
|
||||
import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread";
|
||||
import { RepositoryQuery, SearchContextQuery } from "@/lib/types";
|
||||
import { useState } from "react";
|
||||
import { SearchModeSelector, SearchModeSelectorProps } from "./toolbar";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import { SearchScopeItem } from "@/features/chat/components/chatBox/searchScopeSelector";
|
||||
import { DemoExamples } from "@/types";
|
||||
import { AskSourcebotDemoCards } from "./askSourcebotDemoCards";
|
||||
|
||||
|
|
@ -34,7 +33,7 @@ export const AgenticSearch = ({
|
|||
demoExamples,
|
||||
}: AgenticSearchProps) => {
|
||||
const { createNewChatThread, isLoading } = useCreateNewChatThread();
|
||||
const [selectedItems, setSelectedItems] = useLocalStorage<SearchScopeItem[]>("selectedContextItems", [], { initializeWithValue: false });
|
||||
const [selectedSearchScopes, setSelectedSearchScopes] = useLocalStorage<SearchScope[]>("selectedSearchScopes", [], { initializeWithValue: false });
|
||||
const [isContextSelectorOpen, setIsContextSelectorOpen] = useState(false);
|
||||
|
||||
return (
|
||||
|
|
@ -42,12 +41,12 @@ export const AgenticSearch = ({
|
|||
<div className="mt-4 w-full border rounded-md shadow-sm max-w-[800px]">
|
||||
<ChatBox
|
||||
onSubmit={(children) => {
|
||||
createNewChatThread(children, selectedItems);
|
||||
createNewChatThread(children, selectedSearchScopes);
|
||||
}}
|
||||
className="min-h-[50px]"
|
||||
isRedirecting={isLoading}
|
||||
languageModels={languageModels}
|
||||
selectedItems={selectedItems}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
searchContexts={searchContexts}
|
||||
onContextSelectorOpenChanged={setIsContextSelectorOpen}
|
||||
/>
|
||||
|
|
@ -58,8 +57,8 @@ export const AgenticSearch = ({
|
|||
languageModels={languageModels}
|
||||
repos={repos}
|
||||
searchContexts={searchContexts}
|
||||
selectedItems={selectedItems}
|
||||
onSelectedItemsChange={setSelectedItems}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
onSelectedSearchScopesChange={setSelectedSearchScopes}
|
||||
isContextSelectorOpen={isContextSelectorOpen}
|
||||
onContextSelectorOpenChanged={setIsContextSelectorOpen}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { sew, withAuth, withOrgMembership } from "@/actions";
|
|||
import { env } from "@/env.mjs";
|
||||
import { _getConfiguredLanguageModelsFull, updateChatMessages, updateChatName } from "@/features/chat/actions";
|
||||
import { createAgentStream } from "@/features/chat/agent";
|
||||
import { additionalChatRequestParamsSchema, SBChatMessage } from "@/features/chat/types";
|
||||
import { additionalChatRequestParamsSchema, SBChatMessage, SearchScope } from "@/features/chat/types";
|
||||
import { getAnswerPartFromAssistantMessage } from "@/features/chat/utils";
|
||||
import { ErrorCode } from "@/lib/errorCodes";
|
||||
import { notFound, schemaValidationError, serviceErrorResponse } from "@/lib/serviceError";
|
||||
|
|
@ -64,12 +64,11 @@ export async function POST(req: Request) {
|
|||
return serviceErrorResponse(schemaValidationError(parsed.error));
|
||||
}
|
||||
|
||||
const { messages, id, selectedRepos, selectedReposets, languageModelId } = parsed.data;
|
||||
const { messages, id, selectedSearchScopes, languageModelId } = parsed.data;
|
||||
const response = await chatHandler({
|
||||
messages,
|
||||
id,
|
||||
selectedRepos,
|
||||
selectedReposets,
|
||||
selectedSearchScopes,
|
||||
languageModelId,
|
||||
}, domain);
|
||||
|
||||
|
|
@ -93,12 +92,11 @@ const mergeStreamAsync = async (stream: StreamTextResult<any, any>, writer: UIMe
|
|||
interface ChatHandlerProps {
|
||||
messages: SBChatMessage[];
|
||||
id: string;
|
||||
selectedRepos: string[];
|
||||
selectedReposets?: string[];
|
||||
selectedSearchScopes: SearchScope[];
|
||||
languageModelId: string;
|
||||
}
|
||||
|
||||
const chatHandler = ({ messages, id, selectedRepos, selectedReposets, languageModelId }: ChatHandlerProps, domain: string) => sew(async () =>
|
||||
const chatHandler = ({ messages, id, selectedSearchScopes, languageModelId }: ChatHandlerProps, domain: string) => sew(async () =>
|
||||
withAuth((userId) =>
|
||||
withOrgMembership(userId, domain, async ({ org }) => {
|
||||
const chat = await prisma.chat.findUnique({
|
||||
|
|
@ -188,26 +186,30 @@ const chatHandler = ({ messages, id, selectedRepos, selectedReposets, languageMo
|
|||
|
||||
const startTime = new Date();
|
||||
|
||||
// Expand search contexts to repos
|
||||
let expandedRepos = [...selectedRepos];
|
||||
if (selectedReposets && selectedReposets.length > 0) {
|
||||
const searchReposets = await prisma.searchContext.findMany({
|
||||
where: {
|
||||
orgId: org.id,
|
||||
name: { in: selectedReposets }
|
||||
},
|
||||
include: {
|
||||
repos: true
|
||||
const expandedReposArrays = await Promise.all(selectedSearchScopes.map(async (scope) => {
|
||||
if (scope.type === 'repo') {
|
||||
return [scope.value];
|
||||
}
|
||||
|
||||
if (scope.type === 'reposet') {
|
||||
const reposet = await prisma.searchContext.findFirst({
|
||||
where: {
|
||||
orgId: org.id,
|
||||
name: scope.value
|
||||
},
|
||||
include: {
|
||||
repos: true
|
||||
}
|
||||
});
|
||||
|
||||
if (reposet) {
|
||||
return reposet.repos.map(repo => repo.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const reposetRepos = searchReposets.flatMap(reposet =>
|
||||
reposet.repos.map(repo => repo.name)
|
||||
);
|
||||
|
||||
// Combine and deduplicate repos
|
||||
expandedRepos = Array.from(new Set([...selectedRepos, ...reposetRepos]));
|
||||
}
|
||||
return [];
|
||||
}));
|
||||
const expandedRepos = expandedReposArrays.flat();
|
||||
|
||||
const researchStream = await createAgentStream({
|
||||
model,
|
||||
|
|
@ -215,7 +217,7 @@ const chatHandler = ({ messages, id, selectedRepos, selectedReposets, languageMo
|
|||
headers,
|
||||
inputMessages: messageHistory,
|
||||
inputSources: sources,
|
||||
selectedRepos: expandedRepos,
|
||||
searchScopeRepoNames: expandedRepos,
|
||||
onWriteSource: (source) => {
|
||||
writer.write({
|
||||
type: 'data-source',
|
||||
|
|
@ -241,6 +243,7 @@ const chatHandler = ({ messages, id, selectedRepos, selectedReposets, languageMo
|
|||
totalOutputTokens: totalUsage.outputTokens,
|
||||
totalResponseTimeMs: new Date().getTime() - startTime.getTime(),
|
||||
modelName: languageModelConfig.displayName ?? languageModelConfig.model,
|
||||
selectedSearchScopes,
|
||||
traceId,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ interface AgentOptions {
|
|||
model: LanguageModel;
|
||||
providerOptions?: ProviderOptions;
|
||||
headers?: Record<string, string>;
|
||||
selectedRepos: string[];
|
||||
searchScopeRepoNames: string[];
|
||||
inputMessages: ModelMessage[];
|
||||
inputSources: Source[];
|
||||
onWriteSource: (source: Source) => void;
|
||||
|
|
@ -35,12 +35,12 @@ export const createAgentStream = async ({
|
|||
headers,
|
||||
inputMessages,
|
||||
inputSources,
|
||||
selectedRepos,
|
||||
searchScopeRepoNames,
|
||||
onWriteSource,
|
||||
traceId,
|
||||
}: AgentOptions) => {
|
||||
const baseSystemPrompt = createBaseSystemPrompt({
|
||||
selectedRepos,
|
||||
searchScopeRepoNames,
|
||||
});
|
||||
|
||||
const stream = streamText({
|
||||
|
|
@ -50,7 +50,7 @@ export const createAgentStream = async ({
|
|||
system: baseSystemPrompt,
|
||||
messages: inputMessages,
|
||||
tools: {
|
||||
[toolNames.searchCode]: createCodeSearchTool(selectedRepos),
|
||||
[toolNames.searchCode]: createCodeSearchTool(searchScopeRepoNames),
|
||||
[toolNames.readFiles]: readFilesTool,
|
||||
[toolNames.findSymbolReferences]: findSymbolReferencesTool,
|
||||
[toolNames.findSymbolDefinitions]: findSymbolDefinitionsTool,
|
||||
|
|
@ -150,11 +150,11 @@ export const createAgentStream = async ({
|
|||
}
|
||||
|
||||
interface BaseSystemPromptOptions {
|
||||
selectedRepos: string[];
|
||||
searchScopeRepoNames: string[];
|
||||
}
|
||||
|
||||
export const createBaseSystemPrompt = ({
|
||||
selectedRepos,
|
||||
searchScopeRepoNames,
|
||||
}: BaseSystemPromptOptions) => {
|
||||
return `
|
||||
You are a powerful agentic AI code assistant built into Sourcebot, the world's best code-intelligence platform. Your job is to help developers understand and navigate their large codebases.
|
||||
|
|
@ -176,7 +176,7 @@ Your workflow has two distinct phases:
|
|||
|
||||
<available_repositories>
|
||||
The user has selected the following repositories for analysis:
|
||||
${selectedRepos.map(repo => `- ${repo}`).join('\n')}
|
||||
${searchScopeRepoNames.map(repo => `- ${repo}`).join('\n')}
|
||||
</available_repositories>
|
||||
|
||||
<research_phase_instructions>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { VscodeFileIcon } from "@/app/components/vscodeFileIcon";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { CustomEditor, LanguageModelInfo, MentionElement, RenderElementPropsFor } from "@/features/chat/types";
|
||||
import { CustomEditor, LanguageModelInfo, MentionElement, RenderElementPropsFor, SearchScope } from "@/features/chat/types";
|
||||
import { insertMention, slateContentToString } from "@/features/chat/utils";
|
||||
import { cn, IS_MAC } from "@/lib/utils";
|
||||
import { computePosition, flip, offset, shift, VirtualElement } from "@floating-ui/react";
|
||||
|
|
@ -18,7 +18,6 @@ import { Suggestion } from "./types";
|
|||
import { useSuggestionModeAndQuery } from "./useSuggestionModeAndQuery";
|
||||
import { useSuggestionsData } from "./useSuggestionsData";
|
||||
import { useToast } from "@/components/hooks/use-toast";
|
||||
import { SearchScopeItem } from "./searchScopeSelector";
|
||||
import { SearchContextQuery } from "@/lib/types";
|
||||
|
||||
interface ChatBoxProps {
|
||||
|
|
@ -29,7 +28,7 @@ interface ChatBoxProps {
|
|||
isRedirecting?: boolean;
|
||||
isGenerating?: boolean;
|
||||
languageModels: LanguageModelInfo[];
|
||||
selectedItems: SearchScopeItem[];
|
||||
selectedSearchScopes: SearchScope[];
|
||||
searchContexts: SearchContextQuery[];
|
||||
onContextSelectorOpenChanged: (isOpen: boolean) => void;
|
||||
}
|
||||
|
|
@ -42,7 +41,7 @@ export const ChatBox = ({
|
|||
isRedirecting,
|
||||
isGenerating,
|
||||
languageModels,
|
||||
selectedItems,
|
||||
selectedSearchScopes,
|
||||
searchContexts,
|
||||
onContextSelectorOpenChanged,
|
||||
}: ChatBoxProps) => {
|
||||
|
|
@ -53,7 +52,7 @@ export const ChatBox = ({
|
|||
const { suggestions, isLoading } = useSuggestionsData({
|
||||
suggestionMode,
|
||||
suggestionQuery,
|
||||
selectedRepos: selectedItems.map((item) => {
|
||||
selectedRepos: selectedSearchScopes.map((item) => {
|
||||
if (item.type === 'repo') {
|
||||
return [item.value];
|
||||
}
|
||||
|
|
@ -130,7 +129,7 @@ export const ChatBox = ({
|
|||
}
|
||||
}
|
||||
|
||||
if (selectedItems.length === 0) {
|
||||
if (selectedSearchScopes.length === 0) {
|
||||
return {
|
||||
isSubmitDisabled: true,
|
||||
isSubmitDisabledReason: "no-repos-selected",
|
||||
|
|
@ -154,7 +153,7 @@ export const ChatBox = ({
|
|||
editor.children,
|
||||
isRedirecting,
|
||||
isGenerating,
|
||||
selectedItems.length,
|
||||
selectedSearchScopes.length,
|
||||
selectedLanguageModel,
|
||||
])
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { LanguageModelInfo } from "@/features/chat/types";
|
||||
import { LanguageModelInfo, SearchScope } from "@/features/chat/types";
|
||||
import { RepositoryQuery, SearchContextQuery } from "@/lib/types";
|
||||
import { AtSignIcon } from "lucide-react";
|
||||
import { useCallback } from "react";
|
||||
import { ReactEditor, useSlate } from "slate-react";
|
||||
import { useSelectedLanguageModel } from "../../useSelectedLanguageModel";
|
||||
import { LanguageModelSelector } from "./languageModelSelector";
|
||||
import { SearchScopeSelector, type SearchScopeItem } from "./searchScopeSelector";
|
||||
import { SearchScopeSelector } from "./searchScopeSelector";
|
||||
import { SearchScopeInfoCard } from "@/components/searchScopeInfoCard";
|
||||
import { AtMentionInfoCard } from "@/components/atMentionInfoCard";
|
||||
|
||||
|
|
@ -18,8 +18,8 @@ export interface ChatBoxToolbarProps {
|
|||
languageModels: LanguageModelInfo[];
|
||||
repos: RepositoryQuery[];
|
||||
searchContexts: SearchContextQuery[];
|
||||
selectedItems: SearchScopeItem[];
|
||||
onSelectedItemsChange: (items: SearchScopeItem[]) => void;
|
||||
selectedSearchScopes: SearchScope[];
|
||||
onSelectedSearchScopesChange: (items: SearchScope[]) => void;
|
||||
isContextSelectorOpen: boolean;
|
||||
onContextSelectorOpenChanged: (isOpen: boolean) => void;
|
||||
}
|
||||
|
|
@ -28,8 +28,8 @@ export const ChatBoxToolbar = ({
|
|||
languageModels,
|
||||
repos,
|
||||
searchContexts,
|
||||
selectedItems,
|
||||
onSelectedItemsChange,
|
||||
selectedSearchScopes,
|
||||
onSelectedSearchScopesChange,
|
||||
isContextSelectorOpen,
|
||||
onContextSelectorOpenChanged,
|
||||
}: ChatBoxToolbarProps) => {
|
||||
|
|
@ -68,8 +68,8 @@ export const ChatBoxToolbar = ({
|
|||
className="bg-inherit w-fit h-6 min-h-6"
|
||||
repos={repos}
|
||||
searchContexts={searchContexts}
|
||||
selectedItems={selectedItems}
|
||||
onSelectedItemsChange={onSelectedItemsChange}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
onSelectedSearchScopesChange={onSelectedSearchScopesChange}
|
||||
isOpen={isContextSelectorOpen}
|
||||
onOpenChanged={onContextSelectorOpenChanged}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -4,13 +4,10 @@ import * as React from "react";
|
|||
import {
|
||||
CheckIcon,
|
||||
ChevronDown,
|
||||
FolderIcon,
|
||||
ScanSearchIcon,
|
||||
LibraryBigIcon,
|
||||
} from "lucide-react";
|
||||
import Image from "next/image";
|
||||
|
||||
import { cn, getCodeHostIcon } from "@/lib/utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { RepositoryQuery, SearchContextQuery } from "@/lib/types";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
|
@ -28,28 +25,14 @@ import {
|
|||
CommandList,
|
||||
CommandSeparator,
|
||||
} from "@/components/ui/command";
|
||||
|
||||
export type RepoSearchScopeItem = {
|
||||
type: 'repo';
|
||||
value: string;
|
||||
name: string;
|
||||
codeHostType: string;
|
||||
}
|
||||
|
||||
export type RepoSetSearchScopeItem = {
|
||||
type: 'reposet';
|
||||
value: string;
|
||||
name: string;
|
||||
repoCount: number;
|
||||
}
|
||||
|
||||
export type SearchScopeItem = RepoSearchScopeItem | RepoSetSearchScopeItem;
|
||||
import { RepoSetSearchScope, RepoSearchScope, SearchScope } from "../../types";
|
||||
import { SearchScopeIcon } from "../searchScopeIcon";
|
||||
|
||||
interface SearchScopeSelectorProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
repos: RepositoryQuery[];
|
||||
searchContexts: SearchContextQuery[];
|
||||
selectedItems: SearchScopeItem[];
|
||||
onSelectedItemsChange: (items: SearchScopeItem[]) => void;
|
||||
selectedSearchScopes: SearchScope[];
|
||||
onSelectedSearchScopesChange: (items: SearchScope[]) => void;
|
||||
className?: string;
|
||||
isOpen: boolean;
|
||||
onOpenChanged: (isOpen: boolean) => void;
|
||||
|
|
@ -63,9 +46,9 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
{
|
||||
repos,
|
||||
searchContexts,
|
||||
onSelectedItemsChange,
|
||||
className,
|
||||
selectedItems,
|
||||
selectedSearchScopes,
|
||||
onSelectedSearchScopesChange,
|
||||
isOpen,
|
||||
onOpenChanged,
|
||||
...props
|
||||
|
|
@ -81,43 +64,33 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
if (event.key === "Enter") {
|
||||
onOpenChanged(true);
|
||||
} else if (event.key === "Backspace" && !event.currentTarget.value) {
|
||||
const newSelectedItems = [...selectedItems];
|
||||
const newSelectedItems = [...selectedSearchScopes];
|
||||
newSelectedItems.pop();
|
||||
onSelectedItemsChange(newSelectedItems);
|
||||
onSelectedSearchScopesChange(newSelectedItems);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleItem = (item: SearchScopeItem) => {
|
||||
const toggleItem = (item: SearchScope) => {
|
||||
// Store current scroll position before state update
|
||||
if (scrollContainerRef.current) {
|
||||
scrollPosition.current = scrollContainerRef.current.scrollTop;
|
||||
}
|
||||
|
||||
const isSelected = selectedItems.some(
|
||||
const isSelected = selectedSearchScopes.some(
|
||||
(selected) => selected.type === item.type && selected.value === item.value
|
||||
);
|
||||
|
||||
const isDemoMode = process.env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT === "demo";
|
||||
|
||||
let newSelectedItems: SearchScopeItem[];
|
||||
if (isSelected) {
|
||||
newSelectedItems = selectedItems.filter(
|
||||
const newSelectedItems = isSelected ?
|
||||
selectedSearchScopes.filter(
|
||||
(selected) => !(selected.type === item.type && selected.value === item.value)
|
||||
);
|
||||
} else {
|
||||
// Limit selected search scope to 1 in demo mode
|
||||
if (isDemoMode) {
|
||||
newSelectedItems = [item];
|
||||
} else {
|
||||
newSelectedItems = [...selectedItems, item];
|
||||
}
|
||||
}
|
||||
) :
|
||||
[...selectedSearchScopes, item];
|
||||
|
||||
onSelectedItemsChange(newSelectedItems);
|
||||
onSelectedSearchScopesChange(newSelectedItems);
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
onSelectedItemsChange([]);
|
||||
onSelectedSearchScopesChange([]);
|
||||
};
|
||||
|
||||
const handleTogglePopover = () => {
|
||||
|
|
@ -125,14 +98,14 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
};
|
||||
|
||||
const allSearchScopeItems = React.useMemo(() => {
|
||||
const repoSetSearchScopeItems: RepoSetSearchScopeItem[] = searchContexts.map(context => ({
|
||||
const repoSetSearchScopeItems: RepoSetSearchScope[] = searchContexts.map(context => ({
|
||||
type: 'reposet' as const,
|
||||
value: context.name,
|
||||
name: context.name,
|
||||
repoCount: context.repoNames.length
|
||||
}));
|
||||
|
||||
const repoSearchScopeItems: RepoSearchScopeItem[] = repos.map(repo => ({
|
||||
const repoSearchScopeItems: RepoSearchScope[] = repos.map(repo => ({
|
||||
type: 'repo' as const,
|
||||
value: repo.repoName,
|
||||
name: repo.repoDisplayName || repo.repoName.split('/').pop() || repo.repoName,
|
||||
|
|
@ -146,7 +119,7 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
return allSearchScopeItems
|
||||
.map((item) => ({
|
||||
item,
|
||||
isSelected: selectedItems.some(
|
||||
isSelected: selectedSearchScopes.some(
|
||||
(selected) => selected.type === item.type && selected.value === item.value
|
||||
)
|
||||
}))
|
||||
|
|
@ -159,7 +132,7 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
if (a.item.type === 'repo' && b.item.type === 'reposet') return 1;
|
||||
return 0;
|
||||
})
|
||||
}, [allSearchScopeItems, selectedItems]);
|
||||
}, [allSearchScopeItems, selectedSearchScopes]);
|
||||
|
||||
// Restore scroll position after re-render
|
||||
React.useEffect(() => {
|
||||
|
|
@ -189,9 +162,9 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
className={cn("text-sm text-muted-foreground mx-1 font-medium")}
|
||||
>
|
||||
{
|
||||
selectedItems.length === 0 ? `Search scopes` :
|
||||
selectedItems.length === 1 ? selectedItems[0].name :
|
||||
`${selectedItems.length} selected`
|
||||
selectedSearchScopes.length === 0 ? `Search scopes` :
|
||||
selectedSearchScopes.length === 1 ? selectedSearchScopes[0].name :
|
||||
`${selectedSearchScopes.length} selected`
|
||||
}
|
||||
</span>
|
||||
<ChevronDown className="h-4 cursor-pointer text-muted-foreground ml-2" />
|
||||
|
|
@ -229,25 +202,7 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
<CheckIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
{item.type === 'reposet' ? (
|
||||
<LibraryBigIcon className="h-4 w-4 text-muted-foreground" />
|
||||
) : (
|
||||
// Render code host icon for repos
|
||||
(() => {
|
||||
const codeHostIcon = item.codeHostType ? getCodeHostIcon(item.codeHostType) : null;
|
||||
return codeHostIcon ? (
|
||||
<Image
|
||||
src={codeHostIcon.src}
|
||||
alt={`${item.codeHostType} icon`}
|
||||
width={16}
|
||||
height={16}
|
||||
className={cn("h-4 w-4", codeHostIcon.className)}
|
||||
/>
|
||||
) : (
|
||||
<FolderIcon className="h-4 w-4 text-muted-foreground" />
|
||||
);
|
||||
})()
|
||||
)}
|
||||
<SearchScopeIcon searchScope={item} />
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">
|
||||
|
|
@ -269,7 +224,7 @@ export const SearchScopeSelector = React.forwardRef<
|
|||
})}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
{selectedItems.length > 0 && (
|
||||
{selectedSearchScopes.length > 0 && (
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<CommandItem
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ import { Button } from '@/components/ui/button';
|
|||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { CustomSlateEditor } from '@/features/chat/customSlateEditor';
|
||||
import { AdditionalChatRequestParams, CustomEditor, LanguageModelInfo, SBChatMessage, Source } from '@/features/chat/types';
|
||||
import { AdditionalChatRequestParams, CustomEditor, LanguageModelInfo, SBChatMessage, SearchScope, Source } from '@/features/chat/types';
|
||||
import { createUIMessage, getAllMentionElements, resetEditor, slateContentToString } from '@/features/chat/utils';
|
||||
import { useDomain } from '@/hooks/useDomain';
|
||||
import { useChat } from '@ai-sdk/react';
|
||||
import { CreateUIMessage, DefaultChatTransport } from 'ai';
|
||||
import { ArrowDownIcon } from 'lucide-react';
|
||||
import { useNavigationGuard } from 'next-navigation-guard';
|
||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Descendant } from 'slate';
|
||||
import { useMessagePairs } from '../../useMessagePairs';
|
||||
import { useSelectedLanguageModel } from '../../useSelectedLanguageModel';
|
||||
|
|
@ -23,7 +23,6 @@ import { ErrorBanner } from './errorBanner';
|
|||
import { useRouter } from 'next/navigation';
|
||||
import { usePrevious } from '@uidotdev/usehooks';
|
||||
import { RepositoryQuery, SearchContextQuery } from '@/lib/types';
|
||||
import { SearchScopeItem } from '../chatBox/searchScopeSelector';
|
||||
|
||||
type ChatHistoryState = {
|
||||
scrollOffset?: number;
|
||||
|
|
@ -36,8 +35,8 @@ interface ChatThreadProps {
|
|||
languageModels: LanguageModelInfo[];
|
||||
repos: RepositoryQuery[];
|
||||
searchContexts: SearchContextQuery[];
|
||||
selectedItems: SearchScopeItem[];
|
||||
onSelectedItemsChange: (items: SearchScopeItem[]) => void;
|
||||
selectedSearchScopes: SearchScope[];
|
||||
onSelectedSearchScopesChange: (items: SearchScope[]) => void;
|
||||
isChatReadonly: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -48,8 +47,8 @@ export const ChatThread = ({
|
|||
languageModels,
|
||||
repos,
|
||||
searchContexts,
|
||||
selectedItems,
|
||||
onSelectedItemsChange,
|
||||
selectedSearchScopes,
|
||||
onSelectedSearchScopesChange,
|
||||
isChatReadonly,
|
||||
}: ChatThreadProps) => {
|
||||
const domain = useDomain();
|
||||
|
|
@ -62,12 +61,6 @@ export const ChatThread = ({
|
|||
const router = useRouter();
|
||||
const [isContextSelectorOpen, setIsContextSelectorOpen] = useState(false);
|
||||
|
||||
const { selectedRepos, selectedReposets } = useMemo(() => {
|
||||
const repos = selectedItems.filter(item => item.type === 'repo').map(item => item.value);
|
||||
const reposets = selectedItems.filter(item => item.type === 'reposet').map(item => item.value);
|
||||
return { selectedRepos: repos, selectedReposets: reposets };
|
||||
}, [selectedItems]);
|
||||
|
||||
// Initial state is from attachments that exist in in the chat history.
|
||||
const [sources, setSources] = useState<Source[]>(
|
||||
initialMessages?.flatMap((message) =>
|
||||
|
|
@ -122,12 +115,11 @@ export const ChatThread = ({
|
|||
|
||||
_sendMessage(message, {
|
||||
body: {
|
||||
selectedRepos,
|
||||
selectedReposets,
|
||||
selectedSearchScopes,
|
||||
languageModelId: selectedLanguageModel.model,
|
||||
} satisfies AdditionalChatRequestParams,
|
||||
});
|
||||
}, [_sendMessage, selectedLanguageModel, toast, selectedRepos, selectedReposets]);
|
||||
}, [_sendMessage, selectedLanguageModel, toast, selectedSearchScopes]);
|
||||
|
||||
|
||||
const messagePairs = useMessagePairs(messages);
|
||||
|
|
@ -243,13 +235,13 @@ export const ChatThread = ({
|
|||
const text = slateContentToString(children);
|
||||
const mentions = getAllMentionElements(children);
|
||||
|
||||
const message = createUIMessage(text, mentions.map(({ data }) => data), selectedRepos, selectedReposets);
|
||||
const message = createUIMessage(text, mentions.map(({ data }) => data), selectedSearchScopes);
|
||||
sendMessage(message);
|
||||
|
||||
setIsAutoScrollEnabled(true);
|
||||
|
||||
resetEditor(editor);
|
||||
}, [sendMessage, selectedRepos, selectedReposets]);
|
||||
}, [sendMessage, selectedSearchScopes]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -327,7 +319,7 @@ export const ChatThread = ({
|
|||
isGenerating={status === "streaming" || status === "submitted"}
|
||||
onStop={stop}
|
||||
languageModels={languageModels}
|
||||
selectedItems={selectedItems}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
searchContexts={searchContexts}
|
||||
onContextSelectorOpenChanged={setIsContextSelectorOpen}
|
||||
/>
|
||||
|
|
@ -336,8 +328,8 @@ export const ChatThread = ({
|
|||
languageModels={languageModels}
|
||||
repos={repos}
|
||||
searchContexts={searchContexts}
|
||||
selectedItems={selectedItems}
|
||||
onSelectedItemsChange={onSelectedItemsChange}
|
||||
selectedSearchScopes={selectedSearchScopes}
|
||||
onSelectedSearchScopesChange={onSelectedSearchScopesChange}
|
||||
isContextSelectorOpen={isContextSelectorOpen}
|
||||
onContextSelectorOpenChanged={setIsContextSelectorOpen}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,16 @@ import { Card, CardContent } from '@/components/ui/card';
|
|||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Brain, ChevronDown, ChevronRight, Clock, Cpu, InfoIcon, Loader2, Zap } from 'lucide-react';
|
||||
import { Brain, ChevronDown, ChevronRight, Clock, Cpu, InfoIcon, Loader2, ScanSearchIcon, Zap } from 'lucide-react';
|
||||
import { MarkdownRenderer } from './markdownRenderer';
|
||||
import { FindSymbolDefinitionsToolComponent } from './tools/findSymbolDefinitionsToolComponent';
|
||||
import { FindSymbolReferencesToolComponent } from './tools/findSymbolReferencesToolComponent';
|
||||
import { ReadFilesToolComponent } from './tools/readFilesToolComponent';
|
||||
import { SearchCodeToolComponent } from './tools/searchCodeToolComponent';
|
||||
import { SBChatMessageMetadata, SBChatMessagePart } from '../../types';
|
||||
import { SearchScopeIcon } from '../searchScopeIcon';
|
||||
|
||||
|
||||
interface DetailsCardProps {
|
||||
|
|
@ -61,6 +63,28 @@ export const DetailsCard = ({
|
|||
{!isStreaming && (
|
||||
<>
|
||||
<Separator orientation="vertical" className="h-4" />
|
||||
{metadata?.selectedSearchScopes && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center text-xs cursor-help">
|
||||
<ScanSearchIcon className="w-3 h-3 mr-1 flex-shrink-0" />
|
||||
{metadata.selectedSearchScopes.length} search scope{metadata.selectedSearchScopes.length === 1 ? '' : 's'}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<div className="max-w-xs">
|
||||
<div className="space-y-2">
|
||||
{metadata.selectedSearchScopes.map((item) => (
|
||||
<div key={item.value} className="flex items-center gap-2 text-xs">
|
||||
<SearchScopeIcon searchScope={item} className="h-3 w-3" />
|
||||
<span>{item.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
{metadata?.modelName && (
|
||||
<div className="flex items-center text-xs">
|
||||
<Cpu className="w-3 h-3 mr-1 flex-shrink-0" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import { cn, getCodeHostIcon } from "@/lib/utils";
|
||||
import { FolderIcon, LibraryBigIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { SearchScope } from "../types";
|
||||
|
||||
interface SearchScopeIconProps {
|
||||
searchScope: SearchScope;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const SearchScopeIcon = ({ searchScope, className = "h-4 w-4" }: SearchScopeIconProps) => {
|
||||
if (searchScope.type === 'reposet') {
|
||||
return <LibraryBigIcon className={cn(className, "text-muted-foreground flex-shrink-0")} />;
|
||||
} else {
|
||||
// Render code host icon for repos
|
||||
const codeHostIcon = searchScope.codeHostType ? getCodeHostIcon(searchScope.codeHostType) : null;
|
||||
if (codeHostIcon) {
|
||||
const size = className.includes('h-3') ? 12 : 16;
|
||||
return (
|
||||
<Image
|
||||
src={codeHostIcon.src}
|
||||
alt={`${searchScope.codeHostType} icon`}
|
||||
width={size}
|
||||
height={size}
|
||||
className={cn(className, "flex-shrink-0", codeHostIcon.className)}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <FolderIcon className={cn(className, "text-muted-foreground flex-shrink-0")} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -39,6 +39,28 @@ export const referenceSchema = z.discriminatedUnion('type', [
|
|||
]);
|
||||
export type Reference = z.infer<typeof referenceSchema>;
|
||||
|
||||
export const repoSearchScopeSchema = z.object({
|
||||
type: z.literal('repo'),
|
||||
value: z.string(),
|
||||
name: z.string(),
|
||||
codeHostType: z.string(),
|
||||
});
|
||||
export type RepoSearchScope = z.infer<typeof repoSearchScopeSchema>;
|
||||
|
||||
export const repoSetSearchScopeSchema = z.object({
|
||||
type: z.literal('reposet'),
|
||||
value: z.string(),
|
||||
name: z.string(),
|
||||
repoCount: z.number(),
|
||||
});
|
||||
export type RepoSetSearchScope = z.infer<typeof repoSetSearchScopeSchema>;
|
||||
|
||||
export const searchScopeSchema = z.discriminatedUnion('type', [
|
||||
repoSearchScopeSchema,
|
||||
repoSetSearchScopeSchema,
|
||||
]);
|
||||
export type SearchScope = z.infer<typeof searchScopeSchema>;
|
||||
|
||||
export const sbChatMessageMetadataSchema = z.object({
|
||||
modelName: z.string().optional(),
|
||||
totalInputTokens: z.number().optional(),
|
||||
|
|
@ -50,8 +72,7 @@ export const sbChatMessageMetadataSchema = z.object({
|
|||
timestamp: z.string(), // ISO date string
|
||||
userId: z.string(),
|
||||
})).optional(),
|
||||
selectedRepos: z.array(z.string()).optional(),
|
||||
selectedReposets: z.array(z.string()).optional(),
|
||||
selectedSearchScopes: z.array(searchScopeSchema).optional(),
|
||||
traceId: z.string().optional(),
|
||||
});
|
||||
|
||||
|
|
@ -139,8 +160,7 @@ export const SET_CHAT_STATE_QUERY_PARAM = 'setChatState';
|
|||
|
||||
export type SetChatStatePayload = {
|
||||
inputMessage: CreateUIMessage<SBChatMessage>;
|
||||
selectedRepos: string[];
|
||||
selectedReposets: string[];
|
||||
selectedSearchScopes: SearchScope[];
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -157,7 +177,6 @@ export type LanguageModelInfo = {
|
|||
// Additional request body data that we send along to the chat API.
|
||||
export const additionalChatRequestParamsSchema = z.object({
|
||||
languageModelId: z.string(),
|
||||
selectedRepos: z.array(z.string()),
|
||||
selectedReposets: z.array(z.string()),
|
||||
selectedSearchScopes: z.array(searchScopeSchema),
|
||||
});
|
||||
export type AdditionalChatRequestParams = z.infer<typeof additionalChatRequestParamsSchema>;
|
||||
|
|
@ -10,8 +10,7 @@ import { useRouter } from "next/navigation";
|
|||
import { createChat } from "./actions";
|
||||
import { isServiceError } from "@/lib/utils";
|
||||
import { createPathWithQueryParams } from "@/lib/utils";
|
||||
import { SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from "./types";
|
||||
import { SearchScopeItem } from "./components/chatBox/searchScopeSelector";
|
||||
import { SearchScope, SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from "./types";
|
||||
|
||||
export const useCreateNewChatThread = () => {
|
||||
const domain = useDomain();
|
||||
|
|
@ -19,15 +18,11 @@ export const useCreateNewChatThread = () => {
|
|||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
|
||||
const createNewChatThread = useCallback(async (children: Descendant[], selectedItems: SearchScopeItem[]) => {
|
||||
const createNewChatThread = useCallback(async (children: Descendant[], selectedSearchScopes: SearchScope[]) => {
|
||||
const text = slateContentToString(children);
|
||||
const mentions = getAllMentionElements(children);
|
||||
|
||||
// Extract repos and reposets from selectedItems
|
||||
const selectedRepos = selectedItems.filter(item => item.type === 'repo').map(item => item.value);
|
||||
const selectedReposets = selectedItems.filter(item => item.type === 'reposet').map(item => item.value);
|
||||
|
||||
const inputMessage = createUIMessage(text, mentions.map((mention) => mention.data), selectedRepos, selectedReposets);
|
||||
const inputMessage = createUIMessage(text, mentions.map((mention) => mention.data), selectedSearchScopes);
|
||||
|
||||
setIsLoading(true);
|
||||
const response = await createChat(domain);
|
||||
|
|
@ -42,8 +37,7 @@ export const useCreateNewChatThread = () => {
|
|||
const url = createPathWithQueryParams(`/${domain}/chat/${response.id}`,
|
||||
[SET_CHAT_STATE_QUERY_PARAM, JSON.stringify({
|
||||
inputMessage,
|
||||
selectedRepos,
|
||||
selectedReposets,
|
||||
selectedSearchScopes,
|
||||
} satisfies SetChatStatePayload)],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
SBChatMessage,
|
||||
SBChatMessagePart,
|
||||
SBChatMessageToolTypes,
|
||||
SearchScope,
|
||||
Source,
|
||||
} from "./types"
|
||||
|
||||
|
|
@ -172,7 +173,7 @@ export const addLineNumbers = (source: string, lineOffset = 1) => {
|
|||
return source.split('\n').map((line, index) => `${index + lineOffset}:${line}`).join('\n');
|
||||
}
|
||||
|
||||
export const createUIMessage = (text: string, mentions: MentionData[], selectedRepos: string[], selectedReposets: string[]): CreateUIMessage<SBChatMessage> => {
|
||||
export const createUIMessage = (text: string, mentions: MentionData[], selectedSearchScopes: SearchScope[]): CreateUIMessage<SBChatMessage> => {
|
||||
// Converts applicable mentions into sources.
|
||||
const sources: Source[] = mentions
|
||||
.map((mention) => {
|
||||
|
|
@ -205,8 +206,7 @@ export const createUIMessage = (text: string, mentions: MentionData[], selectedR
|
|||
})) as UIMessagePart<{ source: Source }, SBChatMessageToolTypes>[],
|
||||
],
|
||||
metadata: {
|
||||
selectedRepos,
|
||||
selectedReposets,
|
||||
selectedSearchScopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue