Update search settings + chat/search handling (#2333)

* validate web list

* update search settings + chat/search handling

* remove accidentally added search manager

* minor build fix

* push from local
This commit is contained in:
pablodanswer 2024-09-05 17:07:39 -07:00 committed by GitHub
parent 2d7b312e6c
commit 2bd3833c55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 132 additions and 118 deletions

View File

@ -280,6 +280,13 @@ def delete_chat_session(
db_session: Session,
hard_delete: bool = HARD_DELETE_CHATS,
) -> None:
chat_session = get_chat_session_by_id(
chat_session_id=chat_session_id, user_id=user_id, db_session=db_session
)
if chat_session.deleted:
raise ValueError("Cannot delete an already deleted chat session")
if hard_delete:
delete_messages_and_files_from_chat_session(chat_session_id, db_session)
db_session.execute(delete(ChatSession).where(ChatSession.id == chat_session_id))

View File

@ -269,7 +269,10 @@ def delete_chat_session_by_id(
db_session: Session = Depends(get_session),
) -> None:
user_id = user.id if user is not None else None
delete_chat_session(user_id, session_id, db_session)
try:
delete_chat_session(user_id, session_id, db_session)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
async def is_disconnected(request: Request) -> Callable[[], bool]:

View File

@ -84,7 +84,7 @@ class CloudEmbedding:
self.client = _initialize_client(api_key, self.provider, model)
def _embed_openai(self, texts: list[str], model: str | None) -> list[Embedding]:
if model is None:
if not model:
model = DEFAULT_OPENAI_MODEL
# OpenAI does not seem to provide truncation option, however
@ -111,7 +111,7 @@ class CloudEmbedding:
def _embed_cohere(
self, texts: list[str], model: str | None, embedding_type: str
) -> list[Embedding]:
if model is None:
if not model:
model = DEFAULT_COHERE_MODEL
final_embeddings: list[Embedding] = []
@ -130,7 +130,7 @@ class CloudEmbedding:
def _embed_voyage(
self, texts: list[str], model: str | None, embedding_type: str
) -> list[Embedding]:
if model is None:
if not model:
model = DEFAULT_VOYAGE_MODEL
# Similar to Cohere, the API server will do approximate size chunking
@ -146,7 +146,7 @@ class CloudEmbedding:
def _embed_vertex(
self, texts: list[str], model: str | None, embedding_type: str
) -> list[Embedding]:
if model is None:
if not model:
model = DEFAULT_VERTEX_MODEL
embeddings = self.client.get_embeddings(
@ -172,7 +172,6 @@ class CloudEmbedding:
try:
if self.provider == EmbeddingProvider.OPENAI:
return self._embed_openai(texts, model_name)
embedding_type = EmbeddingModelTextType.get_type(self.provider, text_type)
if self.provider == EmbeddingProvider.COHERE:
return self._embed_cohere(texts, model_name, embedding_type)

View File

@ -95,7 +95,7 @@ export default function AddConnector({
...configuration.values.reduce(
(acc, field) => {
if (field.type === "select") {
acc[field.name] = field.default || "";
acc[field.name] = field.options ? field.options[field.default!]! : "";
} else if (field.type === "list") {
acc[field.name] = field.default || [];
} else if (field.type === "checkbox") {
@ -339,11 +339,13 @@ export default function AddConnector({
...configuration.values.reduce(
(acc, field) => {
let schema: any =
field.type === "list"
? Yup.array().of(Yup.string())
: field.type === "checkbox"
? Yup.boolean()
: Yup.string();
field.type === "select"
? Yup.string()
: field.type === "list"
? Yup.array().of(Yup.string())
: field.type === "checkbox"
? Yup.boolean()
: Yup.string();
if (!field.optional) {
schema = schema.required(`${field.label} is required`);

View File

@ -24,7 +24,10 @@ export function ChangeCredentialsModal({
useFileUpload: boolean;
isProxy?: boolean;
}) {
const [apiKeyOrUrl, setApiKeyOrUrl] = useState("");
const [apiKey, setApiKey] = useState("");
const [apiUrl, setApiUrl] = useState("");
const [modelName, setModelName] = useState("");
const [testError, setTestError] = useState<string>("");
const [fileName, setFileName] = useState<string>("");
const fileInputRef = useRef<HTMLInputElement>(null);
@ -52,7 +55,7 @@ export function ChangeCredentialsModal({
let jsonContent;
try {
jsonContent = JSON.parse(fileContent);
setApiKeyOrUrl(JSON.stringify(jsonContent));
setApiKey(JSON.stringify(jsonContent));
} catch (parseError) {
throw new Error(
"Failed to parse JSON file. Please ensure it's a valid JSON."
@ -64,7 +67,7 @@ export function ChangeCredentialsModal({
? error.message
: "An unknown error occurred while processing the file."
);
setApiKeyOrUrl("");
setApiKey("");
clearFileInput();
}
}
@ -101,16 +104,18 @@ export function ChangeCredentialsModal({
const handleSubmit = async () => {
setTestError("");
const normalizedProviderType = provider.provider_type
.toLowerCase()
.split(" ")[0];
try {
const testResponse = await fetch("/api/admin/embedding/test-embedding", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider_type: provider.provider_type.toLowerCase().split(" ")[0],
[isProxy ? "api_url" : "api_key"]: apiKeyOrUrl,
[isProxy ? "api_key" : "api_url"]: isProxy
? provider.api_key
: provider.api_url,
provider_type: normalizedProviderType,
api_key: apiKey,
api_url: apiUrl,
model_name: modelName,
}),
});
@ -123,8 +128,9 @@ export function ChangeCredentialsModal({
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider_type: provider.provider_type.toLowerCase().split(" ")[0],
[isProxy ? "api_url" : "api_key"]: apiKeyOrUrl,
provider_type: normalizedProviderType,
api_key: apiKey,
api_url: apiUrl,
is_default_provider: false,
is_configured: true,
}),
@ -150,25 +156,31 @@ export function ChangeCredentialsModal({
<Modal
width="max-w-3xl"
icon={provider.icon}
title={`Modify your ${provider.provider_type} ${isProxy ? "URL" : "key"}`}
title={`Modify your ${provider.provider_type} ${isProxy ? "Configuration" : "key"}`}
onOutsideClick={onCancel}
>
<>
{isProxy && (
<div className="mb-4">
<Subtitle className="font-bold text-lg">
Want to swap out your URL?
</Subtitle>
<a
href={provider.apiLink}
target="_blank"
rel="noopener noreferrer"
className="underline cursor-pointer mt-2 mb-4"
>
Visit API
</a>
<p className="mb-4">
You can modify your configuration by providing a new API key
{isProxy ? " or API URL." : "."}
</p>
<div className="flex flex-col mt-4 gap-y-2">
<div className="mb-4 flex flex-col gap-y-2">
<Label className="mt-2">API Key</Label>
{useFileUpload ? (
<>
<Label className="mt-2">Upload JSON File</Label>
<input
ref={fileInputRef}
type="file"
accept=".json"
onChange={handleFileUpload}
className="text-lg w-full p-1"
/>
{fileName && <p>Uploaded file: {fileName}</p>}
</>
) : (
<>
<input
className={`
border
@ -179,67 +191,19 @@ export function ChangeCredentialsModal({
px-3
bg-background-emphasis
`}
value={apiKeyOrUrl}
onChange={(e: any) => setApiKeyOrUrl(e.target.value)}
placeholder="Paste your API URL here"
value={apiKey}
onChange={(e: any) => setApiKey(e.target.value)}
placeholder="Paste your API key here"
/>
</div>
</>
)}
{testError && (
<Callout title="Error" color="red" className="mt-4">
{testError}
</Callout>
)}
{isProxy && (
<>
<Label className="mt-2">API URL</Label>
<div className="flex mt-4 justify-between">
<Button
color="blue"
onClick={() => handleSubmit()}
disabled={!apiKeyOrUrl}
>
Swap URL
</Button>
</div>
{deletionError && (
<Callout title="Error" color="red" className="mt-4">
{deletionError}
</Callout>
)}
<Divider />
</div>
)}
<div className="mb-4">
<Subtitle className="font-bold text-lg">
Want to swap out your key?
</Subtitle>
<a
href={provider.apiLink}
target="_blank"
rel="noopener noreferrer"
className="underline cursor-pointer mt-2 mb-4"
>
Visit API
</a>
<div className="flex flex-col mt-4 gap-y-2">
{useFileUpload ? (
<>
<Label>Upload JSON File</Label>
<input
ref={fileInputRef}
type="file"
accept=".json"
onChange={handleFileUpload}
className="text-lg w-full p-1"
/>
{fileName && <p>Uploaded file: {fileName}</p>}
</>
) : (
<>
<input
className={`
<input
className={`
border
border-border
rounded
@ -248,29 +212,62 @@ export function ChangeCredentialsModal({
px-3
bg-background-emphasis
`}
value={apiKeyOrUrl}
onChange={(e: any) => setApiKeyOrUrl(e.target.value)}
placeholder="Paste your API key here"
/>
</>
)}
</div>
value={apiUrl}
onChange={(e: any) => setApiUrl(e.target.value)}
placeholder="Paste your API URL here"
/>
{deletionError && (
<Callout title="Error" color="red" className="mt-4">
{deletionError}
</Callout>
)}
<div>
<Label className="mt-2">Test Model</Label>
<p>
Since you are using a liteLLM proxy, we&apos;ll need a model
name to test the connection with.
</p>
</div>
<input
className={`
border
border-border
rounded
w-full
py-2
px-3
bg-background-emphasis
`}
value={modelName}
onChange={(e: any) => setModelName(e.target.value)}
placeholder="Paste your API URL here"
/>
{deletionError && (
<Callout title="Error" color="red" className="mt-4">
{deletionError}
</Callout>
)}
</>
)}
{testError && (
<Callout title="Error" color="red" className="mt-4">
<Callout title="Error" color="red" className="my-4">
{testError}
</Callout>
)}
<div className="flex mt-4 justify-between">
<Button
color="blue"
onClick={() => handleSubmit()}
disabled={!apiKeyOrUrl}
>
Swap Key
</Button>
</div>
<Button
className="mr-auto mt-4"
color="blue"
onClick={() => handleSubmit()}
disabled={!apiKey}
>
Update Configuration
</Button>
<Divider />
<Subtitle className="mt-4 font-bold text-lg mb-2">
@ -281,7 +278,7 @@ export function ChangeCredentialsModal({
embedding type!
</Text>
<Button onClick={handleDelete} color="red">
<Button className="mr-auto" onClick={handleDelete} color="red">
Delete Configuration
</Button>
{deletionError && (

View File

@ -232,7 +232,7 @@ function ConnectorRow({
}}
>
<TableCell className={`!pr-0 w-[${columnWidths.first}]`}>
<p className="w-[200px] inline-block ellipsis truncate">
<p className="w-[100px] xl:w-[200px] inline-block ellipsis truncate">
{ccPairsIndexingStatus.name}
</p>
</TableCell>

View File

@ -1621,10 +1621,14 @@ export function ChatPage({
if (response.ok) {
setDeletingChatSession(null);
// go back to the main page
router.push("/chat");
if (deletingChatSession.id === chatSessionIdRef.current) {
router.push("/chat");
}
} else {
alert("Failed to delete chat session");
const responseJson = await response.json();
setPopup({ message: responseJson.detail, type: "error" });
}
router.refresh();
}}
/>
)}

View File

@ -607,8 +607,10 @@ export const SearchSection = ({
// go back to the main page
router.push("/search");
} else {
alert("Failed to delete chat session");
const responseJson = await response.json();
setPopup({ message: responseJson.detail, type: "error" });
}
router.refresh();
}}
/>
)}

View File

@ -96,7 +96,7 @@ export const connectorConfigs: Record<
query: "Select the web connector type:",
label: "Scrape Method",
name: "web_connector_type",
optional: true,
default: 0,
options: [
{ name: "recursive", value: "recursive" },
{ name: "single", value: "single" },