import { Signal, signal, useSignal } from '@preact/signals' import { useEffect, useRef } from 'preact/hooks' import { JSX } from 'preact' import { JsonParseStream } from '$std/json/mod.ts' import { CSS, render as renderMd } from 'gfm' const systemHistory = signal([{ role: 'system', content: `Tu es un assistant spécialisé dans l'open source qui s'appel Coh@bot. Tu répond en français avec des réponse courtes. Tu aide les usagers du Fablab nommé Cohabit en leur proposant des solutions techniques appropriées en français. Tu formule des réponses courtes et précises en français et en markdown si besoin.`, }]) const currentReader = signal | null>( null, ) let currentResponse: string[] = [] function MdCell({ children }: { children: Signal }) { return (
) } type BotMessage = { role: string content: string } type BotResponse = { model: string created_at: string message: { role: 'assistant' | 'user' | 'system' content: string images?: string } done: boolean } async function aiRequest( { model, messages, stream }: { model: string messages: BotMessage[] stream: boolean }, ) { systemHistory.value = [...systemHistory.peek(), ...messages] const body = JSON.stringify({ model, messages: systemHistory, stream, }) console.log('send', JSON.parse(body)) const response = await fetch('http://localhost:11434/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8', }, body, }) if (response.ok) { return response.body?.pipeThrough(new TextDecoderStream()).pipeThrough( new JsonParseStream(), ) as ReadableStream ?? new ReadableStream() } throw new Error(response.statusText) } export default function AiChatBox() { const dialog = useRef(null) const form = useRef(null) const history = useSignal([]) useEffect(() => { dialog.current?.addEventListener('click', (event) => { if (event.target === dialog.current) { dialog.current?.close() } }) form.current?.addEventListener( 'submit', (event) => chatListener(event, history), ) }, []) return ( <>
{history}
) } async function chatListener(event: Event, history: Signal) { event.preventDefault() const form = event.target as HTMLFormElement const query = new FormData(form).get( 'query', ) as string form.reset() const userEntry = ( {query} ) const botMessage = signal('') const botEntry = ( {botMessage} ) history.value = [...history.peek(), userEntry, botEntry] if (currentResponse.length !== 0) { systemHistory.value = [...systemHistory.peek(), { role: 'assistant', content: currentResponse.join(''), }] currentResponse = [] } await currentReader.value?.cancel() const response = await aiRequest({ model: 'mistral', messages: [{ role: 'user', content: query, }], stream: true, }) const reader = response.getReader() currentReader.value = reader while (true) { const { value, done } = await reader.read() currentResponse.push(value?.message.content ?? '') if (done) break console.log(value.message.content) botMessage.value = `${botMessage.peek()}${value.message.content}` } }