mirror of
https://github.com/ArthurDanjou/artagents.git
synced 2026-01-14 12:14:40 +01:00
feat: add chat and file management APIs, implement chat loading and saving functionality
- Introduced new API endpoints for chat management including posting and retrieving chat messages. - Implemented file upload and deletion functionalities for chat and other files. - Added utility functions for streaming text and loading chat data. - Created TypeScript types for models and agents used in the application. - Configured TypeScript settings for server and project. - Added favicon and workspace configuration for pnpm.
This commit is contained in:
55
server/api/chat.post.ts
Normal file
55
server/api/chat.post.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { Message } from 'ai'
|
||||
import { appendResponseMessages, appendClientMessage, streamText } from 'ai'
|
||||
import { createWorkersAI } from 'workers-ai-provider'
|
||||
import { loadChat } from '~~/server/utils/chat'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { id, message, model, agent } = await readBody(event)
|
||||
|
||||
console.log(model, agent)
|
||||
|
||||
const workersAI = createWorkersAI({ binding: hubAI() })
|
||||
|
||||
const previousMessages = await loadChat(id)
|
||||
|
||||
const messages = appendClientMessage({
|
||||
messages: previousMessages,
|
||||
message
|
||||
})
|
||||
|
||||
const result = streamText({
|
||||
model: workersAI('@cf/meta/llama-3.1-8b-instruct'),
|
||||
// system, TODO: add system
|
||||
// prompt, TODO: add prompt
|
||||
messages,
|
||||
// tools, TODO: add tools
|
||||
async onFinish({ response }) {
|
||||
await saveChat({
|
||||
id,
|
||||
messages: appendResponseMessages({
|
||||
messages,
|
||||
responseMessages: response.messages
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
result.consumeStream()
|
||||
|
||||
return result.toDataStreamResponse()
|
||||
})
|
||||
|
||||
async function saveChat({ id, messages }: { id: string, messages: Message[] }) {
|
||||
const hub = hubBlob()
|
||||
|
||||
await hub.delete(`chats/${id}.json`)
|
||||
await $fetch('/api/chats', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
file: {
|
||||
name: `${id}.json`,
|
||||
content: JSON.stringify(messages, null, 2)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
27
server/api/chats/[...pathname].get.ts
Normal file
27
server/api/chats/[...pathname].get.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { streamToText } from '~~/server/utils/stream'
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const { pathname } = getRouterParams(event)
|
||||
|
||||
try {
|
||||
const stream = await hubBlob().serve(event, `chats/${pathname}.json`)
|
||||
|
||||
if (!(stream instanceof ReadableStream)) {
|
||||
throw new Error('Le stream n\'est pas valide')
|
||||
}
|
||||
|
||||
const read = await streamToText(stream)
|
||||
|
||||
try {
|
||||
return JSON.parse(read)
|
||||
}
|
||||
catch (jsonError) {
|
||||
console.error('Erreur lors du parsing JSON:', jsonError)
|
||||
return { error: 'Erreur de format JSON' }
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Erreur lors du traitement du stream:', error)
|
||||
return { error: 'Erreur serveur' }
|
||||
}
|
||||
})
|
||||
18
server/api/chats/index.post.ts
Normal file
18
server/api/chats/index.post.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export default eventHandler(async (event) => {
|
||||
const body = await readBody(event)
|
||||
const file = new File([body.file.content], body.file.name, { type: 'application/json' })
|
||||
|
||||
if (!file || !file.size) {
|
||||
throw createError({ statusCode: 400, message: 'No file provided' })
|
||||
}
|
||||
|
||||
ensureBlob(file, {
|
||||
maxSize: '1MB',
|
||||
types: ['application/jsonml+json', 'application/json']
|
||||
})
|
||||
|
||||
return hubBlob().put(file.name, file, {
|
||||
addRandomSuffix: false,
|
||||
prefix: 'chats'
|
||||
})
|
||||
})
|
||||
12
server/api/files/index.delete.ts
Normal file
12
server/api/files/index.delete.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { z } from 'zod'
|
||||
import { useValidatedBody } from 'h3-zod'
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const { pathname } = await useValidatedBody(event, {
|
||||
pathname: z.string()
|
||||
})
|
||||
|
||||
await hubBlob().del(pathname)
|
||||
|
||||
return sendNoContent(event)
|
||||
})
|
||||
3
server/api/files/index.get.ts
Normal file
3
server/api/files/index.get.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default defineEventHandler(() => {
|
||||
return hubBlob().list()
|
||||
})
|
||||
18
server/api/files/index.post.ts
Normal file
18
server/api/files/index.post.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export default eventHandler(async (event) => {
|
||||
const form = await readFormData(event)
|
||||
const file = form.get('file') as File
|
||||
|
||||
if (!file || !file.size) {
|
||||
throw createError({ statusCode: 400, message: 'No file provided' })
|
||||
}
|
||||
|
||||
ensureBlob(file, {
|
||||
maxSize: '1MB',
|
||||
types: ['image', 'pdf', 'application/pdf']
|
||||
})
|
||||
|
||||
return hubBlob().put(file.name, file, {
|
||||
addRandomSuffix: false,
|
||||
prefix: 'files'
|
||||
})
|
||||
})
|
||||
5
server/routes/files/[...pathname].get.ts
Normal file
5
server/routes/files/[...pathname].get.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default eventHandler(async (event) => {
|
||||
const { pathname } = getRouterParams(event)
|
||||
|
||||
return hubBlob().serve(event, pathname)
|
||||
})
|
||||
3
server/tsconfig.json
Normal file
3
server/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
||||
33
server/utils/chat.ts
Normal file
33
server/utils/chat.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Message } from 'ai'
|
||||
|
||||
export async function loadChat(slug: string): Promise<Message[]> {
|
||||
const { blobs } = await $fetch('/api/files')
|
||||
|
||||
if (!blobs.find(item => item.pathname === `chats/${slug}.json`)) {
|
||||
await createChat(slug)
|
||||
}
|
||||
|
||||
const data = await $fetch<string>(`/api/chats/${slug}`)
|
||||
const dataString = JSON.stringify(data)
|
||||
|
||||
if (dataString === '[]') return []
|
||||
return JSON.parse(dataString)
|
||||
}
|
||||
|
||||
async function createChat(slug: string) {
|
||||
await $fetch('/api/chats', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
file: {
|
||||
name: `${slug}.json`,
|
||||
content: '[]'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function deleteChat(slug: string) {
|
||||
await $fetch(`/api/chats/${slug}.json`, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
||||
13
server/utils/stream.ts
Normal file
13
server/utils/stream.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export async function streamToText(stream: ReadableStream<Uint8Array>) {
|
||||
const reader = stream.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let result = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
result += decoder.decode(value, { stream: true })
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user