basic llm request stream

This commit is contained in:
fabien committed 2026-09-22 21:15:23 +02:00
1 parent f74e4d1043
commit a92cc76a9a
66 files changed
+1496 -158

No files matched your search

@@ -1,8 +1,35 @@
<div class="container">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
<script lang="ts">
import {autofocus} from "$lib/ui/autofocus"
import {chatApi} from "$lib/api/endpoints/chat"
import type {Chat} from "$lib/types/api"
import type { PageProps } from './$types';
import { goto } from "$app/navigation";
let { data }: PageProps = $props();
let chat = $state({} as Chat)
let prompt = $state("")
const handleOnSubmit = async () => {
chatApi.createChat(data.spaceId, chat)
.then((result)=>{
goto(`/${data.spaceId}/${result.id}`, {state: {prompt: prompt }})
}).catch((err)=>console.log(err))
}
</script>
<div class="h-full w-full flex items-center public">
<div class="container">
<form class="request flex w-full" onsubmit={handleOnSubmit}>
<input
class="chatinput w-full flex-1 focus:outline-hidden focus:shadow-none focus:ring-0"
type="text"
placeholder="Demande ce que tu veux"
bind:value={prompt}
use:autofocus
/>
<button class="chatbutton">Go</button>
</form>
</div>
</div>
@@ -1,3 +1,8 @@
export function load() {
return { title: 'Conversation' };
import type { PageLoad } from "./$types";
export const load: PageLoad = ({params}) => {
return {
title: 'Nouvelle conversation',
spaceId : params.space,
};
}
@@ -0,0 +1,75 @@
<script lang="ts">
import {autofocus} from "$lib/ui/autofocus"
import { onMount } from "svelte"
import {page} from "$app/state"
import {chatApi} from "$lib/api/endpoints/chat"
import type { PageProps } from './$types';
import type { Message, Chunk } from "$lib/types/api"
import SvelteMarkdown from '@humanspeak/svelte-markdown'
let prompt = $state("")
let { data }: PageProps = $props();
let messages = $state([] as Message[])
onMount(async ()=>{
if (page.state.prompt) {
console.log(page.state.prompt)
await sendRequest(page.state.prompt)
}
})
let historyDiv: HTMLDivElement;
$effect(() => {
if (historyDiv) {
console.log("effect")
historyDiv.scrollTop = historyDiv.scrollHeight;
}
});
const sendRequest = async(theprompt) => {
prompt = ""
let lastrole=""
for await (const chunk of chatApi.createChatMessage(data.spaceId, data.chatId, { prompt: theprompt })) {
if (chunk.role!=lastrole) {
const currentDate = new Date()
messages.push({
id: currentDate.getTime(),
role: chunk.role,
content: chunk.content,
})
lastrole=chunk.role
} else {
messages[messages.length-1].content += chunk.content
}
historyDiv.scrollTop = historyDiv.scrollHeight;
}
}
const handleOnSubmit = async ()=>{
await sendRequest(prompt)
}
</script>
<div class="h-full w-full flex flex-col">
<div id="history" class="flex-1 container overflow-auto" bind:this={historyDiv}>
{#each messages as message (message.id)}
<div class={["message", "rounded", message.role]}>
<div class="message-content">
<SvelteMarkdown source={message.content} />
</div>
</div>
{/each}
</div>
<div id="prompt" class="container">
<form class="request flex w-full" onsubmit={handleOnSubmit}>
<input
class="chatinput w-full flex-1 focus:outline-hidden focus:shadow-none focus:ring-0"
type="text"
placeholder="..."
bind:value={prompt}
use:autofocus
/>
<button class="chatbutton">Go</button>
</form>
</div>
</div>
@@ -0,0 +1,9 @@
import type { PageLoad } from "./$types";
export const load: PageLoad = ({params}) => {
return {
title: 'Nouvelle conversation',
spaceId: params.space,
chatId: params.conversation,
};
}
@@ -100,7 +100,6 @@
let updSpace = space
updSpace.defaultAgentId = agentId
teamApi.updateSpace(updSpace.id, updSpace).then((newdata)=>{
console.log(newdata)
spaceState.spaces.map((s)=>s.id===spaceState.current?newdata:s)
space=newdata
}).catch((err)=>{
@@ -3,6 +3,7 @@
import { auth, setToken } from '$lib/state/auth.svelte';
import { goto } from '$app/navigation';
import { authApi } from "$lib/api/endpoints/auth"
import { validateEmail } from "$lib/utilities/validators"
type Credentials = {
email: string
@@ -17,7 +18,7 @@
})
const formSubmitEmail = () => {
credentials.validemail = true
credentials.validemail = validateEmail(credentials.email)
}
const formSubmitPass = (e: Event) => {
@@ -43,16 +44,16 @@
<form onsubmit={formSubmitEmail}>
<div class="flex">
<label for="email" class="block mb-2 flex-1">Email</label>
<a href="/recover-password" class="text-xs">Mot de passe oublié ?</a>
<a href="/recover-password" class="text-xs ">Mot de passe oublié ?</a>
</div>
<div class="flex flex-row">
<input
type="text"
bind:value={credentials.email}
class="textinput mr-1"
class="textinput mr-1 flex-1"
autofocus
/>
<button class="bt" title="page suivante">
<button class="bt small" title="page suivante">
<i class="icon-right-big"></i>
</button>
</div>
@@ -72,10 +73,10 @@
<input
type="password"
bind:value={credentials.password}
class="textinput mr-1"
class="textinput mr-1 flex-1"
autofocus
/>
<button class="bt">
<button class="bt small">
Entrer
</button>
</div>
+46 -8
View File
@@ -11,7 +11,7 @@ body {
@apply h-full bg-stone-100 text-stone-950 dark:bg-stone-950 dark:text-stone-100;
}
.formelement > label {
label {
@apply text-xs text-stone-600 uppercase dark:text-stone-400;
}
hr {
@@ -30,9 +30,9 @@ h2 {
}
.bt {
appearance: button;
@apply min-w-40 cursor-pointer rounded bg-stone-300 px-4 py-1 transition-all duration-300 hover:bg-stone-200 dark:bg-stone-800 dark:hover:bg-stone-700;
@apply min-w-40 cursor-pointer rounded bg-stone-300 px-4 py-1 transition-all duration-300 hover:bg-stone-400 dark:bg-stone-800 dark:hover:bg-stone-700;
&.small {
@apply min-w-0 px-2 py-1;
@apply min-w-2 px-2 py-1;
}
&.changed,
&.saving {
@@ -62,10 +62,12 @@ h2 {
.public h1 {
@apply mb-4 text-3xl font-bold;
}
.profile a,
.public a {
.profile a {
@apply bg-sky-300 dark:bg-sky-700;
}
.public a {
@apply text-sky-500 dark:text-sky-500;
}
#outer {
@apply flex h-full flex-row;
@@ -113,10 +115,18 @@ h2 {
}
}
.chatinput {
@apply w-full rounded-l-xl border-stone-300 bg-stone-50 p-2 text-stone-900 dark:border-stone-700 dark:bg-stone-900 dark:text-stone-100;
}
.chatbutton {
@apply min-w-12 cursor-pointer rounded-r-xl bg-stone-300 px-2 py-1 transition-all duration-300 hover:bg-stone-400 dark:bg-stone-700 dark:hover:bg-stone-600;
}
/* form */
.formgroup {
@apply rounded border border-stone-300 bg-stone-100 p-2 text-sm dark:border-stone-700 dark:bg-stone-900;
@apply rounded border border-stone-300 bg-stone-200 p-2 text-sm dark:border-stone-700 dark:bg-stone-900;
}
.formelement {
@@ -124,11 +134,11 @@ h2 {
}
.textinput {
@apply h-8 w-full rounded border-stone-700 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100;
@apply h-8 w-full rounded border-stone-300 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:border-stone-700 dark:bg-stone-950 dark:text-stone-100;
}
.areainput {
@apply w-full rounded border-stone-700 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100;
@apply w-full rounded border-stone-300 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100;
}
/* svelte select */
@@ -261,3 +271,31 @@ h2 {
.list-item {
@apply list-none;
}
/* chat */
.message {
@apply m-2 rounded p-2;
&.user {
@apply bg-sky-500 dark:text-white;
}
&.assistant {
&.reasoning {
@apply text-white/50;
}
}
}
.message-content {
@apply text-sm;
}
.message-content p {
@apply my-2;
}
.message-content pre {
@apply my-2 whitespace-pre dark:bg-stone-700;
}