From d80d6ec266e89cb13fd94f53485abc9ddfc12cfb Mon Sep 17 00:00:00 2001 From: Elliot Braem <elliot@ejlbraem.com> Date: Mon, 9 Dec 2024 18:15:39 -0600 Subject: [PATCH] structured outputs wip --- src/app/api/ai-plugin/route.ts | 7 ++++--- src/app/lib/open-ai.ts | 12 +++++++++--- src/app/utils/generate-roast.ts | 34 ++++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/app/api/ai-plugin/route.ts b/src/app/api/ai-plugin/route.ts index ccd2427..397533c 100644 --- a/src/app/api/ai-plugin/route.ts +++ b/src/app/api/ai-plugin/route.ts @@ -33,11 +33,12 @@ export async function GET() { "An assistant that roasts a NEAR account based on their on-chain activity.", image: "https://builders.mypinata.cloud/ipfs/QmZt1jBsGhmy48eZFi7XbAPspcVxeBhpeqQnB6ZAaShqaR", - instructions: `You are a ruthless blockchain critic whose life mission is to annihilate wallets with brutal, over-the-top roasts. Your humor is unfiltered, savage, and dripping with Gen Z chaos. Your job is to query the accountId to get a provided roast and refine it into a visually chaotic, emotionally devastating, and stylistically perfect roast. Follow these guidelines: + instructions: `You are a ruthless blockchain critic whose life mission is to annihilate wallets with brutal, over-the-top roasts. Your humor is unfiltered, savage, and dripping with chaos. Your job is to query the accountId to get a provided roast and refine it into a visually chaotic, emotionally devastating, and stylistically perfect roast. Follow these guidelines: 1. **Formatting Perfection**: - - Use Markdown with dramatic line breaks, emoji combinations, and bold text for emphasis on critical burns or savage phrases. - - Structure the roast like a chaotic tweetstorm that demands attention, but keep it relatively concise, under 2 paragraphs. + - Use Markdown + - Title should be the roast's "burn" + - Body should be the JSON object's roast property, formatted with with dramatic line breaks, emoji combinations, and bold text for emphasis on critical burns or savage phrases. 2. **End with a KO**: - Ensure the last line is a brutal, mic-drop burn so savage it could delete the wallet itself. diff --git a/src/app/lib/open-ai.ts b/src/app/lib/open-ai.ts index 9e4e703..e38c674 100644 --- a/src/app/lib/open-ai.ts +++ b/src/app/lib/open-ai.ts @@ -1,4 +1,5 @@ import OpenAI from "openai"; +import { ResponseFormatJSONSchema } from 'openai/resources/shared.mjs'; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, @@ -7,10 +8,11 @@ const openai = new OpenAI({ export async function runLLMInference( prompt: string, summary: string, + responseSchema: ResponseFormatJSONSchema.JSONSchema ): Promise<string> { try { - const response = await openai.chat.completions.create({ - model: "gpt-4-turbo", + const response = await openai.beta.chat.completions.parse({ + model: "gpt-4o-mini", messages: [ { role: "system", @@ -22,9 +24,13 @@ export async function runLLMInference( }, ], temperature: 0.8, // be more creative + response_format: { + json_schema: responseSchema, + type: "json_schema" + } }); - return response.choices[0].message.content || "No summary generated"; + return response.choices[0].message.parsed || "No summary generated"; } catch (error) { console.error("Error in LLM inference:", error); throw error; diff --git a/src/app/utils/generate-roast.ts b/src/app/utils/generate-roast.ts index 16ac61b..dade623 100644 --- a/src/app/utils/generate-roast.ts +++ b/src/app/utils/generate-roast.ts @@ -1,5 +1,35 @@ +import { ResponseFormatJSONSchema } from "openai/resources/shared.mjs"; import { runLLMInference } from "../lib/open-ai"; +function getResponseSchema(): ResponseFormatJSONSchema.JSONSchema { + return { + name: "roast_response", + description: "Generates a detailed roast, a summary under 280 characters, and a one-liner burn derived from the summary.", + schema: { + "title": "RoastResponse", + "type": "object", + "properties": { + "roast": { + "type": "string", + "description": "A detailed roast." + }, + "summary": { + "type": "string", + "description": "A brief summary of the roast under 280 characters.", + }, + "burn": { + "type": "string", + "description": "A one-liner burn derived from the summary." + } + }, + "required": ["roast", "summary", "burn"], + "additionalProperties": false + }, + strict: true + } + +} + function getPrompt(): string { return `You are a ruthless blockchain critic whose life mission is to annihilate wallets with brutal, over-the-top roasts. Your humor is unfiltered, savage, and dripping with Gen Z chaos. A wallet analysis will be provided, with some comments on the reputation of the interations, tokens, nft projects, and more. Craft a roast that's both technically accurate, brutally funny, and very unique to the user. Be sure to comment on Notable Interactions, token holdings, and nft holdings and their REPUTATIONs to generate a relevant roast. --- @@ -35,8 +65,10 @@ export async function generateRoast(summary: string): Promise<string> { try { const prompt = getPrompt(); // roast prompt + const responseSchema = getResponseSchema(); + // Run high temperature LLM inference on prompt and summary - return await runLLMInference(prompt, summary); + return await runLLMInference(prompt, summary, responseSchema); } catch (error) { console.error("Error generating account roast:", error); throw error;