diff --git a/frontend/package.json b/frontend/package.json index 36b74b6..37cc923 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -51,8 +51,6 @@ "vue-tsc": "^2.2.8" }, "msw": { - "workerDirectory": [ - "public" - ] + "workerDirectory": ["public"] } } diff --git a/frontend/src/api/mocks/aiHandlers.ts b/frontend/src/api/mocks/aiHandlers.ts new file mode 100644 index 0000000..d5b0ae9 --- /dev/null +++ b/frontend/src/api/mocks/aiHandlers.ts @@ -0,0 +1,11 @@ +import { faker } from "@faker-js/faker"; +import { http, HttpResponse } from "msw"; + +export default [ + http.post("/ai/chat", () => { + const response = HttpResponse.json({ + message: faker.lorem.sentence(1000), + }); + return response; + }), +]; diff --git a/frontend/src/api/mocks/setup.ts b/frontend/src/api/mocks/setup.ts index 0209acb..4a5881f 100644 --- a/frontend/src/api/mocks/setup.ts +++ b/frontend/src/api/mocks/setup.ts @@ -6,6 +6,7 @@ import roleHandlers from "./roleHandlers"; import userHandlers from "./userHandlers"; import departmentHandlers from "./departmentHandlers"; import positionHandlers from "./positionHandlers"; +import aiHandlers from "./aiHandlers"; export const worker = setupWorker( ...userHandlers, ...authHandlers, @@ -14,4 +15,5 @@ export const worker = setupWorker( ...jobHandlers, ...departmentHandlers, ...positionHandlers, + ...aiHandlers, ); diff --git a/frontend/src/api/schema/openapi.json b/frontend/src/api/schema/openapi.json index 9678d32..49aa550 100644 --- a/frontend/src/api/schema/openapi.json +++ b/frontend/src/api/schema/openapi.json @@ -691,6 +691,39 @@ } } }, + "/ai/chat": { + "post": { + "tags": [ + "ai-controller" + ], + "operationId": "chat", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/event-stream": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, "/scheduler/page-query": { "get": { "tags": [ diff --git a/frontend/src/api/types/schema.d.ts b/frontend/src/api/types/schema.d.ts index a254da5..ca1b349 100644 --- a/frontend/src/api/types/schema.d.ts +++ b/frontend/src/api/types/schema.d.ts @@ -340,6 +340,22 @@ export interface paths { patch?: never; trace?: never; }; + "/ai/chat": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["chat"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/scheduler/page-query": { parameters: { query?: never; @@ -1333,6 +1349,30 @@ export interface operations { }; }; }; + chat: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": string; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/event-stream": string[]; + }; + }; + }; + }; pageQuery: { parameters: { query: { diff --git a/frontend/src/components/Button.vue b/frontend/src/components/Button.vue new file mode 100644 index 0000000..8be4c36 --- /dev/null +++ b/frontend/src/components/Button.vue @@ -0,0 +1,28 @@ + + + diff --git a/frontend/src/components/icons/LoadingIcon.vue b/frontend/src/components/icons/LoadingIcon.vue new file mode 100644 index 0000000..2a0f62f --- /dev/null +++ b/frontend/src/components/icons/LoadingIcon.vue @@ -0,0 +1,6 @@ + diff --git a/frontend/src/composables/ai/useAiChat.ts b/frontend/src/composables/ai/useAiChat.ts new file mode 100644 index 0000000..b085a18 --- /dev/null +++ b/frontend/src/composables/ai/useAiChat.ts @@ -0,0 +1,31 @@ +import { ref } from "vue"; +import client from "../../api/client"; + +export const useAiChat = () => { + const messages = ref([]); + const isLoading = ref(false); + + const chat = async (message: string) => { + isLoading.value = true; + try { + const { response } = await client.POST("/ai/chat", { + body: message, + parseAs: "stream", + }); + const reader = response.body?.getReader(); + if (reader) { + const decoder = new TextDecoder(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + messages.value.push(decoder.decode(value, { stream: true })); + console.log(decoder.decode(value)); + } + return; + } + } finally { + isLoading.value = false; + } + }; + return { messages, chat, isLoading }; +}; diff --git a/frontend/src/views/AiChatView.vue b/frontend/src/views/AiChatView.vue index 9228370..aa62ac0 100644 --- a/frontend/src/views/AiChatView.vue +++ b/frontend/src/views/AiChatView.vue @@ -1,50 +1,30 @@ + +