Skip to content

Commit

Permalink
feat: attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
lmiller1990 committed Jul 11, 2024
1 parent 72fd7ae commit 9ffb31e
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 8 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
"@pinia/nuxt": "^0.5.1",
"@shikijs/markdown-it": "^1.10.1",
"@types/events": "^3.0.3",
"@types/formidable": "^3.4.5",
"@types/markdown-it": "^14.1.1",
"@vueuse/core": "^10.10.0",
"events": "^3.3.0",
"formidable": "^3.5.1",
"h3-formidable": "^1.0.0",
"markdown-it": "^14.1.0",
"nuxt": "3.12.2",
"nuxt-auth-utils": "^0.0.25",
Expand Down
45 changes: 40 additions & 5 deletions pages/threads/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,34 @@ const { data, refresh } = await useFetch(`/api/threads/${route.params.id}`);
const msg = ref("");
const textAreaRef = ref<{ textarea: HTMLTextAreaElement }>();
const attachmentModel = ref("");
onMounted(() => {
textAreaRef.value?.textarea.focus();
});
const localMessages = ref<SerializeObject<Message>[]>([]);
const attachments = ref<string[]>([]);
const uploadingAttachments = ref(false);
async function handleUpload(e: Event) {
uploadingAttachments.value = true;
const files = (e.target as any).files as File[];
const form = new FormData();
let i = 0;
for (const file of files) {
form.append(i.toString(), file);
i++;
}
const { fileIds } = await $fetch("/api/attachments", {
method: "POST",
body: form,
});
attachments.value = fileIds;
uploadingAttachments.value = false;
}
const allMessages = computed(() => {
return [
Expand Down Expand Up @@ -91,6 +113,7 @@ async function handleSubmitMessage() {
body: JSON.stringify({
threadId: id,
message: cachedMsg,
files: attachments.value,
}),
});
const reader = response?.body?.getReader()!;
Expand All @@ -117,6 +140,7 @@ async function handleSubmitMessage() {
controller.close();
reader.releaseLock();
msg.value = "";
attachmentModel.value = "";
},
});
}
Expand Down Expand Up @@ -197,11 +221,22 @@ function handleKeydown(event: KeyboardEvent) {
class="w-full mb-2"
ref="textAreaRef"
/>
<UButton
type="submit"
:disabled="submitting || !msg.length"
>Send</UButton
>
<div class="flex">
<UInput
type="file"
:disabled="submitting"
class="mr-2"
multiple
v-model="attachmentModel"
accept="image/png, image/jpeg, image/jpg"
@input="handleUpload"
/>
<UButton
type="submit"
:disabled="submitting || !msg.length || uploadingAttachments"
>Send</UButton
>
</div>
</form>
</template>

Expand Down
48 changes: 48 additions & 0 deletions server/api/attachments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { readFiles } from "h3-formidable";
import fs from "node:fs";
import { rename } from "node:fs/promises";
import path from "path";
import { openai } from "~/server/open_ai";

export default eventHandler(async (event) => {
const { files } = await readFiles(event, {
// formidable options
// https://github.com/node-formidable/formidable#options
});

const allFiles = Object.values(files).map((x) => x![0]);

const fileIds = await Promise.all(
allFiles.map(async (file) => {
const ext = path.extname(file.originalFilename!);
const newname = `${file.filepath}${ext}`;
console.log(`Uploading`, newname);
await rename(file.filepath, newname);
return openai.files.create({
file: fs.createReadStream(newname),
purpose: "assistants",
});
}),
);

return { fileIds: fileIds.map((x) => x.id) };

// for (const fileId of fileIds) {
// const uploaded = await openai.beta.threads.messages.create("", {
// role: "user",
// content: [
// {
// type: "image_file",
// image_file: {
// file_id: fileId.id,
// },
// },
// ],
// });
// }

// for (const file of Object.values(files)) {

// console.log(file?.[0].filepath, file?.[0]);
// }
});
24 changes: 22 additions & 2 deletions server/api/message.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { assistants } from "~/server/shared";
import { getUser } from "~/server/token";
import { queryForThreadById } from "~/src/dynamo";
import { Writable } from "stream";
import { ImageFileContentBlock } from "openai/resources/beta/threads/messages.mjs";

interface StreamCallbackOptions {
send: (chunk: string) => void;
Expand Down Expand Up @@ -39,7 +40,11 @@ export default defineEventHandler(async (event) => {
const user = await getUser(event);
// await tryDeductFreeMessage(user);

const body = await readBody<{ threadId: string; message: string }>(event);
const body = await readBody<{
threadId: string;
message: string;
files: string[];
}>(event);
const thread = await queryForThreadById(user.email, body.threadId);

if (!thread) {
Expand All @@ -48,9 +53,24 @@ export default defineEventHandler(async (event) => {

console.log(`Creating message: ${body.message} in thread: ${body.threadId}`);

const files = body.files.map<ImageFileContentBlock>((file_id) => {
return {
type: "image_file",
image_file: {
file_id,
},
};
});

await openai.beta.threads.messages.create(thread.openai_id, {
role: "user",
content: body.message,
content: [
...files,
{
type: "text",
text: body.message,
},
],
});

const res = event.node.res;
Expand Down
43 changes: 42 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2752,6 +2752,13 @@
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.3.tgz#a8ef894305af28d1fc6d2dfdfc98e899591ea529"
integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==

"@types/formidable@^3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-3.4.5.tgz#8e45c053cac5868e2b71cc7410e2bd92872f6b9c"
integrity sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==
dependencies:
"@types/node" "*"

"@types/http-proxy@^1.17.14":
version "1.17.14"
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec"
Expand Down Expand Up @@ -3351,6 +3358,11 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==

asap@^2.0.0:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==

assertion-error@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
Expand Down Expand Up @@ -4222,6 +4234,14 @@ devalue@^5.0.0:
resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.0.0.tgz#1ca0099a7d715b4d6cac3924e770ccbbc584ad98"
integrity sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==

dezalgo@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81"
integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==
dependencies:
asap "^2.0.0"
wrappy "1"

didyoumean@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
Expand Down Expand Up @@ -4666,6 +4686,15 @@ formdata-node@^4.3.2:
node-domexception "1.0.0"
web-streams-polyfill "4.0.0-beta.3"

formidable@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.1.tgz#9360a23a656f261207868b1484624c4c8d06ee1a"
integrity sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==
dependencies:
dezalgo "^1.0.4"
hexoid "^1.0.0"
once "^1.4.0"

fraction.js@^4.3.7:
version "4.3.7"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
Expand Down Expand Up @@ -4921,6 +4950,13 @@ gzip-size@^7.0.0:
dependencies:
duplexer "^0.1.2"

h3-formidable@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/h3-formidable/-/h3-formidable-1.0.0.tgz#669dfabd3835a581987208c0742ef7a0e399e768"
integrity sha512-RZf0e17Q1tGFc60KJb9BrptcbSnO75r/YhyQ5OtEjvAimhYY80M1yVDu2DldwcnXvYYngxAcvGOG7xihJldfzg==
dependencies:
formidable "^3.5.1"

h3@^1.10.2, h3@^1.11.1, h3@^1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/h3/-/h3-1.12.0.tgz#9d7f05f08a997d263e484b02436cb027df3026d8"
Expand Down Expand Up @@ -4988,6 +5024,11 @@ hasown@^2.0.0, hasown@^2.0.2:
dependencies:
function-bind "^1.1.2"

hexoid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==

hookable@^5.5.3:
version "5.5.3"
resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d"
Expand Down Expand Up @@ -6397,7 +6438,7 @@ [email protected], on-finished@^2.3.0:
dependencies:
ee-first "1.1.1"

once@^1.3.0:
once@^1.3.0, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
Expand Down

0 comments on commit 9ffb31e

Please sign in to comment.