rogerserper commited on
Commit
6f7b315
1 Parent(s): 7457e8c

Added Serper.dev API as an alternative web search provider (#302)

Browse files
.env CHANGED
@@ -8,7 +8,8 @@ MONGODB_DIRECT_CONNECTION=false
8
  COOKIE_NAME=hf-chat
9
  HF_ACCESS_TOKEN=#hf_<token> from from https://huggingface.co/settings/token
10
 
11
- # used to activate search with web functionality. disabled if not defined
 
12
  SERPAPI_KEY=#your serpapi key here
13
 
14
  # Parameters to enable "Sign in with HF"
 
8
  COOKIE_NAME=hf-chat
9
  HF_ACCESS_TOKEN=#hf_<token> from from https://huggingface.co/settings/token
10
 
11
+ # used to activate search with web functionality. disabled if none are defined. choose one of the following:
12
+ SERPER_API_KEY=#your serper.dev api key here
13
  SERPAPI_KEY=#your serpapi key here
14
 
15
  # Parameters to enable "Sign in with HF"
src/lib/server/websearch/searchWeb.ts CHANGED
@@ -1,10 +1,53 @@
1
- import { SERPAPI_KEY } from "$env/static/private";
2
 
3
  import { getJson } from "serpapi";
4
  import type { GoogleParameters } from "serpapi";
5
 
6
  // Show result as JSON
7
  export async function searchWeb(query: string) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  const params = {
9
  q: query,
10
  hl: "en",
 
1
+ import { SERPAPI_KEY, SERPER_API_KEY } from "$env/static/private";
2
 
3
  import { getJson } from "serpapi";
4
  import type { GoogleParameters } from "serpapi";
5
 
6
  // Show result as JSON
7
  export async function searchWeb(query: string) {
8
+ if (SERPER_API_KEY) {
9
+ return await searchWebSerper(query);
10
+ }
11
+ if (SERPAPI_KEY) {
12
+ return await searchWebSerpApi(query);
13
+ }
14
+ throw new Error("No Serper.dev or SerpAPI key found");
15
+ }
16
+
17
+ export async function searchWebSerper(query: string) {
18
+ const params = {
19
+ q: query,
20
+ hl: "en",
21
+ gl: "us",
22
+ };
23
+
24
+ const response = await fetch("https://google.serper.dev/search", {
25
+ method: "POST",
26
+ body: JSON.stringify(params),
27
+ headers: {
28
+ "x-api-key": SERPER_API_KEY,
29
+ "Content-type": "application/json; charset=UTF-8",
30
+ },
31
+ });
32
+
33
+ /* eslint-disable @typescript-eslint/no-explicit-any */
34
+ const data = (await response.json()) as Record<string, any>;
35
+
36
+ if (!response.ok) {
37
+ throw new Error(
38
+ data["message"] ??
39
+ `Serper API returned error code ${response.status} - ${response.statusText}`
40
+ );
41
+ }
42
+
43
+ return {
44
+ organic_results: data["organic"] ?? [],
45
+ knowledge_graph: data["knowledgeGraph"] ?? null,
46
+ answer_box: data["answerBox"] ?? null,
47
+ };
48
+ }
49
+
50
+ export async function searchWebSerpApi(query: string) {
51
  const params = {
52
  q: query,
53
  hl: "en",
src/lib/types/WebSearch.ts CHANGED
@@ -12,6 +12,7 @@ export interface WebSearch extends Timestamps {
12
  searchQuery: string;
13
  results: string[];
14
  knowledgeGraph: string;
 
15
  summary: string;
16
 
17
  messages: WebSearchMessage[];
 
12
  searchQuery: string;
13
  results: string[];
14
  knowledgeGraph: string;
15
+ answerBox: string;
16
  summary: string;
17
 
18
  messages: WebSearchMessage[];
src/routes/+layout.server.ts CHANGED
@@ -6,7 +6,7 @@ import { UrlDependency } from "$lib/types/UrlDependency";
6
  import { defaultModel, models, oldModels, validateModel } from "$lib/server/models";
7
  import { authCondition, requiresUser } from "$lib/server/auth";
8
  import { DEFAULT_SETTINGS } from "$lib/types/Settings";
9
- import { SERPAPI_KEY } from "$env/static/private";
10
 
11
  export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
12
  const { conversations } = collections;
@@ -61,7 +61,7 @@ export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
61
  DEFAULT_SETTINGS.shareConversationsWithModelAuthors,
62
  ethicsModalAcceptedAt: settings?.ethicsModalAcceptedAt ?? null,
63
  activeModel: settings?.activeModel ?? DEFAULT_SETTINGS.activeModel,
64
- searchEnabled: !!SERPAPI_KEY,
65
  },
66
  models: models.map((model) => ({
67
  id: model.id,
 
6
  import { defaultModel, models, oldModels, validateModel } from "$lib/server/models";
7
  import { authCondition, requiresUser } from "$lib/server/auth";
8
  import { DEFAULT_SETTINGS } from "$lib/types/Settings";
9
+ import { SERPAPI_KEY, SERPER_API_KEY } from "$env/static/private";
10
 
11
  export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
12
  const { conversations } = collections;
 
61
  DEFAULT_SETTINGS.shareConversationsWithModelAuthors,
62
  ethicsModalAcceptedAt: settings?.ethicsModalAcceptedAt ?? null,
63
  activeModel: settings?.activeModel ?? DEFAULT_SETTINGS.activeModel,
64
+ searchEnabled: !!(SERPAPI_KEY || SERPER_API_KEY),
65
  },
66
  models: models.map((model) => ({
67
  id: model.id,
src/routes/conversation/[id]/web-search/+server.ts CHANGED
@@ -50,6 +50,7 @@ export async function GET({ params, locals, url }) {
50
  prompt: prompt,
51
  searchQuery: "",
52
  knowledgeGraph: "",
 
53
  results: [],
54
  summary: "",
55
  messages: [],
@@ -79,7 +80,12 @@ export async function GET({ params, locals, url }) {
79
  results.organic_results.map((el: { link: string }) => el.link)) ??
80
  [];
81
 
82
- if (results.knowledge_graph) {
 
 
 
 
 
83
  // if google returns a knowledge graph, we use it
84
  webSearch.knowledgeGraph = JSON.stringify(removeLinks(results.knowledge_graph));
85
  text = webSearch.knowledgeGraph;
 
50
  prompt: prompt,
51
  searchQuery: "",
52
  knowledgeGraph: "",
53
+ answerBox: "",
54
  results: [],
55
  summary: "",
56
  messages: [],
 
80
  results.organic_results.map((el: { link: string }) => el.link)) ??
81
  [];
82
 
83
+ if (results.answer_box) {
84
+ // if google returns an answer box, we use it
85
+ webSearch.answerBox = JSON.stringify(removeLinks(results.answer_box));
86
+ text = webSearch.answerBox;
87
+ appendUpdate("Found a Google answer box");
88
+ } else if (results.knowledge_graph) {
89
  // if google returns a knowledge graph, we use it
90
  webSearch.knowledgeGraph = JSON.stringify(removeLinks(results.knowledge_graph));
91
  text = webSearch.knowledgeGraph;