From aca069141d03ffdb51a4a2fb37eed0414b746347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andri=20=C3=93skarsson?= Date: Thu, 7 Nov 2024 17:11:48 +0100 Subject: [PATCH] insult my car --- src/actions/hello/ai.ts | 60 -------------- src/actions/index.ts | 4 +- src/actions/insults/cars.ts | 61 ++++++++++++++ src/actions/insults/country-ai.ts | 60 ++++++++++++++ .../{hello => insults}/country-list.ts | 0 src/actions/{hello => insults}/country.ts | 0 src/actions/{hello => insults}/index.ts | 36 +++++++-- src/pages/insult-my-car.astro | 79 +++++++++++++++++++ src/pages/insult-my-country.astro | 8 +- 9 files changed, 237 insertions(+), 71 deletions(-) delete mode 100644 src/actions/hello/ai.ts create mode 100644 src/actions/insults/cars.ts create mode 100644 src/actions/insults/country-ai.ts rename src/actions/{hello => insults}/country-list.ts (100%) rename src/actions/{hello => insults}/country.ts (100%) rename src/actions/{hello => insults}/index.ts (61%) create mode 100644 src/pages/insult-my-car.astro diff --git a/src/actions/hello/ai.ts b/src/actions/hello/ai.ts deleted file mode 100644 index cd59a6f..0000000 --- a/src/actions/hello/ai.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { OpenAI } from "openai"; - -const client = new OpenAI({ - baseURL: "https://api.model.box/v1", - apiKey: import.meta.env.MODEL_BOX_API_KEY, -}); - -export function greetingPrompt(country: string) { - return [ - `You are a travel journalist knowledgeable of all countries of the world`, - `You are creative and full of sarcasm and wit`, - `Make up a random fact about ${country}`, - ].join(". "); -} - -export function imagePrompt(insult: string, country: string) { - const prompt = [ - `Summarize the following joke about ${country}: "${insult}"`, - "stunning shot, beautiful nature, people", - "Caricature art-style", - "No text, No titles, No quotes", - ]; - return prompt.join(". "); -} - -export async function promptAI(prompt: string): Promise { - const data = await client.chat.completions.create({ - //model: "meta-llama/llama-3.2-3b-instruct", - model: "deepseek/deepseek-chat", - - messages: [ - { - role: "user", - content: prompt, - }, - ], - temperature: 1.5, - max_tokens: 150, - max_completion_tokens: 100, - }); - - const msg = data.choices[0].message?.content; - if (!msg) - throw new Error("No response from AI", { - cause: data, - }); - return msg; -} - -export async function aiImage(prompt: string): Promise { - const data = await client.images.generate({ - prompt, - n: 1, - size: "512x512", - model: "black-forest-labs/flux-schnell", - }); - - const img = data.data[0]; - return img.url; -} diff --git a/src/actions/index.ts b/src/actions/index.ts index 84bdfc4..3c358a3 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -1,6 +1,6 @@ import { defineAction } from "astro:actions"; import { z } from "astro:schema"; -import { hello } from "./hello"; +import { insults } from "./insults"; export const server = { contactMe: defineAction({ @@ -13,5 +13,5 @@ export const server = { return `Hello, ${input.email}!`; }, }), - hello, + insults, }; diff --git a/src/actions/insults/cars.ts b/src/actions/insults/cars.ts new file mode 100644 index 0000000..557eba5 --- /dev/null +++ b/src/actions/insults/cars.ts @@ -0,0 +1,61 @@ +type CarBrand = { + name: string; + models: string[]; +}; + +export const CAR_BRANDS_WITH_MODELS: CarBrand[] = [ + { name: "Toyota", models: ["Camry", "Corolla", "RAV4"] }, + { name: "Ford", models: ["Mustang", "F-150", "Explorer"] }, + { name: "Honda", models: ["Civic", "Accord", "CR-V"] }, + { name: "Chevrolet", models: ["Silverado", "Malibu", "Equinox"] }, + { name: "Nissan", models: ["Altima", "Rogue", "Sentra"] }, + { name: "Volkswagen", models: ["Golf", "Jetta", "Tiguan"] }, + { name: "Hyundai", models: ["Elantra", "Tucson", "Santa Fe"] }, + { name: "BMW", models: ["3 Series", "5 Series", "X5"] }, + { name: "Mercedes-Benz", models: ["C-Class", "E-Class", "GLC"] }, + { name: "Audi", models: ["A4", "Q5", "A6"] }, + { name: "Tesla", models: ["Model 3", "Model S", "Model X"] }, + { name: "Ferrari", models: ["488 GTB", "F8 Tributo", "Portofino"] }, + { name: "Lamborghini", models: ["Huracán", "Aventador", "Urus"] }, +]; +export const CAR_BRANDS = [ + "Toyota", + "Ford", + "Honda", + "Chevrolet", + "Nissan", + "Volkswagen", + "Hyundai", + "BMW", + "Mercedes-Benz", + "Audi", + "Tesla", + "Ferrari", + "Volvo", + "Porsche", + "Lamborghini", + "Peugeot", + "Renault", + "KIA", + "Lexus", +] as const; + +export function insultCarPrompt(brand: string) { + return [ + `You are a popular standup comic, focused on car jokes`, + //`You are creative and full of sarcasm and wit`, + `Make up a joke or insult about the car brand ${brand} or its' typical owners'.`, + ].join(". "); +} + +export function insultCarImagePrompt(insult: string, brand: string) { + const prompt = [ + `Summarize the following joke about ${brand}: "${insult}"`, + "Caricature art-style", + "stunning shot, beautiful cityscape, people", + //"Cartoon art-style", + //"Humorous, stunning shot, lively", + //"No text, No titles, No quotes", + ]; + return prompt.join(". "); +} diff --git a/src/actions/insults/country-ai.ts b/src/actions/insults/country-ai.ts new file mode 100644 index 0000000..5ab3985 --- /dev/null +++ b/src/actions/insults/country-ai.ts @@ -0,0 +1,60 @@ +import { OpenAI } from "openai"; + +const client = new OpenAI({ + baseURL: "https://api.model.box/v1", + apiKey: import.meta.env.MODEL_BOX_API_KEY, +}); + +export function insultCountryPrompt(country: string) { + return [ + `You are a travel journalist knowledgeable of all countries of the world`, + `You are creative and full of sarcasm and wit`, + `Make up a random fact about ${country}`, + ].join(". "); +} + +export function insultCountryImagePrompt(insult: string, country: string) { + const prompt = [ + `Summarize the following joke about ${country}: "${insult}"`, + "stunning shot, beautiful nature, people", + "Caricature art-style", + "No text, No titles, No quotes", + ]; + return prompt.join(". "); +} + +export async function promptAI(prompt: string): Promise { + const data = await client.chat.completions.create({ + //model: "meta-llama/llama-3.2-3b-instruct", + model: "deepseek/deepseek-chat", + + messages: [ + { + role: "user", + content: prompt, + }, + ], + temperature: 1.5, + max_tokens: 150, + max_completion_tokens: 100, + }); + + const msg = data.choices[0].message?.content; + if (!msg) + throw new Error("No response from AI", { + cause: data, + }); + return msg; +} + +export async function aiImage(prompt: string): Promise { + const data = await client.images.generate({ + prompt, + n: 1, + size: "512x512", + model: "black-forest-labs/flux-schnell", + }); + + const img = data.data[0]; + return img.url; +} diff --git a/src/actions/hello/country-list.ts b/src/actions/insults/country-list.ts similarity index 100% rename from src/actions/hello/country-list.ts rename to src/actions/insults/country-list.ts diff --git a/src/actions/hello/country.ts b/src/actions/insults/country.ts similarity index 100% rename from src/actions/hello/country.ts rename to src/actions/insults/country.ts diff --git a/src/actions/hello/index.ts b/src/actions/insults/index.ts similarity index 61% rename from src/actions/hello/index.ts rename to src/actions/insults/index.ts index 3c3c92b..2257a35 100644 --- a/src/actions/hello/index.ts +++ b/src/actions/insults/index.ts @@ -1,11 +1,17 @@ import { defineAction } from "astro:actions"; import { z } from "astro:schema"; +import { CAR_BRANDS, insultCarImagePrompt, insultCarPrompt } from "./cars"; import { getCountryFromIP, getIPfromHeaders } from "./country"; -import { aiImage, greetingPrompt, imagePrompt, promptAI } from "./ai"; +import { + aiImage, + insultCountryImagePrompt, + insultCountryPrompt, + promptAI, +} from "./country-ai"; import { COUNTRIES } from "./country-list"; -export const hello = { - getGreeting: defineAction({ +export const insults = { + insultMyCountry: defineAction({ input: z.object({ country: z.enum(COUNTRIES), }), @@ -22,10 +28,10 @@ export const hello = { insult: "I couldn't figure out where you're from. So no insult for you.", }; - const insult = await promptAI(greetingPrompt(country)); + const insult = await promptAI(insultCountryPrompt(country)); let img = undefined; try { - img = await aiImage(imagePrompt(insult, country)); + img = await aiImage(insultCountryImagePrompt(insult, country)); } catch (err) { console.error("Image generation failed", err); } @@ -62,4 +68,24 @@ export const hello = { } }, }), + insultMyCar: defineAction({ + input: z.object({ + brand: z.enum(CAR_BRANDS), + }), + accept: "form", + handler: async (input, ctx) => { + const insult = await promptAI(insultCarPrompt(input.brand)); + let img = undefined; + try { + img = await aiImage(insultCarImagePrompt(insult, input.brand)); + } catch (err) { + console.error("Image generation failed", err); + } + + return { + insult, + image: img, + }; + }, + }), }; diff --git a/src/pages/insult-my-car.astro b/src/pages/insult-my-car.astro new file mode 100644 index 0000000..23539b8 --- /dev/null +++ b/src/pages/insult-my-car.astro @@ -0,0 +1,79 @@ +--- +const prerender = false; +import { actions } from "astro:actions"; +import { CAR_BRANDS } from "~/actions/insults/cars"; +import Layout from "~/layouts/Layout.astro"; +import PageContainer from "~/layouts/page-container.astro"; +--- + + + +
+

Insult my car

+

This is a silly parody experiment. Don't take it seriously.

+
+
+ +
+ +
+
+
+ + +
+
+
+ diff --git a/src/pages/insult-my-country.astro b/src/pages/insult-my-country.astro index 859db7f..c398714 100644 --- a/src/pages/insult-my-country.astro +++ b/src/pages/insult-my-country.astro @@ -1,11 +1,11 @@ --- import { actions } from "astro:actions"; -import { COUNTRIES } from "~/actions/hello/country-list"; +import { COUNTRIES } from "~/actions/insults/country-list"; import Layout from "~/layouts/Layout.astro"; import PageContainer from "~/layouts/page-container.astro"; const prerender = false; -const { data } = await Astro.callAction(actions.hello.getCountry, {}); +const { data } = await Astro.callAction(actions.insults.getCountry, {}); const defaultCountry = data ?? undefined; --- @@ -57,7 +57,7 @@ const defaultCountry = data ?? undefined;