Added display of more extended error messages
Browse files
chat-ui/src/lib/components/OpenWebSearchResults.svelte
CHANGED
|
@@ -13,7 +13,9 @@
|
|
| 13 |
|
| 14 |
let detailsOpen: boolean;
|
| 15 |
let error: boolean;
|
|
|
|
| 16 |
$: error = webSearchMessages[webSearchMessages.length - 1]?.messageType === "error";
|
|
|
|
| 17 |
</script>
|
| 18 |
|
| 19 |
<details
|
|
@@ -30,8 +32,12 @@
|
|
| 30 |
{:else}
|
| 31 |
<CarbonCheckmark class="my-auto text-gray-500" />
|
| 32 |
{/if}
|
| 33 |
-
<span class="px-2 font-medium" class:text-red-700={error} class:dark:text-red-500={error}
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
</span>
|
| 36 |
<div class="my-auto transition-all" class:rotate-90={detailsOpen}>
|
| 37 |
<CarbonCaretRight />
|
|
|
|
| 13 |
|
| 14 |
let detailsOpen: boolean;
|
| 15 |
let error: boolean;
|
| 16 |
+
let errorMessage: string;
|
| 17 |
$: error = webSearchMessages[webSearchMessages.length - 1]?.messageType === "error";
|
| 18 |
+
$: errorMessage = webSearchMessages[webSearchMessages.length - 1]?.messageType === "error" ? webSearchMessages[webSearchMessages.length - 1].message : "";
|
| 19 |
</script>
|
| 20 |
|
| 21 |
<details
|
|
|
|
| 32 |
{:else}
|
| 33 |
<CarbonCheckmark class="my-auto text-gray-500" />
|
| 34 |
{/if}
|
| 35 |
+
<span class="px-2 font-medium" class:text-red-700={error} class:dark:text-red-500={error}>
|
| 36 |
+
{#if error}
|
| 37 |
+
{ errorMessage ? errorMessage : "Произошла ошибка при поиске. Кликните сюда для просмотра лога." }
|
| 38 |
+
{:else}
|
| 39 |
+
Поиск в интернете
|
| 40 |
+
{/if}
|
| 41 |
</span>
|
| 42 |
<div class="my-auto transition-all" class:rotate-90={detailsOpen}>
|
| 43 |
<CarbonCaretRight />
|
chat-ui/src/lib/server/websearch/runWebSearch.ts
CHANGED
|
@@ -65,88 +65,101 @@ export async function runWebSearch(
|
|
| 65 |
|
| 66 |
webSearch.results = webSearch.results
|
| 67 |
.filter(({ link }) => !link.includes("youtube.com") && !link.includes(".pdf")); // filter out youtube links
|
| 68 |
-
|
| 69 |
|
| 70 |
// let paragraphChunks: { source: WebSearchSource; text: string }[] = [];
|
| 71 |
|
| 72 |
-
let texts
|
| 73 |
if (webSearch.results.length > 0) {
|
| 74 |
appendUpdate("Обработка результатов");
|
| 75 |
//let fullText = '';
|
| 76 |
-
for(const i in webSearch.results) {
|
| 77 |
-
if(texts.length > 30) break;
|
| 78 |
const { link, hostname, title } = webSearch.results[i];
|
| 79 |
let text = "";
|
| 80 |
try {
|
| 81 |
-
if(USE_CUSTOM_PARSER){
|
| 82 |
-
try{
|
| 83 |
text = await parseNalogGovRu(link);
|
| 84 |
-
}catch(e){
|
| 85 |
console.log('Custom parser failed. link: ', link)
|
| 86 |
console.log('Custom parser failed. error: ', e)
|
| 87 |
console.log('Starting fallback parser')
|
| 88 |
text = await parseWeb(link);
|
| 89 |
console.log('Fallback returned: ', text)
|
| 90 |
}
|
| 91 |
-
|
| 92 |
-
}else{
|
| 93 |
text = await parseWeb(link);
|
| 94 |
}
|
| 95 |
-
|
| 96 |
appendUpdate("Обработка страницы", [link]);
|
| 97 |
} catch (e) {
|
| 98 |
console.error(`Error parsing webpage "${link}"`, e);
|
| 99 |
}
|
| 100 |
-
|
| 101 |
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
| 105 |
}
|
| 106 |
-
|
| 107 |
-
|
| 108 |
// fullText += text;
|
| 109 |
// if(fullText.length >= Number(SAIGA_TRUNCATE_WEB_CONTEXT)){
|
| 110 |
// break;
|
| 111 |
// }
|
| 112 |
-
|
| 113 |
// texts.push(...chunk(text, CHUNK_CAR_LEN).slice(0, MAX_N_CHUNKS));
|
| 114 |
}
|
| 115 |
} else {
|
| 116 |
appendUpdate("Поиск не вернул релевантных результатов");
|
| 117 |
appendUpdate("Генерируем ответ на основе данных нейросети");
|
| 118 |
-
|
| 119 |
-
|
|
|
|
| 120 |
|
| 121 |
-
if(texts && texts.length > 0){
|
| 122 |
appendUpdate("Получение релевантной информации");
|
| 123 |
console.log('webSearch.contextSources', webSearch.contextSources)
|
| 124 |
-
|
| 125 |
-
const allIndices = await findSimilarSentences(prompt, texts.map((t) => t.text));//, { topK: topKClosestParagraphs});
|
| 126 |
-
|
| 127 |
-
console.log('similarity check result:', allIndices);
|
| 128 |
-
const indices = allIndices.filter((r) => r.score >= Number(SIMILARITY_THRESHOLD))
|
| 129 |
-
.sort((a, b) => b.score - a.score)
|
| 130 |
-
.slice(0, Number(MAX_N_PAGES_SCRAPE));
|
| 131 |
-
|
| 132 |
-
let fullText = '';
|
| 133 |
-
for(const i in indices){
|
| 134 |
-
const text = texts[indices[i].i];
|
| 135 |
-
if(fullText.length >= Number(SAIGA_TRUNCATE_WEB_CONTEXT)){
|
| 136 |
-
break;
|
| 137 |
-
}
|
| 138 |
|
| 139 |
-
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
}
|
| 142 |
-
webSearch.context = fullText.slice(0, Number(SAIGA_TRUNCATE_WEB_CONTEXT));
|
| 143 |
-
console.log('web search context:', webSearch.context);
|
| 144 |
-
updatePad({
|
| 145 |
-
type: "webSearch",
|
| 146 |
-
messageType: "sources",
|
| 147 |
-
message: "sources",
|
| 148 |
-
sources: webSearch.contextSources,
|
| 149 |
-
});
|
| 150 |
|
| 151 |
// webSearch.contextSources = [
|
| 152 |
// {
|
|
@@ -165,13 +178,13 @@ export async function runWebSearch(
|
|
| 165 |
// title: 'Как получить ИНН через электронные сервисы ФНС'
|
| 166 |
// }
|
| 167 |
// ];
|
| 168 |
-
|
| 169 |
|
| 170 |
// webSearch.context = texts.map((t) => t.text)
|
| 171 |
// .join("\n")
|
| 172 |
// .slice(0, Number(SAIGA_TRUNCATE_WEB_CONTEXT));
|
| 173 |
-
|
| 174 |
-
|
| 175 |
|
| 176 |
// updatePad({
|
| 177 |
// type: "webSearch",
|
|
@@ -179,7 +192,7 @@ export async function runWebSearch(
|
|
| 179 |
// message: "sources",
|
| 180 |
// sources: [],
|
| 181 |
// });
|
| 182 |
-
|
| 183 |
// const usedSources = new Set<string>();
|
| 184 |
// for (const idx of indices) {
|
| 185 |
// const { source } = texts[idx.i];
|
|
@@ -199,18 +212,20 @@ export async function runWebSearch(
|
|
| 199 |
console.log('searchError', searchError)
|
| 200 |
if (searchError instanceof Error) {
|
| 201 |
appendUpdate(
|
| 202 |
-
|
| 203 |
-
[
|
| 204 |
"error"
|
| 205 |
);
|
| 206 |
}
|
|
|
|
|
|
|
| 207 |
}
|
| 208 |
|
| 209 |
return webSearch;
|
| 210 |
}
|
| 211 |
|
| 212 |
|
| 213 |
-
async function findSimilarSentences(query: string, sentences: string[]): Promise<{i: number, score: number}[]> {
|
| 214 |
const requestOptions = {
|
| 215 |
method: 'POST',
|
| 216 |
headers: {
|
|
@@ -246,5 +261,5 @@ async function findSimilarSentences(query: string, sentences: string[]): Promise
|
|
| 246 |
}
|
| 247 |
}
|
| 248 |
|
| 249 |
-
throw new Error(`
|
| 250 |
}
|
|
|
|
| 65 |
|
| 66 |
webSearch.results = webSearch.results
|
| 67 |
.filter(({ link }) => !link.includes("youtube.com") && !link.includes(".pdf")); // filter out youtube links
|
| 68 |
+
//slice(0, Number(MAX_N_PAGES_SCRAPE)); // limit to first 10 links only
|
| 69 |
|
| 70 |
// let paragraphChunks: { source: WebSearchSource; text: string }[] = [];
|
| 71 |
|
| 72 |
+
let texts: { source: any, text: string }[] = [];
|
| 73 |
if (webSearch.results.length > 0) {
|
| 74 |
appendUpdate("Обработка результатов");
|
| 75 |
//let fullText = '';
|
| 76 |
+
for (const i in webSearch.results) {
|
| 77 |
+
if (texts.length > 30) break;
|
| 78 |
const { link, hostname, title } = webSearch.results[i];
|
| 79 |
let text = "";
|
| 80 |
try {
|
| 81 |
+
if (USE_CUSTOM_PARSER) {
|
| 82 |
+
try {
|
| 83 |
text = await parseNalogGovRu(link);
|
| 84 |
+
} catch (e) {
|
| 85 |
console.log('Custom parser failed. link: ', link)
|
| 86 |
console.log('Custom parser failed. error: ', e)
|
| 87 |
console.log('Starting fallback parser')
|
| 88 |
text = await parseWeb(link);
|
| 89 |
console.log('Fallback returned: ', text)
|
| 90 |
}
|
| 91 |
+
|
| 92 |
+
} else {
|
| 93 |
text = await parseWeb(link);
|
| 94 |
}
|
| 95 |
+
|
| 96 |
appendUpdate("Обработка страницы", [link]);
|
| 97 |
} catch (e) {
|
| 98 |
console.error(`Error parsing webpage "${link}"`, e);
|
| 99 |
}
|
|
|
|
| 100 |
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
if (text.length > 0) {
|
| 104 |
+
texts.push({ source: { link: link, hostname: hostname, title: title }, text })
|
| 105 |
}
|
| 106 |
+
|
| 107 |
+
|
| 108 |
// fullText += text;
|
| 109 |
// if(fullText.length >= Number(SAIGA_TRUNCATE_WEB_CONTEXT)){
|
| 110 |
// break;
|
| 111 |
// }
|
| 112 |
+
|
| 113 |
// texts.push(...chunk(text, CHUNK_CAR_LEN).slice(0, MAX_N_CHUNKS));
|
| 114 |
}
|
| 115 |
} else {
|
| 116 |
appendUpdate("Поиск не вернул релевантных результатов");
|
| 117 |
appendUpdate("Генерируем ответ на основе данных нейросети");
|
| 118 |
+
appendUpdate("По вашему запросу ничего не найдено.", [], "error");
|
| 119 |
+
throw new Error("По вашему запросу ничего не найдено.");
|
| 120 |
+
}
|
| 121 |
|
| 122 |
+
if (texts && texts.length > 0) {
|
| 123 |
appendUpdate("Получение релевантной информации");
|
| 124 |
console.log('webSearch.contextSources', webSearch.contextSources)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
+
try {
|
| 127 |
+
const allIndices = await findSimilarSentences(prompt, texts.map((t) => t.text));//, { topK: topKClosestParagraphs});
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
console.log('similarity check result:', allIndices);
|
| 131 |
+
const indices = allIndices.filter((r) => r.score >= Number(SIMILARITY_THRESHOLD))
|
| 132 |
+
.sort((a, b) => b.score - a.score)
|
| 133 |
+
.slice(0, Number(MAX_N_PAGES_SCRAPE));
|
| 134 |
+
|
| 135 |
+
let fullText = '';
|
| 136 |
+
for (const i in indices) {
|
| 137 |
+
const text = texts[indices[i].i];
|
| 138 |
+
if (fullText.length >= Number(SAIGA_TRUNCATE_WEB_CONTEXT)) {
|
| 139 |
+
break;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
fullText += text.text + "\n";
|
| 143 |
+
webSearch.contextSources.push(text.source);
|
| 144 |
+
}
|
| 145 |
+
webSearch.context = fullText.slice(0, Number(SAIGA_TRUNCATE_WEB_CONTEXT));
|
| 146 |
+
console.log('web search context:', webSearch.context);
|
| 147 |
+
updatePad({
|
| 148 |
+
type: "webSearch",
|
| 149 |
+
messageType: "sources",
|
| 150 |
+
message: "sources",
|
| 151 |
+
sources: webSearch.contextSources,
|
| 152 |
+
});
|
| 153 |
+
} catch (e) {
|
| 154 |
+
if (e instanceof Error) {
|
| 155 |
+
appendUpdate(
|
| 156 |
+
e.message,
|
| 157 |
+
[],
|
| 158 |
+
"error"
|
| 159 |
+
);
|
| 160 |
+
}
|
| 161 |
+
throw e;
|
| 162 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
// webSearch.contextSources = [
|
| 165 |
// {
|
|
|
|
| 178 |
// title: 'Как получить ИНН через электронные сервисы ФНС'
|
| 179 |
// }
|
| 180 |
// ];
|
| 181 |
+
|
| 182 |
|
| 183 |
// webSearch.context = texts.map((t) => t.text)
|
| 184 |
// .join("\n")
|
| 185 |
// .slice(0, Number(SAIGA_TRUNCATE_WEB_CONTEXT));
|
| 186 |
+
|
| 187 |
+
|
| 188 |
|
| 189 |
// updatePad({
|
| 190 |
// type: "webSearch",
|
|
|
|
| 192 |
// message: "sources",
|
| 193 |
// sources: [],
|
| 194 |
// });
|
| 195 |
+
|
| 196 |
// const usedSources = new Set<string>();
|
| 197 |
// for (const idx of indices) {
|
| 198 |
// const { source } = texts[idx.i];
|
|
|
|
| 212 |
console.log('searchError', searchError)
|
| 213 |
if (searchError instanceof Error) {
|
| 214 |
appendUpdate(
|
| 215 |
+
searchError.message,
|
| 216 |
+
[],
|
| 217 |
"error"
|
| 218 |
);
|
| 219 |
}
|
| 220 |
+
|
| 221 |
+
throw searchError;
|
| 222 |
}
|
| 223 |
|
| 224 |
return webSearch;
|
| 225 |
}
|
| 226 |
|
| 227 |
|
| 228 |
+
async function findSimilarSentences(query: string, sentences: string[]): Promise<{ i: number, score: number }[]> {
|
| 229 |
const requestOptions = {
|
| 230 |
method: 'POST',
|
| 231 |
headers: {
|
|
|
|
| 261 |
}
|
| 262 |
}
|
| 263 |
|
| 264 |
+
throw new Error(`Сервер генератора контекста недоступен.`);
|
| 265 |
}
|
chat-ui/src/routes/conversation/[id]/+server.ts
CHANGED
|
@@ -159,11 +159,6 @@ export async function POST({ request, fetch, locals, params, getClientAddress })
|
|
| 159 |
const stream = new ReadableStream({
|
| 160 |
async start(controller) {
|
| 161 |
console.log('conversation start')
|
| 162 |
-
console.log({
|
| 163 |
-
'api_dev_key': PASTEBIN_DEV_KEY,
|
| 164 |
-
'api_user_name': PASTEBIN_USERNAME,
|
| 165 |
-
'api_user_password': PASTEBIN_PASSWORD,
|
| 166 |
-
})
|
| 167 |
// fetch the endpoint
|
| 168 |
const randomEndpoint = modelEndpoint(model);
|
| 169 |
|
|
@@ -189,8 +184,19 @@ export async function POST({ request, fetch, locals, params, getClientAddress })
|
|
| 189 |
let webSearchResults: WebSearch | undefined;
|
| 190 |
|
| 191 |
if (webSearch) {
|
| 192 |
-
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
}
|
| 195 |
|
| 196 |
|
|
@@ -348,6 +354,7 @@ export async function POST({ request, fetch, locals, params, getClientAddress })
|
|
| 348 |
|
| 349 |
} catch (error) {
|
| 350 |
console.log('Error fetching data from Flask API:', error);
|
|
|
|
| 351 |
update({
|
| 352 |
type: "finalAnswer",
|
| 353 |
text: '',
|
|
|
|
| 159 |
const stream = new ReadableStream({
|
| 160 |
async start(controller) {
|
| 161 |
console.log('conversation start')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
// fetch the endpoint
|
| 163 |
const randomEndpoint = modelEndpoint(model);
|
| 164 |
|
|
|
|
| 184 |
let webSearchResults: WebSearch | undefined;
|
| 185 |
|
| 186 |
if (webSearch) {
|
| 187 |
+
try{
|
| 188 |
+
webSearchResults = await runWebSearch(conv, newPrompt, update);
|
| 189 |
+
logEntry.websearch = webSearchResults;
|
| 190 |
+
} catch (error) {
|
| 191 |
+
console.log('Error in search subsystem:', error);
|
| 192 |
+
logEntry.error = error;
|
| 193 |
+
update({
|
| 194 |
+
type: "finalAnswer",
|
| 195 |
+
text: '',
|
| 196 |
+
});
|
| 197 |
+
} finally {
|
| 198 |
+
Log(logEntry);
|
| 199 |
+
}
|
| 200 |
}
|
| 201 |
|
| 202 |
|
|
|
|
| 354 |
|
| 355 |
} catch (error) {
|
| 356 |
console.log('Error fetching data from Flask API:', error);
|
| 357 |
+
logEntry.error = error;
|
| 358 |
update({
|
| 359 |
type: "finalAnswer",
|
| 360 |
text: '',
|