mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2026-01-11 05:45:45 +00:00
Compare commits
8 Commits
5174820554
...
6016090f12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6016090f12 | ||
|
|
8aed9518a2 | ||
|
|
2df6250ba1 | ||
|
|
85f6c3b901 | ||
|
|
96001a9e26 | ||
|
|
331387efa4 | ||
|
|
d0e71e6482 | ||
|
|
e329820bc8 |
@@ -30,12 +30,12 @@
|
||||
"better-sqlite3": "^11.9.1",
|
||||
"clsx": "^2.1.0",
|
||||
"drizzle-orm": "^0.40.1",
|
||||
"framer-motion": "^12.23.24",
|
||||
"framer-motion": "^12.23.25",
|
||||
"html-to-text": "^9.0.5",
|
||||
"jspdf": "^3.0.1",
|
||||
"langchain": "^1.0.4",
|
||||
"lightweight-charts": "^5.0.9",
|
||||
"lucide-react": "^0.363.0",
|
||||
"lucide-react": "^0.556.0",
|
||||
"mammoth": "^1.9.1",
|
||||
"markdown-to-jsx": "^7.7.2",
|
||||
"mathjs": "^15.1.0",
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import { Brain, Search, FileText, ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import {
|
||||
Brain,
|
||||
Search,
|
||||
FileText,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
BookSearch,
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ResearchBlock, ResearchBlockSubStep } from '@/lib/types';
|
||||
@@ -11,9 +18,12 @@ const getStepIcon = (step: ResearchBlockSubStep) => {
|
||||
return <Brain className="w-4 h-4" />;
|
||||
} else if (step.type === 'searching') {
|
||||
return <Search className="w-4 h-4" />;
|
||||
} else if (step.type === 'reading') {
|
||||
} else if (step.type === 'search_results') {
|
||||
return <FileText className="w-4 h-4" />;
|
||||
} else if (step.type === 'reading') {
|
||||
return <BookSearch className="w-4 h-4" />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -25,9 +35,12 @@ const getStepTitle = (
|
||||
return isStreaming && !step.reasoning ? 'Thinking...' : 'Thinking';
|
||||
} else if (step.type === 'searching') {
|
||||
return `Searching ${step.searching.length} ${step.searching.length === 1 ? 'query' : 'queries'}`;
|
||||
} else if (step.type === 'reading') {
|
||||
} else if (step.type === 'search_results') {
|
||||
return `Found ${step.reading.length} ${step.reading.length === 1 ? 'result' : 'results'}`;
|
||||
} else if (step.type === 'reading') {
|
||||
return `Reading ${step.reading.length} ${step.reading.length === 1 ? 'source' : 'sources'}`;
|
||||
}
|
||||
|
||||
return 'Processing';
|
||||
};
|
||||
|
||||
@@ -91,10 +104,9 @@ const AssistantSteps = ({
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.2, delay: 0 }}
|
||||
className="flex gap-3"
|
||||
className="flex gap-2"
|
||||
>
|
||||
{/* Timeline connector */}
|
||||
<div className="flex flex-col items-center pt-0.5">
|
||||
<div className="flex flex-col items-center -mt-0.5">
|
||||
<div
|
||||
className={`rounded-full p-1.5 bg-light-100 dark:bg-dark-100 text-black/70 dark:text-white/70 ${isStreaming ? 'animate-pulse' : ''}`}
|
||||
>
|
||||
@@ -105,7 +117,6 @@ const AssistantSteps = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Step content */}
|
||||
<div className="flex-1 pb-1">
|
||||
<span className="text-sm font-medium text-black dark:text-white">
|
||||
{getStepTitle(step, isStreaming)}
|
||||
@@ -151,37 +162,39 @@ const AssistantSteps = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step.type === 'reading' && step.reading.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5 mt-1.5">
|
||||
{step.reading.slice(0, 4).map((result, idx) => {
|
||||
const url = result.metadata.url || '';
|
||||
const title = result.metadata.title || 'Untitled';
|
||||
const domain = url ? new URL(url).hostname : '';
|
||||
const faviconUrl = domain
|
||||
? `https://s2.googleusercontent.com/s2/favicons?domain=${domain}&sz=128`
|
||||
: '';
|
||||
{(step.type === 'search_results' ||
|
||||
step.type === 'reading') &&
|
||||
step.reading.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5 mt-1.5">
|
||||
{step.reading.slice(0, 4).map((result, idx) => {
|
||||
const url = result.metadata.url || '';
|
||||
const title = result.metadata.title || 'Untitled';
|
||||
const domain = url ? new URL(url).hostname : '';
|
||||
const faviconUrl = domain
|
||||
? `https://s2.googleusercontent.com/s2/favicons?domain=${domain}&sz=128`
|
||||
: '';
|
||||
|
||||
return (
|
||||
<span
|
||||
key={idx}
|
||||
className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-xs font-medium bg-light-100 dark:bg-dark-100 text-black/70 dark:text-white/70 border border-light-200 dark:border-dark-200"
|
||||
>
|
||||
{faviconUrl && (
|
||||
<img
|
||||
src={faviconUrl}
|
||||
alt=""
|
||||
className="w-3 h-3 rounded-sm flex-shrink-0"
|
||||
onError={(e) => {
|
||||
e.currentTarget.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="line-clamp-1">{title}</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
return (
|
||||
<span
|
||||
key={idx}
|
||||
className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-xs font-medium bg-light-100 dark:bg-dark-100 text-black/70 dark:text-white/70 border border-light-200 dark:border-dark-200"
|
||||
>
|
||||
{faviconUrl && (
|
||||
<img
|
||||
src={faviconUrl}
|
||||
alt=""
|
||||
className="w-3 h-3 rounded-sm flex-shrink-0"
|
||||
onError={(e) => {
|
||||
e.currentTarget.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="line-clamp-1">{title}</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { getMeasurementUnit } from '@/lib/config/clientRegistry';
|
||||
import { Wind, Droplets, Gauge } from 'lucide-react';
|
||||
import { useMemo, useEffect, useState } from 'react';
|
||||
|
||||
@@ -226,6 +227,20 @@ const Weather = ({
|
||||
timezone,
|
||||
}: WeatherWidgetProps) => {
|
||||
const [isDarkMode, setIsDarkMode] = useState(false);
|
||||
const unit = getMeasurementUnit();
|
||||
const isImperial = unit === 'imperial';
|
||||
const tempUnitLabel = isImperial ? '°F' : '°C';
|
||||
const windUnitLabel = isImperial ? 'mph' : 'km/h';
|
||||
|
||||
const formatTemp = (celsius: number) => {
|
||||
if (!Number.isFinite(celsius)) return 0;
|
||||
return Math.round(isImperial ? (celsius * 9) / 5 + 32 : celsius);
|
||||
};
|
||||
|
||||
const formatWind = (speedKmh: number) => {
|
||||
if (!Number.isFinite(speedKmh)) return 0;
|
||||
return Math.round(isImperial ? speedKmh * 0.621371 : speedKmh);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const checkDarkMode = () => {
|
||||
@@ -266,14 +281,12 @@ const Weather = ({
|
||||
return {
|
||||
day: dayName,
|
||||
icon: info.icon,
|
||||
high: Math.round(daily.temperature_2m_max[idx + 1]),
|
||||
low: Math.round(daily.temperature_2m_min[idx + 1]),
|
||||
highF: Math.round((daily.temperature_2m_max[idx + 1] * 9) / 5 + 32),
|
||||
lowF: Math.round((daily.temperature_2m_min[idx + 1] * 9) / 5 + 32),
|
||||
high: formatTemp(daily.temperature_2m_max[idx + 1]),
|
||||
low: formatTemp(daily.temperature_2m_min[idx + 1]),
|
||||
precipitation: daily.precipitation_probability_max[idx + 1] || 0,
|
||||
};
|
||||
});
|
||||
}, [daily, isDarkMode]);
|
||||
}, [daily, isDarkMode, isImperial]);
|
||||
|
||||
if (!current || !daily || !daily.time || daily.time.length === 0) {
|
||||
return (
|
||||
@@ -305,9 +318,9 @@ const Weather = ({
|
||||
<div>
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-4xl font-bold drop-shadow-md">
|
||||
{current.temperature_2m}°
|
||||
{formatTemp(current.temperature_2m)}°
|
||||
</span>
|
||||
<span className="text-lg">F C</span>
|
||||
<span className="text-lg">{tempUnitLabel}</span>
|
||||
</div>
|
||||
<p className="text-sm font-medium drop-shadow mt-0.5">
|
||||
{weatherInfo.description}
|
||||
@@ -316,7 +329,8 @@ const Weather = ({
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-xs font-medium opacity-90">
|
||||
{daily.temperature_2m_max[0]}° {daily.temperature_2m_min[0]}°
|
||||
{formatTemp(daily.temperature_2m_max[0])}°{' '}
|
||||
{formatTemp(daily.temperature_2m_min[0])}°
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -370,7 +384,7 @@ const Weather = ({
|
||||
Wind
|
||||
</p>
|
||||
<p className="font-semibold">
|
||||
{Math.round(current.wind_speed_10m)} km/h
|
||||
{formatWind(current.wind_speed_10m)} {windUnitLabel}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -394,7 +408,8 @@ const Weather = ({
|
||||
Feels Like
|
||||
</p>
|
||||
<p className="font-semibold">
|
||||
{Math.round(current.apparent_temperature)}°C
|
||||
{formatTemp(current.apparent_temperature)}
|
||||
{tempUnitLabel}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -55,19 +55,20 @@ class SearchAgent {
|
||||
});
|
||||
|
||||
const finalContext =
|
||||
searchResults?.findings
|
||||
.filter((f) => f.type === 'search_results')
|
||||
.flatMap((f) => f.results)
|
||||
.map((f) => `${f.metadata.title}: ${f.content}`)
|
||||
searchResults?.searchFindings
|
||||
.map(
|
||||
(f, index) =>
|
||||
`<result index=${index} title=${f.metadata.title}>${f.content}</result>`,
|
||||
)
|
||||
.join('\n') || '';
|
||||
|
||||
const widgetContext = widgetOutputs
|
||||
.map((o) => {
|
||||
return `${o.type}: ${o.llmContext}`;
|
||||
return `<result>${o.llmContext}</result>`;
|
||||
})
|
||||
.join('\n-------------\n');
|
||||
|
||||
const finalContextWithWidgets = `<search_results note="These are the search results and you can cite these">${finalContext}</search_results>\n<widgets_result noteForAssistant="Its output is already showed to the user, you can use this information to answer the query but do not CITE this as a souce">${widgetContext}</widgets_result>`;
|
||||
const finalContextWithWidgets = `<search_results note="These are the search results and assistant can cite these">\n${finalContext}\n</search_results>\n<widgets_result noteForAssistant="Its output is already showed to the user, assistant can use this information to answer the query but do not CITE this as a souce">\n${widgetContext}\n</widgets_result>`;
|
||||
|
||||
const writerPrompt = getWriterPrompt(finalContextWithWidgets);
|
||||
const answerStream = input.config.llm.streamText({
|
||||
|
||||
@@ -50,7 +50,7 @@ class ActionRegistry {
|
||||
static async execute(
|
||||
name: string,
|
||||
params: any,
|
||||
additionalConfig: AdditionalConfig,
|
||||
additionalConfig: AdditionalConfig & { researchBlockId: string },
|
||||
) {
|
||||
const action = this.actions.get(name);
|
||||
|
||||
@@ -63,7 +63,7 @@ class ActionRegistry {
|
||||
|
||||
static async executeAll(
|
||||
actions: ToolCall[],
|
||||
additionalConfig: AdditionalConfig,
|
||||
additionalConfig: AdditionalConfig & { researchBlockId: string },
|
||||
): Promise<ActionOutput[]> {
|
||||
const results: ActionOutput[] = [];
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import z from 'zod';
|
||||
import { ResearchAction } from '../../types';
|
||||
import { Chunk } from '@/lib/types';
|
||||
import { Chunk, ReadingResearchBlock } from '@/lib/types';
|
||||
import TurnDown from 'turndown';
|
||||
import path from 'path';
|
||||
|
||||
const turndownService = new TurnDown();
|
||||
|
||||
@@ -12,12 +13,19 @@ const schema = z.object({
|
||||
const scrapeURLAction: ResearchAction<typeof schema> = {
|
||||
name: 'scrape_url',
|
||||
description:
|
||||
'Use after __plan to scrape and extract content from the provided URLs. This is useful when you need detailed information from specific web pages or if the user asks you to summarize or analyze content from certain links. You can scrape maximum of 3 URLs.',
|
||||
'Use this tool to scrape and extract content from the provided URLs. This is useful when you the user has asked you to extract or summarize information from specific web pages. You can provide up to 3 URLs at a time. NEVER CALL THIS TOOL EXPLICITLY YOURSELF UNLESS INSTRUCTED TO DO SO BY THE USER.',
|
||||
schema: schema,
|
||||
enabled: (_) => true,
|
||||
execute: async (params, additionalConfig) => {
|
||||
params.urls = params.urls.slice(0, 3);
|
||||
|
||||
let readingBlockId = crypto.randomUUID();
|
||||
let readingEmitted = false;
|
||||
|
||||
const researchBlock = additionalConfig.session.getBlock(
|
||||
additionalConfig.researchBlockId,
|
||||
);
|
||||
|
||||
const results: Chunk[] = [];
|
||||
|
||||
await Promise.all(
|
||||
@@ -28,6 +36,70 @@ const scrapeURLAction: ResearchAction<typeof schema> = {
|
||||
|
||||
const title =
|
||||
text.match(/<title>(.*?)<\/title>/i)?.[1] || `Content from ${url}`;
|
||||
|
||||
if (
|
||||
!readingEmitted &&
|
||||
researchBlock &&
|
||||
researchBlock.type === 'research'
|
||||
) {
|
||||
readingEmitted = true;
|
||||
researchBlock.data.subSteps.push({
|
||||
id: readingBlockId,
|
||||
type: 'reading',
|
||||
reading: [
|
||||
{
|
||||
content: '',
|
||||
metadata: {
|
||||
url,
|
||||
title: title,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
additionalConfig.session.updateBlock(
|
||||
additionalConfig.researchBlockId,
|
||||
[
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/data/subSteps',
|
||||
value: researchBlock.data.subSteps,
|
||||
},
|
||||
],
|
||||
);
|
||||
} else if (
|
||||
readingEmitted &&
|
||||
researchBlock &&
|
||||
researchBlock.type === 'research'
|
||||
) {
|
||||
const subStepIndex = researchBlock.data.subSteps.findIndex(
|
||||
(step: any) => step.id === readingBlockId,
|
||||
);
|
||||
|
||||
const subStep = researchBlock.data.subSteps[
|
||||
subStepIndex
|
||||
] as ReadingResearchBlock;
|
||||
|
||||
subStep.reading.push({
|
||||
content: '',
|
||||
metadata: {
|
||||
url,
|
||||
title: title,
|
||||
},
|
||||
});
|
||||
|
||||
additionalConfig.session.updateBlock(
|
||||
additionalConfig.researchBlockId,
|
||||
[
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/data/subSteps',
|
||||
value: researchBlock.data.subSteps,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
const markdown = turndownService.turndown(text);
|
||||
|
||||
results.push({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import z from 'zod';
|
||||
import { ResearchAction } from '../../types';
|
||||
import { searchSearxng } from '@/lib/searxng';
|
||||
import { Chunk } from '@/lib/types';
|
||||
import { Chunk, SearchResultsResearchBlock } from '@/lib/types';
|
||||
|
||||
const actionSchema = z.object({
|
||||
type: z.literal('web_search'),
|
||||
@@ -28,23 +28,90 @@ const webSearchAction: ResearchAction<typeof actionSchema> = {
|
||||
schema: actionSchema,
|
||||
enabled: (config) =>
|
||||
config.classification.classification.skipSearch === false,
|
||||
execute: async (input, _) => {
|
||||
execute: async (input, additionalConfig) => {
|
||||
input.queries = input.queries.slice(0, 3);
|
||||
|
||||
|
||||
const researchBlock = additionalConfig.session.getBlock(
|
||||
additionalConfig.researchBlockId,
|
||||
);
|
||||
|
||||
if (researchBlock && researchBlock.type === 'research') {
|
||||
researchBlock.data.subSteps.push({
|
||||
id: crypto.randomUUID(),
|
||||
type: 'searching',
|
||||
searching: input.queries,
|
||||
});
|
||||
|
||||
additionalConfig.session.updateBlock(additionalConfig.researchBlockId, [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/data/subSteps',
|
||||
value: researchBlock.data.subSteps,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const searchResultsBlockId = crypto.randomUUID();
|
||||
let searchResultsEmitted = false;
|
||||
|
||||
let results: Chunk[] = [];
|
||||
|
||||
const search = async (q: string) => {
|
||||
const res = await searchSearxng(q);
|
||||
|
||||
res.results.forEach((r) => {
|
||||
results.push({
|
||||
content: r.content || r.title,
|
||||
metadata: {
|
||||
title: r.title,
|
||||
url: r.url,
|
||||
},
|
||||
const resultChunks: Chunk[] = res.results.map((r) => ({
|
||||
content: r.content || r.title,
|
||||
metadata: {
|
||||
title: r.title,
|
||||
url: r.url,
|
||||
},
|
||||
}));
|
||||
|
||||
results.push(...resultChunks);
|
||||
|
||||
if (
|
||||
!searchResultsEmitted &&
|
||||
researchBlock &&
|
||||
researchBlock.type === 'research'
|
||||
) {
|
||||
searchResultsEmitted = true;
|
||||
|
||||
researchBlock.data.subSteps.push({
|
||||
id: searchResultsBlockId,
|
||||
type: 'search_results',
|
||||
reading: resultChunks,
|
||||
});
|
||||
});
|
||||
|
||||
additionalConfig.session.updateBlock(additionalConfig.researchBlockId, [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/data/subSteps',
|
||||
value: researchBlock.data.subSteps,
|
||||
},
|
||||
]);
|
||||
} else if (
|
||||
searchResultsEmitted &&
|
||||
researchBlock &&
|
||||
researchBlock.type === 'research'
|
||||
) {
|
||||
const subStepIndex = researchBlock.data.subSteps.findIndex(
|
||||
(step) => step.id === searchResultsBlockId,
|
||||
);
|
||||
|
||||
const subStep = researchBlock.data.subSteps[
|
||||
subStepIndex
|
||||
] as SearchResultsResearchBlock;
|
||||
|
||||
subStep.reading.push(...resultChunks);
|
||||
|
||||
additionalConfig.session.updateBlock(additionalConfig.researchBlockId, [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/data/subSteps',
|
||||
value: researchBlock.data.subSteps,
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
await Promise.all(input.queries.map(search));
|
||||
|
||||
@@ -154,33 +154,11 @@ class Researcher {
|
||||
tool_calls: finalToolCalls,
|
||||
});
|
||||
|
||||
const searchCalls = finalToolCalls.filter(
|
||||
(tc) =>
|
||||
tc.name === 'web_search' ||
|
||||
tc.name === 'academic_search' ||
|
||||
tc.name === 'discussion_search',
|
||||
);
|
||||
|
||||
if (searchCalls.length > 0 && block && block.type === 'research') {
|
||||
block.data.subSteps.push({
|
||||
id: crypto.randomUUID(),
|
||||
type: 'searching',
|
||||
searching: searchCalls.map((sc) => sc.arguments.queries).flat(),
|
||||
});
|
||||
|
||||
session.updateBlock(researchBlockId, [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/data/subSteps',
|
||||
value: block.data.subSteps,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const actionResults = await ActionRegistry.executeAll(finalToolCalls, {
|
||||
llm: input.config.llm,
|
||||
embedding: input.config.embedding,
|
||||
session: session,
|
||||
researchBlockId: researchBlockId,
|
||||
});
|
||||
|
||||
actionOutput.push(...actionResults);
|
||||
@@ -193,39 +171,41 @@ class Researcher {
|
||||
content: JSON.stringify(action),
|
||||
});
|
||||
});
|
||||
|
||||
const searchResults = actionResults.filter(
|
||||
(a) => a.type === 'search_results',
|
||||
);
|
||||
|
||||
if (searchResults.length > 0 && block && block.type === 'research') {
|
||||
block.data.subSteps.push({
|
||||
id: crypto.randomUUID(),
|
||||
type: 'reading',
|
||||
reading: searchResults.flatMap((a) => a.results),
|
||||
});
|
||||
|
||||
session.updateBlock(researchBlockId, [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/data/subSteps',
|
||||
value: block.data.subSteps,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const searchResults = actionOutput.filter(
|
||||
(a) => a.type === 'search_results',
|
||||
);
|
||||
const searchResults = actionOutput
|
||||
.filter((a) => a.type === 'search_results')
|
||||
.flatMap((a) => a.results);
|
||||
|
||||
const seenUrls = new Map<string, number>();
|
||||
|
||||
const filteredSearchResults = searchResults
|
||||
.map((result, index) => {
|
||||
if (result.metadata.url && !seenUrls.has(result.metadata.url)) {
|
||||
seenUrls.set(result.metadata.url, index);
|
||||
return result;
|
||||
} else if (result.metadata.url && seenUrls.has(result.metadata.url)) {
|
||||
const existingIndex = seenUrls.get(result.metadata.url)!;
|
||||
|
||||
const existingResult = searchResults[existingIndex];
|
||||
|
||||
existingResult.content += `\n\n${result.content}`;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return result;
|
||||
})
|
||||
.filter((r) => r !== undefined);
|
||||
|
||||
session.emit('data', {
|
||||
type: 'sources',
|
||||
data: searchResults.flatMap((a) => a.results),
|
||||
data: filteredSearchResults,
|
||||
});
|
||||
|
||||
return {
|
||||
findings: actionOutput,
|
||||
searchFindings: filteredSearchResults,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export type ResearcherInput = {
|
||||
|
||||
export type ResearcherOutput = {
|
||||
findings: ActionOutput[];
|
||||
searchFindings: Chunk[];
|
||||
};
|
||||
|
||||
export type SearchActionOutput = {
|
||||
@@ -103,6 +104,8 @@ export interface ResearchAction<
|
||||
enabled: (config: { classification: ClassifierOutput }) => boolean;
|
||||
execute: (
|
||||
params: z.infer<TSchema>,
|
||||
additionalConfig: AdditionalConfig,
|
||||
additionalConfig: AdditionalConfig & {
|
||||
researchBlockId: string;
|
||||
},
|
||||
) => Promise<ActionOutput>;
|
||||
}
|
||||
|
||||
@@ -17,3 +17,13 @@ export const getShowWeatherWidget = () =>
|
||||
|
||||
export const getShowNewsWidget = () =>
|
||||
getClientConfig('showNewsWidget', 'true') === 'true';
|
||||
|
||||
export const getMeasurementUnit = () => {
|
||||
const value =
|
||||
getClientConfig('measureUnit') ??
|
||||
getClientConfig('measurementUnit', 'metric');
|
||||
|
||||
if (typeof value !== 'string') return 'metric';
|
||||
|
||||
return value.toLowerCase();
|
||||
};
|
||||
|
||||
@@ -75,6 +75,12 @@ export type SearchingResearchBlock = {
|
||||
searching: string[];
|
||||
};
|
||||
|
||||
export type SearchResultsResearchBlock = {
|
||||
id: string;
|
||||
type: 'search_results';
|
||||
reading: Chunk[];
|
||||
};
|
||||
|
||||
export type ReadingResearchBlock = {
|
||||
id: string;
|
||||
type: 'reading';
|
||||
@@ -84,6 +90,7 @@ export type ReadingResearchBlock = {
|
||||
export type ResearchBlockSubStep =
|
||||
| ReasoningResearchBlock
|
||||
| SearchingResearchBlock
|
||||
| SearchResultsResearchBlock
|
||||
| ReadingResearchBlock;
|
||||
|
||||
export type ResearchBlock = {
|
||||
|
||||
16
yarn.lock
16
yarn.lock
@@ -2830,10 +2830,10 @@ fraction.js@^5.2.1:
|
||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-5.3.4.tgz#8c0fcc6a9908262df4ed197427bdeef563e0699a"
|
||||
integrity sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==
|
||||
|
||||
framer-motion@^12.23.24:
|
||||
version "12.23.24"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-12.23.24.tgz#4895b67e880bd2b1089e61fbaa32ae802fc24b8c"
|
||||
integrity sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==
|
||||
framer-motion@^12.23.25:
|
||||
version "12.23.25"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-12.23.25.tgz#32d717f8b172c2673f573c0805ecc37d017441b2"
|
||||
integrity sha512-gUHGl2e4VG66jOcH0JHhuJQr6ZNwrET9g31ZG0xdXzT0CznP7fHX4P8Bcvuc4MiUB90ysNnWX2ukHRIggkl6hQ==
|
||||
dependencies:
|
||||
motion-dom "^12.23.23"
|
||||
motion-utils "^12.23.6"
|
||||
@@ -3750,10 +3750,10 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lucide-react@^0.363.0:
|
||||
version "0.363.0"
|
||||
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.363.0.tgz#2bb1f9d09b830dda86f5118fcd097f87247fe0e3"
|
||||
integrity sha512-AlsfPCsXQyQx7wwsIgzcKOL9LwC498LIMAo+c0Es5PkHJa33xwmYAkkSoKoJWWWSYQEStqu58/jT4tL2gi32uQ==
|
||||
lucide-react@^0.556.0:
|
||||
version "0.556.0"
|
||||
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.556.0.tgz#aad61a065737aef30322695a11fd21c7542c71aa"
|
||||
integrity sha512-iOb8dRk7kLaYBZhR2VlV1CeJGxChBgUthpSP8wom9jfj79qovgG6qcSdiy6vkoREKPnbUYzJsCn4o4PtG3Iy+A==
|
||||
|
||||
mammoth@^1.9.1:
|
||||
version "1.9.1"
|
||||
|
||||
Reference in New Issue
Block a user