ai impl
ci / docker_image (push) Failing after 1s
ci / deploy (push) Has been skipped

This commit is contained in:
2026-02-06 02:39:23 -06:00
parent 156e74450a
commit 90bbe4bfa4
11 changed files with 207 additions and 59 deletions
@@ -8,7 +8,7 @@
import { Separator } from '$lib/components/ui/separator';
import TrashIcon from '@lucide/svelte/icons/trash';
import ImageUpload from '$lib/components/custom/image-upload/image-upload.svelte';
import { genDescription } from '$lib/db/items.remote';
import { approveDenyItem, genDescription } from '$lib/db/items.remote';
import { EMAIL_REGEX_STRING } from '$lib/consts';
import type { Item } from '$lib/types/item.server';
import { Textarea } from '$lib/components/ui/textarea';
@@ -26,9 +26,9 @@
let description: string = $derived(item ? item.description : '');
let isGenerating = $state(false);
async function onSelect() {
async function onSelect(file: File) {
isGenerating = true;
description = await genDescription();
description = await genDescription(file);
isGenerating = false;
}
@@ -110,13 +110,23 @@
</Field.Group>
<Dialog.Footer class="mt-4">
<Dialog.Close
type="button"
class={buttonVariants({ variant: "outline" })}
>
Cancel
</Dialog.Close>
<Button type="submit">Submit</Button>
<div class="flex justify-between w-full">
<Button variant="destructive"
onclick={() => {approveDenyItem({id: item?.id || 0, approved: false}); invalidateAll();}}>
Delete
</Button>
<div>
<Dialog.Close
type="button"
class={buttonVariants({ variant: "outline" })}
>
Cancel
</Dialog.Close>
<Button type="submit" class="ml-1">Submit</Button>
</div>
</div>
</Dialog.Footer>
</form>
{#if item?.threads}
@@ -14,7 +14,6 @@
export let previewUrl: string | null = null;
let dragging = false;
console.log(previewUrl);
function openFileDialog() {
if (!disabled) {
@@ -69,6 +68,7 @@
disabled={disabled}
capture="environment"
on:change={onInputChange}
formaction="/item?/describe"
hidden
/>
@@ -31,7 +31,7 @@
</script>
<div
class="h-full bg-card text-card-foreground flex flex-col gap-2 rounded-xl border shadow-sm max-w-sm overflow-hidden min-2-3xs">
class="h-full bg-card text-card-foreground flex flex-col gap-2 rounded-xl border shadow-sm max-w-sm overflow-hidden min-w-2xs">
{#if item.image}
<img src="https://fbla26.marinodev.com/uploads/{item.id}.jpg" class="object-cover min-h-48 max-h-48"
alt="Lost item">
@@ -16,9 +16,15 @@
let description: string | undefined = $state();
let isGenerating = $state(false);
async function onSelect() {
async function onSelect(file: File) {
isGenerating = true;
description = await genDescription();
const buffer = await file.arrayBuffer();
const base64 = btoa(
String.fromCharCode(...new Uint8Array(buffer))
);
description = await genDescription(base64);
isGenerating = false;
}
+23 -4
View File
@@ -1,12 +1,31 @@
import { command, getRequestEvent, query } from '$app/server';
import { command, getRequestEvent } from '$app/server';
import * as v from 'valibot';
import sql from '$lib/db/db.server';
import { verifyJWT } from '$lib/auth/index.server';
import sharp from 'sharp';
import { LLMDescribe } from '$lib/llm/llm.server';
export const genDescription = query(async () => {
await new Promise((f) => setTimeout(f, 1000));
export const genDescription = command(v.string(), async (data) => {
const inputBuffer = Buffer.from(data, 'base64');
return 'A matte black water bottle with a black lid and a "BKLYN BENTO" logo on the side, resting on a tree trunk in a forest.';
// Convert File → Buffer
// const inputBuffer = Buffer.from(await file.arrayBuffer());
// Detect format (Sharp does this internally)
const image = sharp(inputBuffer).rotate();
const outputBuffer = await image
.rotate()
.resize({ fit: 'outside', height: 400, width: 400 })
.jpeg({ quality: 80 }) // adjust if needed
.toBuffer();
const description = await LLMDescribe(
`data:image/jpeg;base64,${outputBuffer.toString('base64')}`
);
console.log(description);
return description;
});
export const approveDenyItem = command(
+11 -11
View File
@@ -1,7 +1,7 @@
import nodemailer from 'nodemailer';
// Create a transporter object using SMTP transport
const transporter = nodemailer.createTransport({
export const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: Number(process.env.EMAIL_PORT),
secure: true, // true for 465, false for other ports
@@ -11,13 +11,13 @@ const transporter = nodemailer.createTransport({
}
});
export async function sendEmployerNotificationEmail() {
// Send mail with defined transport object
await transporter.sendMail({
from: `CareerConnect Notifications <${process.env.EMAIL_USER}>`,
// to: info.emails.join(', '), // EMAILING OF REAL COMPANIES DISABLED, UNCOMMENT TO ENABLE
to: 'drake@marinodev.com', // TEMPORARY EMAIL FOR TESTING
subject: 'New Application Received!',
text: `A new application has been received for the posting ''!\n\nCheck it out at ${process.env.BASE_URL}/`
});
}
// export async function sendEmail() {
// // Send mail with defined transport object
// await transporter.sendMail({
// from: `Westuffind Notifier <${process.env.EMAIL_USER}>`,
// // to: info.emails.join(', '), // EMAILING OF REAL COMPANIES DISABLED, UNCOMMENT TO ENABLE
// to: 'drake@marinodev.com', // TEMPORARY EMAIL FOR TESTING
// subject: 'New Application Received!',
// text: `A new application has been received for the posting ''!\n\nCheck it out at ${process.env.BASE_URL}/`
// });
// }
+44
View File
@@ -0,0 +1,44 @@
export async function LLMDescribe(imageData: string) {
const payload = {
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'Describe only the main object in this image in one sentence. Include its color, shape, brand, or any text on it. Focus solely on the object itself. Example: A blue Hydroflask water bottle with a dented lid and a sticker of a mountain on the side.'
},
{
type: 'image_url',
image_url: {
url: imageData
}
}
]
}
],
max_tokens: 128,
temperature: 0.2
};
console.log('AIing it');
const res = await fetch(
`http://${process.env.LLAMA_HOST!}:${process.env.LLAMA_PORT!}/v1/chat/completions`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
}
);
if (!res.ok) {
console.error(await res.text());
process.exit(1);
}
const data = await res.json();
return data.choices[0].message.content;
}