victor HF staff nsarrazin HF staff commited on
Commit
7c4fdc9
1 Parent(s): 7767757

Reopen: Feature/disable login for n messages (#356)

Browse files

* disable login on first message

* update banner here too

* modal wording tweaks

* prevent NaN

* fix login wall

* fix flicker

* lint

* put modal text behind login check

* fix bug with sending messages without login

* fix misalignment between ui and api

* fix data update on disable login

---------

Co-authored-by: Nathan Sarrazin <sarrazin.nathan@gmail.com>

.env CHANGED
@@ -60,10 +60,10 @@ PUBLIC_SHARE_PREFIX=#https://hf.co/chat
60
  PUBLIC_GOOGLE_ANALYTICS_ID=#G-XXXXXXXX / Leave empty to disable
61
  PUBLIC_DEPRECATED_GOOGLE_ANALYTICS_ID=#UA-XXXXXXXX-X / Leave empty to disable
62
  PUBLIC_ANNOUNCEMENT_BANNERS=`[
63
- {
64
- "title": "Chat UI is now open sourced on GitHub",
65
- "linkTitle": "GitHub repo",
66
- "linkHref": "https://github.com/huggingface/chat-ui"
67
  }
68
  ]`
69
 
@@ -72,6 +72,7 @@ PARQUET_EXPORT_HF_TOKEN=
72
  PARQUET_EXPORT_SECRET=
73
 
74
  RATE_LIMIT= # requests per minute
 
75
 
76
  PUBLIC_APP_NAME=ChatUI # name used as title throughout the app
77
  PUBLIC_APP_ASSETS=chatui # used to find logos & favicons in static/$PUBLIC_APP_ASSETS
 
60
  PUBLIC_GOOGLE_ANALYTICS_ID=#G-XXXXXXXX / Leave empty to disable
61
  PUBLIC_DEPRECATED_GOOGLE_ANALYTICS_ID=#UA-XXXXXXXX-X / Leave empty to disable
62
  PUBLIC_ANNOUNCEMENT_BANNERS=`[
63
+ {
64
+ "title": "Llama v2 is live on HuggingChat! 🦙",
65
+ "linkTitle": "Announcement",
66
+ "linkHref": "https://huggingface.co/blog/llama2"
67
  }
68
  ]`
69
 
 
72
  PARQUET_EXPORT_SECRET=
73
 
74
  RATE_LIMIT= # requests per minute
75
+ MESSAGES_BEFORE_LOGIN=# how many messages a user can send in a conversation before having to login. set to 0 to force login right away
76
 
77
  PUBLIC_APP_NAME=ChatUI # name used as title throughout the app
78
  PUBLIC_APP_ASSETS=chatui # used to find logos & favicons in static/$PUBLIC_APP_ASSETS
src/hooks.server.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { COOKIE_NAME } from "$env/static/private";
2
  import type { Handle } from "@sveltejs/kit";
3
  import {
4
  PUBLIC_GOOGLE_ANALYTICS_ID,
@@ -64,7 +64,11 @@ export const handle: Handle = async ({ event, resolve }) => {
64
  !event.url.pathname.startsWith(`${base}/admin`) &&
65
  !["GET", "OPTIONS", "HEAD"].includes(event.request.method)
66
  ) {
67
- if (!user && requiresUser) {
 
 
 
 
68
  return errorResponse(401, ERROR_MESSAGES.authOnly);
69
  }
70
 
 
1
+ import { COOKIE_NAME, MESSAGES_BEFORE_LOGIN } from "$env/static/private";
2
  import type { Handle } from "@sveltejs/kit";
3
  import {
4
  PUBLIC_GOOGLE_ANALYTICS_ID,
 
64
  !event.url.pathname.startsWith(`${base}/admin`) &&
65
  !["GET", "OPTIONS", "HEAD"].includes(event.request.method)
66
  ) {
67
+ if (
68
+ !user &&
69
+ requiresUser &&
70
+ !((MESSAGES_BEFORE_LOGIN ? parseInt(MESSAGES_BEFORE_LOGIN) : 0) > 0)
71
+ ) {
72
  return errorResponse(401, ERROR_MESSAGES.authOnly);
73
  }
74
 
src/lib/components/LoginModal.svelte CHANGED
@@ -25,11 +25,16 @@
25
  v{PUBLIC_VERSION}
26
  </div>
27
  </h2>
28
- <p class="px-4 text-lg font-semibold leading-snug text-gray-800 sm:px-12">
29
- This application is for demonstration purposes only.
30
- </p>
 
 
 
 
 
31
  <p class="text-base text-gray-800">
32
- AI is an area of active research with known problems such as biased generation and
33
  misinformation. Do not use this application for high-stakes decisions or advice.
34
  </p>
35
  {#if PUBLIC_APP_DATA_SHARING}
@@ -54,7 +59,6 @@
54
  with <LogoHuggingFaceBorderless classNames="text-xl mr-1 ml-1.5" /> Hugging Face
55
  {/if}
56
  </button>
57
- <p class="mt-2 px-2 text-sm text-gray-500">to start chatting right away</p>
58
  {:else}
59
  <input type="hidden" name="ethicsModalAccepted" value={true} />
60
  {#each Object.entries(settings) as [key, val]}
 
25
  v{PUBLIC_VERSION}
26
  </div>
27
  </h2>
28
+ {#if $page.data.requiresLogin}
29
+ <p
30
+ class="px-4 text-lg font-semibold leading-snug text-gray-800 sm:px-12"
31
+ style="text-wrap: balance;"
32
+ >
33
+ Please Sign in with Hugging Face to continue
34
+ </p>
35
+ {/if}
36
  <p class="text-base text-gray-800">
37
+ Disclaimer: AI is an area of active research with known problems such as biased generation and
38
  misinformation. Do not use this application for high-stakes decisions or advice.
39
  </p>
40
  {#if PUBLIC_APP_DATA_SHARING}
 
59
  with <LogoHuggingFaceBorderless classNames="text-xl mr-1 ml-1.5" /> Hugging Face
60
  {/if}
61
  </button>
 
62
  {:else}
63
  <input type="hidden" name="ethicsModalAccepted" value={true} />
64
  {#each Object.entries(settings) as [key, val]}
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, SERPER_API_KEY } from "$env/static/private";
10
 
11
  export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
12
  const { conversations } = collections;
@@ -82,5 +82,6 @@ export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
82
  email: locals.user.email,
83
  },
84
  requiresLogin: requiresUser,
 
85
  };
86
  };
 
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, MESSAGES_BEFORE_LOGIN } from "$env/static/private";
10
 
11
  export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
12
  const { conversations } = collections;
 
82
  email: locals.user.email,
83
  },
84
  requiresLogin: requiresUser,
85
+ messagesBeforeLogin: MESSAGES_BEFORE_LOGIN ? parseInt(MESSAGES_BEFORE_LOGIN) : 0,
86
  };
87
  };
src/routes/+layout.svelte CHANGED
@@ -178,7 +178,7 @@
178
  {#if isSettingsOpen}
179
  <SettingsModal on:close={() => (isSettingsOpen = false)} settings={data.settings} />
180
  {/if}
181
- {#if requiresLogin}
182
  <LoginModal settings={data.settings} />
183
  {/if}
184
  <slot />
 
178
  {#if isSettingsOpen}
179
  <SettingsModal on:close={() => (isSettingsOpen = false)} settings={data.settings} />
180
  {/if}
181
+ {#if requiresLogin && data.messagesBeforeLogin === 0}
182
  <LoginModal settings={data.settings} />
183
  {/if}
184
  <slot />
src/routes/conversation/[id]/+page.svelte CHANGED
@@ -15,7 +15,7 @@
15
  import { webSearchParameters } from "$lib/stores/webSearchParameters";
16
  import type { WebSearchMessage } from "$lib/types/WebSearch";
17
  import type { Message } from "$lib/types/Message";
18
- import { browser } from "$app/environment";
19
 
20
  export let data;
21
 
@@ -33,6 +33,7 @@
33
 
34
  let loading = false;
35
  let pending = false;
 
36
 
37
  async function getTextGenerationStream(
38
  inputs: string,
@@ -195,7 +196,6 @@
195
  await getTextGenerationStream(message, messageId, isRetry, searchResponseId ?? undefined);
196
 
197
  webSearchMessages = [];
198
- if (browser) invalidate(UrlDependency.Conversation);
199
 
200
  if (messages.filter((m) => m.from === "user").length === 1) {
201
  summarizeTitle($page.params.id)
@@ -259,6 +259,12 @@
259
  });
260
  $: $page.params.id, (isAborted = true);
261
  $: title = data.conversations.find((conv) => conv.id === $page.params.id)?.title ?? data.title;
 
 
 
 
 
 
262
  </script>
263
 
264
  <svelte:head>
@@ -279,4 +285,5 @@
279
  models={data.models}
280
  currentModel={findCurrentModel([...data.models, ...data.oldModels], data.model)}
281
  settings={data.settings}
 
282
  />
 
15
  import { webSearchParameters } from "$lib/stores/webSearchParameters";
16
  import type { WebSearchMessage } from "$lib/types/WebSearch";
17
  import type { Message } from "$lib/types/Message";
18
+ import { PUBLIC_APP_DISCLAIMER } from "$env/static/public";
19
 
20
  export let data;
21
 
 
33
 
34
  let loading = false;
35
  let pending = false;
36
+ let loginRequired = false;
37
 
38
  async function getTextGenerationStream(
39
  inputs: string,
 
196
  await getTextGenerationStream(message, messageId, isRetry, searchResponseId ?? undefined);
197
 
198
  webSearchMessages = [];
 
199
 
200
  if (messages.filter((m) => m.from === "user").length === 1) {
201
  summarizeTitle($page.params.id)
 
259
  });
260
  $: $page.params.id, (isAborted = true);
261
  $: title = data.conversations.find((conv) => conv.id === $page.params.id)?.title ?? data.title;
262
+
263
+ $: loginRequired =
264
+ (data.requiresLogin
265
+ ? !data.user
266
+ : !data.settings.ethicsModalAcceptedAt && !!PUBLIC_APP_DISCLAIMER) &&
267
+ messages.length >= data.messagesBeforeLogin;
268
  </script>
269
 
270
  <svelte:head>
 
285
  models={data.models}
286
  currentModel={findCurrentModel([...data.models, ...data.oldModels], data.model)}
287
  settings={data.settings}
288
+ {loginRequired}
289
  />
src/routes/conversation/[id]/+server.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { RATE_LIMIT } from "$env/static/private";
2
  import { buildPrompt } from "$lib/buildPrompt";
3
  import { PUBLIC_SEP_TOKEN } from "$lib/constants/publicSepToken";
4
  import { abortedGenerations } from "$lib/server/abortedGenerations";
5
- import { authCondition } from "$lib/server/auth";
6
  import { collections } from "$lib/server/database";
7
  import { modelEndpoint } from "$lib/server/modelEndpoint";
8
  import { models } from "$lib/server/models";
@@ -37,6 +37,14 @@ export async function POST({ request, fetch, locals, params, getClientAddress })
37
  throw error(404, "Conversation not found");
38
  }
39
 
 
 
 
 
 
 
 
 
40
  if (RATE_LIMIT !== "") {
41
  let nEvents = 0;
42
  if (locals.user?._id) {
 
1
+ import { MESSAGES_BEFORE_LOGIN, RATE_LIMIT } from "$env/static/private";
2
  import { buildPrompt } from "$lib/buildPrompt";
3
  import { PUBLIC_SEP_TOKEN } from "$lib/constants/publicSepToken";
4
  import { abortedGenerations } from "$lib/server/abortedGenerations";
5
+ import { authCondition, requiresUser } from "$lib/server/auth";
6
  import { collections } from "$lib/server/database";
7
  import { modelEndpoint } from "$lib/server/modelEndpoint";
8
  import { models } from "$lib/server/models";
 
37
  throw error(404, "Conversation not found");
38
  }
39
 
40
+ if (
41
+ !locals.user?._id &&
42
+ requiresUser &&
43
+ conv.messages.length > (MESSAGES_BEFORE_LOGIN ? parseInt(MESSAGES_BEFORE_LOGIN) : 0)
44
+ ) {
45
+ throw error(429, "Exceeded number of messages before login");
46
+ }
47
+
48
  if (RATE_LIMIT !== "") {
49
  let nEvents = 0;
50
  if (locals.user?._id) {