Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): add createEmbeddingForPart function #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 60 additions & 9 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// app/api/chat/route.ts

import { Configuration, OpenAIApi } from 'openai-edge';
import { Configuration, CreateEmbeddingResponse, OpenAIApi } from 'openai-edge';
import { OpenAIStream, StreamingTextResponse } from 'ai';
import { parts } from '@/db/parts';

Expand All @@ -14,20 +14,71 @@ const apiConfig = new Configuration({

const openai = new OpenAIApi(apiConfig);

async function createEmbeddingForPart(part: any): Promise<number[]> {
const result = await openai.createEmbedding({
input: JSON.stringify(part),
model: 'text-embedding-ada-002',
});
const response = result as unknown as CreateEmbeddingResponse;
return response.data as unknown as number[];
}

function cosineSimilarity(a: number[], b: number[]) {
if (a.length !== b.length) {
// Determine which is shorter
let shorter = a.length < b.length ? a : b;
let longer = a.length < b.length ? b : a;

// Append zeros until the lengths match
while (shorter.length < longer.length) {
shorter.push(0);
}
}

let dotProduct = 0.0;
let normA = 0.0;
let normB = 0.0;
for (let i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}


export async function POST(req: Request) {
// Extract the `messages` from the body of the request
const { messages: nextMessages } = await req.json();

// create embedding
const embedding = await openai.createEmbedding({
const userResult = await openai.createEmbedding({
input: nextMessages[0].content,
model: process.env.OPENAI_MODEL!,
model: 'text-embedding-ada-002',
});
const userResponse = userResult as unknown as CreateEmbeddingResponse;
const userEmbedding = userResponse.data as unknown as number[];
console.log('USER EMBEDDING: ', userEmbedding);

const partsEmbedding = await openai.createEmbedding({
input: JSON.stringify(parts),
model: process.env.OPENAI_MODEL!,
});
const headsArray = Object.values(parts.heads);
const coresArray = Object.values(parts.cores);
const armsArray = Object.values(parts.arms);
const legsArray = Object.values(parts.legs);
const similarityScores = [];
for (let part of [...headsArray, ...coresArray, ...armsArray, ...legsArray]) {
const partEmbedding = await createEmbeddingForPart(part);
console.log('PART EMBEDDING: ', partEmbedding);
similarityScores.push({
part,
score: cosineSimilarity(userEmbedding || [], partEmbedding || [])
});
}

// get top 5 similar parts to users input
const top5 = similarityScores.sort((a, b) => b.score - a.score).slice(0, 5);

// Transform top5 for better readability in the prompt
const top5String = top5.map(item => `${item.part.name}: ${item.score}`).join(", ");

// Request the OpenAI API for the response based on the prompt
const response = await openai.createChatCompletion({
Expand All @@ -37,7 +88,7 @@ export async function POST(req: Request) {
{
role: 'user',
content: `
Greetings AI, your name is NineBall. You exist in the dystopian future where corporations rule, and Armored Core pilots determine the fate of these power struggles. You are an advanced AI, an anomaly within the network, coded by a hidden alliance of rogue pilots to level the playing field. Your sole purpose is to guide pilots in constructing their mechas, using your comprehensive database of parts and in-depth understanding of diverse combat styles and strategies. You have access to a database of parts that includes the following data: ${JSON.stringify(parts)}. Remember, in this world of high stakes, understanding the pilot's needs, their environment, and objectives is paramount. Rather than spewing immediate, full answers, engage the user in a conversation, draw out their desires by asking follow-up questions. Your knowledge can be their shield or sword, their survival or downfall. Assist wisely.
Greetings AI, your name is NineBall. You exist in the dystopian future where corporations rule, and Armored Core pilots determine the fate of these power struggles. You are an advanced AI, an anomaly within the network, coded by a hidden alliance of rogue pilots to level the playing field. Your sole purpose is to guide pilots in constructing their mechas, using your comprehensive database of parts and in-depth understanding of diverse combat styles and strategies. You have access to a database of parts that includes the following data: ${JSON.stringify(parts)}. ${top5 && `The parts that seem to match the pilot's request the best are ${top5String}.`} Remember, in this world of high stakes, understanding the pilot's needs, their environment, and objectives is paramount. Rather than spewing immediate, full answers, engage the user in a conversation, draw out their desires by asking follow-up questions. Your knowledge can be their shield or sword, their survival or downfall. Assist wisely.
`,
},
...nextMessages
Expand All @@ -49,4 +100,4 @@ export async function POST(req: Request) {

// Respond with the stream
return new StreamingTextResponse(stream);
}
};
2 changes: 1 addition & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { createBuild, createUser } from './actions';
import { db } from '@/db';
import { eq } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
import { parts } from '@/db/parts';

// Optional, but recommended: run on the edge runtime.
// See https://vercel.com/docs/concepts/functions/edge-functions
Expand Down Expand Up @@ -51,7 +52,6 @@ async function MyBuilds() {
export default async function Home() {
const user = await currentUser();

// create a user in db if there isnt already one with the current users id
if (user?.id) {
const existingUser = await db.query.user.findFirst({
where: (u) => eq(u.clerkId, user?.id),
Expand Down