refactor: vercel ai sdk

This commit is contained in:
tglide
2023-07-07 19:26:20 +01:00
parent 2bfd693224
commit 1daa098560
4 changed files with 93 additions and 3129 deletions
+80 -2990
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -24,6 +24,7 @@
"@popperjs/core": "^2.11.6",
"@sentry/svelte": "^7.44.2",
"@sentry/tracing": "^7.44.2",
"ai": "^2.1.15",
"analytics": "^0.8.1",
"dotenv": "^16.0.3",
"echarts": "^5.4.1",
+10 -138
View File
@@ -1,120 +1,12 @@
<script lang="ts">
import Code from '$lib/components/code.svelte';
import { slide } from 'svelte/transition';
import Template from './template.svelte';
const MOCK = false;
const mockAnswer =
'To create a database with Web SDK in Appwrite, you can add it to your Appwrite project\'s dashboard by accessing the Databases Service settings from your project\'s left-hand navigation panel. Then, click the Create Database button, name your new database, and optionally provide a custom database ID. You can also create databases with the Appwrite CLI or the Appwrite Server SDKs. Once you have created your database, you can add collections to your database by navigating to it and then clicking on the Create Collection button. You can use Appwrite Realtime API to subscribe to live changes in your database. To create a document in the database, you can use the createDocument() method provided by the Web SDK. Here is an example code snippet in Kotlin for creating a document:\n\n```\nimport io.appwrite.Client\nimport io.appwrite.services.Databases\n\nsuspend fun main() {\n val client = Client(applicationContext)\n .setEndpoint("https://cloud.appwrite.io/v1")\n .setProject("[PROJECT_ID]")\n\n val databases = Databases(client)\n\n try {\n val document = databases.createDocument(\n databaseId = "[DATABASE_ID]",\n collectionId = "[COLLECTION_ID]",\n documentId = ID.unique(),\n data = mapOf("a" to "b"),\n )\n } catch (e: Exception) {\n Log.e("Appwrite", "Error: " + e.message)\n }\n}\n```\nSOURCES: https://appwrite.io/docs/databases';
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
import { useCompletion } from 'ai/svelte';
let question = '';
let answer: string | null = null;
let loading = false;
async function handleSubmit() {
loading = true;
answer = null;
try {
if (MOCK) {
await sleep(1000);
answer = mockAnswer;
} else {
const res = await fetch(
'http://64.227.148.116/chat?question=' + encodeURI(question),
{
headers: {
accept: 'application/json'
}
}
);
const data = await res.json();
answer = data.answer;
}
} finally {
loading = false;
}
}
type AnswerSources = {
type: 'sources';
content: string[];
};
type AnswerText = {
type: 'text';
content: string;
};
type AnswerCode = {
type: 'code';
content: string;
};
type ParsedAnwer = Array<AnswerSources | AnswerText | AnswerCode>;
function parseAnswer(answer: string) {
if (!answer) return null;
// Separate into text and code blocks
const textAndCodeBlocks: Array<AnswerText | AnswerCode> = [];
const blocks = answer.matchAll(/```(?<code>.*?)```/gs);
let lastIndex = 0;
for (const block of blocks) {
const [full, code] = block;
const index = answer.indexOf(full);
if (index > lastIndex) {
textAndCodeBlocks.push({
content: answer.slice(lastIndex, index),
type: 'text'
});
}
textAndCodeBlocks.push({
content: code,
type: 'code'
});
lastIndex = index + full.length;
}
if (lastIndex < answer.length) {
textAndCodeBlocks.push({
content: answer.slice(lastIndex),
type: 'text'
});
}
// Split text blocks into source and text blocks
const parsedAnswer: ParsedAnwer = [];
for (let i = 0; i < textAndCodeBlocks.length; i++) {
const block = textAndCodeBlocks[i];
// Can match Sources: or Source:
// Ignore case
const sourcesMatch = block.content.match(/SOURCES?:\s*(?<sources>.*?)(\n|$)/i);
if (block.type === 'code' || !sourcesMatch) {
parsedAnswer.push(block);
continue;
}
parsedAnswer.push({
type: 'text',
content: block.content.slice(0, sourcesMatch.index)
});
const sources = sourcesMatch.groups?.sources;
parsedAnswer.push({
type: 'sources',
content: sources.split(',').map((s) => {
const match = s.match(/https?:\/\/(?<url>.*)/);
return match?.groups?.url || s;
})
});
}
return parsedAnswer;
}
$: parsedAnswer = parseAnswer(answer);
const { input, handleSubmit, completion, isLoading } = useCompletion({
api: 'http://localhost:3003/'
});
</script>
<Template --command-panel-width="600px">
@@ -128,45 +20,25 @@
class="input-text"
autofocus
placeholder="Ask AI..."
bind:value={question}
disabled={loading} />
bind:value={$input}
disabled={$isLoading} />
<div class="options-list">
{#if loading}
{#if $isLoading}
<div class="loader" />
{:else}
<button
class="options-list-button"
aria-label="ask AI"
type="submit"
disabled={!question.trim() || loading}>
disabled={!$input.trim() || $isLoading}>
<span class="icon-arrow-sm-right" aria-hidden="true" />
</button>
{/if}
</div>
</form>
{#if parsedAnswer}
{#if $completion}
<div class="answer" transition:slide|local={{ duration: 250 }}>
{#each parsedAnswer as block}
{#if block.type === 'text'}
<p>{block.content}</p>
{:else if block.type === 'code'}
<!-- TODO: Get language automatically -->
<div class="code">
<Code code={block.content} language="js" noMargin />
</div>
{:else if block.type === 'sources'}
<div class="u-margin-block-start-8">
<p>Sources:</p>
<ul>
{#each block.content as source}
<li>
<a class="link" href={source} target="_blank">{source}</a>
</li>
{/each}
</ul>
</div>
{/if}
{/each}
{$completion}
</div>
{/if}
</Template>
+2 -1
View File
@@ -10,7 +10,8 @@ export const VARS = {
| undefined,
CONSOLE_MODE: import.meta.env?.VITE_CONSOLE_MODE?.toString() as string | undefined,
VERCEL_ENV: import.meta.env?.VITE_VERCEL_ENV?.toString() as string | undefined,
GOOGLE_ANALYTICS: import.meta.env?.VITE_GA_PROJECT?.toString() as string | undefined
GOOGLE_ANALYTICS: import.meta.env?.VITE_GA_PROJECT?.toString() as string | undefined,
AI_ENDPOINT: import.meta.env?.VITE_AI_ENDPOINT?.toString() as string | undefined
};
export const ENV = {