add a profile photo for chatbot, styling fixes, add button to reset session, load an epub instead of our github vector embeddings
This commit is contained in:
6
src/app.d.ts
vendored
6
src/app.d.ts
vendored
@@ -3,11 +3,13 @@
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
interface Locals {
|
||||
sessionId: string;
|
||||
}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
export { };
|
||||
|
@@ -1,5 +1,26 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import type { SearchResult } from './utils/search';
|
||||
import type { HumanMessage, AIMessage, SystemMessage } from '@langchain/core/messages';
|
||||
|
||||
const initArray: SearchResult[] = [];
|
||||
export const searchResults = writable(initArray);
|
||||
|
||||
export type ChatHistory = (HumanMessage | AIMessage | SystemMessage)[];
|
||||
|
||||
const chatHistories: Record<string, ChatHistory> = {};
|
||||
|
||||
export const chatStore = writable(chatHistories);
|
||||
|
||||
export function getChatHistory(sessionId: string): ChatHistory {
|
||||
return chatHistories[sessionId] || [];
|
||||
}
|
||||
|
||||
export function setChatHistory(sessionId: string, history: ChatHistory): void {
|
||||
chatHistories[sessionId] = history;
|
||||
chatStore.set(chatHistories);
|
||||
}
|
||||
|
||||
export function clearChatHistory(sessionId: string): void {
|
||||
chatHistories[sessionId] = [];
|
||||
chatStore.set(chatHistories);
|
||||
}
|
||||
|
@@ -1,11 +1,10 @@
|
||||
<script lang="ts">
|
||||
import '../../app.css';
|
||||
import type { SearchResult } from '$lib/utils/search';
|
||||
import { searchResults } from '$lib/store';
|
||||
import { searchResults, type ChatHistory } from '$lib/store';
|
||||
import { onMount } from 'svelte';
|
||||
import { marked } from 'marked';
|
||||
import { HumanMessage, AIMessage } from '@langchain/core/messages';
|
||||
import type { ChatHistory } from '../api/ai/+server';
|
||||
import { PUBLIC_LOAD_DUMMY_HISTORY } from '$env/static/public';
|
||||
|
||||
let searchResultsValue: SearchResult[] = [];
|
||||
@@ -86,6 +85,22 @@
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleNewSession() {
|
||||
try {
|
||||
const response = await fetch('/api/ai/new-session', { method: 'POST' });
|
||||
if (response.ok) {
|
||||
const historyResponse = await fetch('/api/ai');
|
||||
const data = await historyResponse.json();
|
||||
chatHistory = data.chatHistory || [];
|
||||
query = '';
|
||||
} else {
|
||||
console.error('Failed to start new session');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error starting new session:', error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -93,13 +108,29 @@
|
||||
</svelte:head>
|
||||
|
||||
{#if searchResultsValue.length === 0}
|
||||
<div class="flex-grow flex-col overflow-auto p-4">
|
||||
<div class="flex-grow flex-col overflow-auto p-4 my-2 bg-base-200">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<div class="avatar">
|
||||
<div class="ring-primary ring-offset-base-100 w-64 rounded-full ring ring-offset-2">
|
||||
<img src="/imgs/ai/profile.jpg" alt="Portrait of an orange tabby cat reading a book" />
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
This lil guy just finished reading <a
|
||||
class="link link-primary"
|
||||
href="https://bookshop.org/p/books/a-history-of-tea-the-life-and-times-of-the-world-s-favorite-beverage-laura-c-martin/11044690"
|
||||
target="_blank"
|
||||
>
|
||||
A History of Tea
|
||||
</a>, so ask 'em anything about the book.
|
||||
</span>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
{#each chatHistory as message}
|
||||
{@const { role, content } = getRoleAndContent(message)}
|
||||
{#if role === 'human'}
|
||||
<div class="chat chat-end">
|
||||
<div class="chat-bubble chat-bubble-primary">
|
||||
<div class="chat-bubble chat-bubble-primary prose">
|
||||
{@html renderMarkdown(content)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,30 +143,29 @@
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="mt-4">
|
||||
<div class="mt-4 flex flex-col items-center justify-center">
|
||||
<span class="loading loading-dots loading-lg"></span>
|
||||
<span>This may take a minute; streaming is still a work in progress.</span>
|
||||
</div>
|
||||
{/if}
|
||||
<form on:submit|preventDefault={handleSubmit} class="mt-4 flex-col">
|
||||
<label class="form-control">
|
||||
<div class="label">
|
||||
<span class="label-text">
|
||||
Querying the Authenticator <a
|
||||
href="https://git.silentsilas.com/silentsilas/Authenticator"
|
||||
target="_blank"
|
||||
class="link-primary">repository</a
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<textarea
|
||||
bind:value={query}
|
||||
class="textarea textarea-bordered h-24"
|
||||
placeholder="Type your message here..."
|
||||
></textarea>
|
||||
</label>
|
||||
<button type="submit" class="btn btn-block btn-primary mt-2" disabled={loading}>
|
||||
<button type="submit" class="btn btn-block btn-primary mt-4" disabled={loading}>
|
||||
Send
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-block btn-error btn-outline mt-2"
|
||||
on:click={handleNewSession}
|
||||
>
|
||||
New Session
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
|
@@ -1,47 +1,34 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { FaissStore } from '@langchain/community/vectorstores/faiss';
|
||||
import { OpenAIEmbeddings } from '@langchain/openai';
|
||||
import { ChatAnthropic } from '@langchain/anthropic';
|
||||
import { RunnableSequence, RunnablePassthrough } from '@langchain/core/runnables';
|
||||
import { StringOutputParser } from '@langchain/core/output_parsers';
|
||||
import { ChatPromptTemplate } from '@langchain/core/prompts';
|
||||
import { join } from 'path';
|
||||
import { HumanMessage, AIMessage, SystemMessage } from '@langchain/core/messages';
|
||||
import { HumanMessage, AIMessage } from '@langchain/core/messages';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
|
||||
const chatHistories: Record<string, ChatHistory> = {};
|
||||
|
||||
type VectorDocument = {
|
||||
pageContent: string;
|
||||
};
|
||||
|
||||
const formatDocumentsAsString = (documents: VectorDocument[]) => {
|
||||
return documents.map((doc) => doc.pageContent).join('\n\n');
|
||||
};
|
||||
|
||||
export type ChatHistory = HumanMessage[] | AIMessage[] | SystemMessage[];
|
||||
import { EPubLoader } from "@langchain/community/document_loaders/fs/epub";
|
||||
import { join } from 'path';
|
||||
import { getChatHistory, setChatHistory } from '$lib/store';
|
||||
|
||||
export async function POST({ request, locals }: RequestEvent): Promise<Response> {
|
||||
const { query } = await request.json();
|
||||
const sessionId = locals.sessionId;
|
||||
|
||||
if (!chatHistories[sessionId]) {
|
||||
chatHistories[sessionId] = [];
|
||||
}
|
||||
const chatHistory = chatHistories[sessionId];
|
||||
const chatHistory = getChatHistory(sessionId);
|
||||
|
||||
const directory = join(process.cwd(), 'vectorstore');
|
||||
const embeddings = new OpenAIEmbeddings();
|
||||
const vectorStore = await FaissStore.load(directory, embeddings);
|
||||
const vectorStoreRetriever = vectorStore.asRetriever();
|
||||
const directory = join(process.cwd(), 'static/book.epub');
|
||||
const loader = new EPubLoader(directory);
|
||||
const docs = await loader.load();
|
||||
|
||||
const context = docs.map(doc => doc.pageContent).join('\n\n');
|
||||
|
||||
const model = new ChatAnthropic({
|
||||
modelName: 'claude-3-5-sonnet-20240620',
|
||||
anthropicApiKey: process.env.ANTHROPIC_API_KEY
|
||||
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
||||
});
|
||||
|
||||
const SYSTEM_TEMPLATE = `Use the following pieces of context to answer the question at the end.
|
||||
If you don't know the answer, just say that you don't know, don't try to make up an answer.
|
||||
Always format your response in markdown.
|
||||
|
||||
----------------
|
||||
|
||||
@@ -59,7 +46,7 @@ Question:
|
||||
|
||||
const chain = RunnableSequence.from([
|
||||
{
|
||||
context: vectorStoreRetriever.pipe(formatDocumentsAsString),
|
||||
context: () => context,
|
||||
question: new RunnablePassthrough(),
|
||||
},
|
||||
prompt,
|
||||
@@ -72,11 +59,13 @@ Question:
|
||||
chatHistory.push(new HumanMessage({ content: query }));
|
||||
chatHistory.push(new AIMessage({ content: answer }));
|
||||
|
||||
setChatHistory(sessionId, chatHistory);
|
||||
|
||||
return json({ response: answer, chatHistory });
|
||||
}
|
||||
|
||||
export async function GET({ locals }): Promise<Response> {
|
||||
const sessionId = locals.sessionId;
|
||||
const chatHistory = chatHistories[sessionId] || [];
|
||||
const chatHistory = getChatHistory(sessionId);
|
||||
return json({ chatHistory });
|
||||
}
|
||||
|
8
src/routes/api/ai/new-session/+server.ts
Normal file
8
src/routes/api/ai/new-session/+server.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
import { clearChatHistory } from '$lib/store';
|
||||
|
||||
export async function POST({ locals }: RequestEvent) {
|
||||
const sessionId = locals.sessionId;
|
||||
clearChatHistory(sessionId);
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
@@ -34,13 +34,12 @@
|
||||
'An experiment with 3D vector math for a rudimentary simulation of gravity. You can change the strength of the gravity in the Controls menu.'
|
||||
},
|
||||
{
|
||||
title: 'Animation Editor',
|
||||
path: '/editor',
|
||||
source:
|
||||
'https://git.silentsilas.com/silentsilas/playground/src/branch/main/src/routes/editor',
|
||||
title: 'Tea Guru',
|
||||
path: '/ai',
|
||||
source: 'https://git.silentsilas.com/silentsilas/playground/src/branch/main/src/routes/ai',
|
||||
position: [4, -2, -4],
|
||||
description:
|
||||
'A 3D animation editor via Theatre.js. This is used internally by me for other ThreeJS projects.'
|
||||
'A chatbot trained on an in-depth book about tea. Ask it about anything related to tea!'
|
||||
},
|
||||
{
|
||||
title: 'Headbang',
|
||||
|
Reference in New Issue
Block a user