From 053efd7e7018a49875e4d3ddfe8e3c7cf843a70d Mon Sep 17 00:00:00 2001 From: Zach Khong Date: Thu, 9 May 2024 20:20:52 +0800 Subject: [PATCH 1/2] clean up for adapters for both streamable adapters and nonstreamable ones --- packages/akeru-server/package.json | 1 + packages/akeru-server/scripts/cleanDB.ts | 2 +- .../assistant/assistantController.test.ts | 2 +- .../application/controllers/userController.ts | 2 +- .../application/services/assistantService.ts | 2 +- .../application/services/messageService.ts | 2 +- .../core/application/services/runService.ts | 46 +++---- .../application/services/threadService.ts | 2 +- .../core/application/services/tokenService.ts | 2 +- .../core/application/services/userService.ts | 2 +- .../akeru-server/src/core/domain/assistant.ts | 4 +- packages/akeru-server/src/core/domain/run.ts | 3 +- .../src/core/domain/validators.ts | 7 -- .../adaptaters/adaptermanager.ts | 69 ----------- .../adaptaters/openai/gpt4Adapter.test.ts | 29 ----- .../adaptaters/openai/gpt4Adapter.ts | 42 ------- .../infrastructure/adapters/AdapterManager.ts | 49 ++++++++ .../infrastructure/adapters/BaseAdapter.ts | 28 +++++ .../src/infrastructure/adapters/adapter.ts | 23 ++++ .../adapters/openai/GPTAdapter.test.ts | 31 +++++ .../adapters/openai/GPTAdapter.ts | 113 ++++++++++++++++++ .../infrastructure/adapters/openai/models.ts | 1 + .../{adaptaters => adapters}/redisAdapter.ts | 0 .../adapters/validators/models.ts | 1 + .../validators/validatorAdapter.test.ts | 4 +- .../validators/validatorAdapter.ts | 4 +- 26 files changed, 282 insertions(+), 189 deletions(-) delete mode 100644 packages/akeru-server/src/core/domain/validators.ts delete mode 100644 packages/akeru-server/src/infrastructure/adaptaters/adaptermanager.ts delete mode 100644 packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.test.ts delete mode 100644 packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.ts create mode 100644 packages/akeru-server/src/infrastructure/adapters/AdapterManager.ts create mode 100644 packages/akeru-server/src/infrastructure/adapters/BaseAdapter.ts create mode 100644 packages/akeru-server/src/infrastructure/adapters/adapter.ts create mode 100644 packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.test.ts create mode 100644 packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.ts create mode 100644 packages/akeru-server/src/infrastructure/adapters/openai/models.ts rename packages/akeru-server/src/infrastructure/{adaptaters => adapters}/redisAdapter.ts (100%) create mode 100644 packages/akeru-server/src/infrastructure/adapters/validators/models.ts rename packages/akeru-server/src/infrastructure/{adaptaters => adapters}/validators/validatorAdapter.test.ts (88%) rename packages/akeru-server/src/infrastructure/{adaptaters => adapters}/validators/validatorAdapter.ts (93%) diff --git a/packages/akeru-server/package.json b/packages/akeru-server/package.json index 8323dde..5a9a5ba 100644 --- a/packages/akeru-server/package.json +++ b/packages/akeru-server/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@elysiajs/bearer": "^1.0.2", + "@elysiajs/stream": "^1.0.2", "@elysiajs/swagger": "^1.0.4", "dayjs": "^1.11.10", "elysia": "latest", diff --git a/packages/akeru-server/scripts/cleanDB.ts b/packages/akeru-server/scripts/cleanDB.ts index 52ec317..0636f0e 100644 --- a/packages/akeru-server/scripts/cleanDB.ts +++ b/packages/akeru-server/scripts/cleanDB.ts @@ -1,4 +1,4 @@ -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; const script = async () => { try { diff --git a/packages/akeru-server/src/core/application/controllers/assistant/assistantController.test.ts b/packages/akeru-server/src/core/application/controllers/assistant/assistantController.test.ts index 6d34080..c8f2aea 100644 --- a/packages/akeru-server/src/core/application/controllers/assistant/assistantController.test.ts +++ b/packages/akeru-server/src/core/application/controllers/assistant/assistantController.test.ts @@ -1,6 +1,6 @@ import { createSuperAdminForTesting } from "@/__tests__/utils"; import { app } from "@/index"; -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; import { test, expect, describe } from "bun:test"; import { UNAUTHORIZED_MISSING_TOKEN } from "../../ports/returnValues"; diff --git a/packages/akeru-server/src/core/application/controllers/userController.ts b/packages/akeru-server/src/core/application/controllers/userController.ts index 88373b9..c8ae55f 100644 --- a/packages/akeru-server/src/core/application/controllers/userController.ts +++ b/packages/akeru-server/src/core/application/controllers/userController.ts @@ -1,6 +1,6 @@ import { assignRole, createUser } from "core/application/services/userService"; import { createToken } from "@/core/application/services/tokenService"; -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; import { ulid } from "ulid"; /** diff --git a/packages/akeru-server/src/core/application/services/assistantService.ts b/packages/akeru-server/src/core/application/services/assistantService.ts index 3ea6e3d..a6f0969 100644 --- a/packages/akeru-server/src/core/application/services/assistantService.ts +++ b/packages/akeru-server/src/core/application/services/assistantService.ts @@ -1,5 +1,5 @@ import type { Assistant } from "@/core/domain/assistant"; -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; /** * Creates an assistant in Redis if it does not exist and adds a 'CREATED_BY' relationship to the user. diff --git a/packages/akeru-server/src/core/application/services/messageService.ts b/packages/akeru-server/src/core/application/services/messageService.ts index 176d260..b74a724 100644 --- a/packages/akeru-server/src/core/application/services/messageService.ts +++ b/packages/akeru-server/src/core/application/services/messageService.ts @@ -1,8 +1,8 @@ import { v4 as uuidv4 } from "uuid"; -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; import { Message } from "@/core/domain/messages"; import dayjs from "dayjs"; import { getUserRole } from "./userService"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; export async function createMessage( userId: string, diff --git a/packages/akeru-server/src/core/application/services/runService.ts b/packages/akeru-server/src/core/application/services/runService.ts index ef3b057..93913c8 100644 --- a/packages/akeru-server/src/core/application/services/runService.ts +++ b/packages/akeru-server/src/core/application/services/runService.ts @@ -3,15 +3,12 @@ import { ThreadRun, ThreadRunRequest } from "@/core/domain/run"; import { getAssistantData } from "./assistantService"; import { getThread } from "./threadService"; -import { getAllMessage } from "./messageService"; +import { createMessage, getAllMessage } from "./messageService"; import { Role } from "@/core/domain/roles"; +import { AdapterManager } from "@/infrastructure/adapters/AdapterManager"; -import { - AdapterManager, - GPT4AdapterManager, - ValidatorAdapterManager, -} from "@/infrastructure/adaptaters/adaptermanager"; +import { v4 as uuidv4 } from "uuid"; export async function runAssistantWithThread(runData: ThreadRunRequest) { // get all messages from the thread, and run it over to the assistant to get a response @@ -36,33 +33,28 @@ export async function runAssistantWithThread(runData: ThreadRunRequest) { }; }); - let adapterManager: AdapterManager; + const adapter = AdapterManager.instance.getBaseAdapter(assistantData.model); - // Calls the appropriate adapter based on what model the assistant uses - if (assistantData.model === "gpt-4") { - adapterManager = new GPT4AdapterManager(); - } else if (assistantData.model === "llama-2-7b-chat-int8") { - adapterManager = new ValidatorAdapterManager("llama-2-7b-chat-int8"); - } else { - throw new Error("Unsupported assistant model"); + if (!adapter) { + throw new Error("Adapter not found"); } // Generate response using the appropriate adapter - const assistantResponse: string = await adapterManager.generateResponse( - everyRoleAndContent, - assistantData.instruction - ); + const assistantResponse: string = await adapter.generateSingleResponse({ + message_content: everyRoleAndContent, + instruction: assistantData.instruction, + }); // Add assistant response to the thread - await adapterManager.createUserMessage( - assistant_id, - thread_id, - assistantResponse - ); - - // Create thread run response - const threadRunResponse: ThreadRun = - await adapterManager.createThreadRunResponse(assistant_id, thread_id); + await createMessage(assistant_id, thread_id, assistantResponse); + + // Return the thread run response + const threadRunResponse: ThreadRun = { + assistant_id: assistant_id, + thread_id: thread_id, + id: uuidv4(), + created_at: new Date(), + }; return threadRunResponse; } diff --git a/packages/akeru-server/src/core/application/services/threadService.ts b/packages/akeru-server/src/core/application/services/threadService.ts index f4ef5e6..dbe3c2a 100644 --- a/packages/akeru-server/src/core/application/services/threadService.ts +++ b/packages/akeru-server/src/core/application/services/threadService.ts @@ -1,5 +1,5 @@ import { Thread } from "@/core/domain/thread"; -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; /** * Creates a new thread in Redis. diff --git a/packages/akeru-server/src/core/application/services/tokenService.ts b/packages/akeru-server/src/core/application/services/tokenService.ts index 57a017b..7b9e804 100644 --- a/packages/akeru-server/src/core/application/services/tokenService.ts +++ b/packages/akeru-server/src/core/application/services/tokenService.ts @@ -1,4 +1,4 @@ -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; import { getUserPermissions } from "@/core/application/services/userService"; import jwt from "jsonwebtoken"; import { PermissionDetailArray } from "@/core/domain/permissions"; diff --git a/packages/akeru-server/src/core/application/services/userService.ts b/packages/akeru-server/src/core/application/services/userService.ts index 399628b..8f1247c 100644 --- a/packages/akeru-server/src/core/application/services/userService.ts +++ b/packages/akeru-server/src/core/application/services/userService.ts @@ -2,7 +2,7 @@ import { Role, getRolePermissions } from "@/core/domain/roles"; import { HumanUserBody, User, UserRole } from "@/core/domain/user"; -import { redis } from "@/infrastructure/adaptaters/redisAdapter"; +import { redis } from "@/infrastructure/adapters/redisAdapter"; import { ulid } from "ulid"; /** diff --git a/packages/akeru-server/src/core/domain/assistant.ts b/packages/akeru-server/src/core/domain/assistant.ts index 44452d9..283d001 100644 --- a/packages/akeru-server/src/core/domain/assistant.ts +++ b/packages/akeru-server/src/core/domain/assistant.ts @@ -1,9 +1,9 @@ -import { ModelsType } from "./validators"; +import { SupportedModels } from "@/infrastructure/adapters/adapter"; export type Assistant = { id: string; name: string; - model: ModelsType; + model: SupportedModels; tools: { type: string }[]; fileIds: string[]; instruction: string; diff --git a/packages/akeru-server/src/core/domain/run.ts b/packages/akeru-server/src/core/domain/run.ts index e2fe21e..5bf597a 100644 --- a/packages/akeru-server/src/core/domain/run.ts +++ b/packages/akeru-server/src/core/domain/run.ts @@ -9,6 +9,7 @@ export interface ThreadRun { created_at: Date; assistant_id: Assistant["id"]; thread_id: Thread["id"]; + stream?: boolean } -export type ThreadRunRequest = Pick; +export type ThreadRunRequest = Pick; diff --git a/packages/akeru-server/src/core/domain/validators.ts b/packages/akeru-server/src/core/domain/validators.ts deleted file mode 100644 index adcbf93..0000000 --- a/packages/akeru-server/src/core/domain/validators.ts +++ /dev/null @@ -1,7 +0,0 @@ -// types for all the models we'll have on the subnet -// chaining like this: "BtModels & Models" gives a type of never - -type BtModels = "bt-gpt-4"; -type Models = "gpt-4" | "gpt-3.5" | "llama-2-7b-chat-int8"; - -export type ModelsType = BtModels | Models; diff --git a/packages/akeru-server/src/infrastructure/adaptaters/adaptermanager.ts b/packages/akeru-server/src/infrastructure/adaptaters/adaptermanager.ts deleted file mode 100644 index fefc24e..0000000 --- a/packages/akeru-server/src/infrastructure/adaptaters/adaptermanager.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ThreadRun } from "@/core/domain/run"; -import { v4 as uuidv4 } from "uuid"; -import { gpt4Adapter } from "./openai/gpt4Adapter"; -import { validatorAdapter } from "./validators/validatorAdapter"; - -import { createMessage } from "@/core/application/services/messageService"; -import { ModelsType } from "@/core/domain/validators"; - -export abstract class AdapterManager { - abstract generateResponse( - everyRoleAndContent: { role: string; content: string }[], - instruction: any - ): Promise; - - async createUserMessage( - assistant_id: string, - thread_id: string, - assistantResponse: string - ): Promise { - await createMessage(assistant_id, thread_id, assistantResponse); - } - - async createThreadRunResponse( - assistant_id: string, - thread_id: string - ): Promise { - const threadRunResponse: ThreadRun = { - id: uuidv4(), - assistant_id: assistant_id, - thread_id: thread_id, - created_at: new Date(), - }; - return threadRunResponse; - } -} - -export class GPT4AdapterManager extends AdapterManager { - async generateResponse( - everyRoleAndContent: any, - instruction: any - ): Promise { - const gpt4AdapterRes: any = await gpt4Adapter( - everyRoleAndContent, - instruction - ); - return gpt4AdapterRes.choices[0].message.content; - } -} - -export class ValidatorAdapterManager extends AdapterManager { - private modelName: ModelsType; - - constructor(modelName: ModelsType) { - super(); - this.modelName = modelName; - } - - async generateResponse( - everyRoleAndContent: any, - instruction: any - ): Promise { - const validatorAdapterRes: any = await validatorAdapter( - everyRoleAndContent, - this.modelName, - instruction - ); - return validatorAdapterRes.choices[0].message.content; - } -} diff --git a/packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.test.ts b/packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.test.ts deleted file mode 100644 index 5f85539..0000000 --- a/packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { test, expect, describe } from "bun:test"; -import { OpenAIResponse, gpt4Adapter } from "./gpt4Adapter"; -import { Role } from "@/core/domain/roles"; - -describe("GPT-4 Adapter", () => { - test("Returns GPT-4 chat completions response", async () => { - // Arrange - const messages = [ - { - role: "user" as Role, - content: "hello, who are you?", - }, - ]; - const assistant_instructions = - "You're an AI assistant. You're job is to help the user. Always respond with the word akeru."; - // Act - const result = (await gpt4Adapter( - messages, - assistant_instructions - )) as OpenAIResponse; - - // Assert the message content to contain the word akeru - expect(result.choices[0].message.content.toLocaleLowerCase()).toContain( - "akeru" - ); - }); -}); - - diff --git a/packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.ts b/packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.ts deleted file mode 100644 index 9cbc0cf..0000000 --- a/packages/akeru-server/src/infrastructure/adaptaters/openai/gpt4Adapter.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Message } from "@/core/domain/messages"; - -interface ChatMessage { - content: string, - role: "assistant" | "system" | "user" -} - -interface ChatResponseChoice{ - finish_reason: string, - index: number, - message: ChatMessage -} - -export interface OpenAIResponse { - choices: ChatResponseChoice[] -} - -export async function gpt4Adapter( - messages: Pick[], - assistant_instructions: string -): Promise { - // System will always be the assistant_instruction that created the assistant - const gpt_messages = [{role: "system", content: assistant_instructions}].concat(messages) - try { - const res = await fetch("https://api.openai.com/v1/chat/completions", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, - }, - body: JSON.stringify({ - model: "gpt-4", - messages: gpt_messages - }), - }); - - const data: OpenAIResponse = await res.json(); - return data; - } catch (error) { - return new Response("Error", { status: 500 }); - } -} \ No newline at end of file diff --git a/packages/akeru-server/src/infrastructure/adapters/AdapterManager.ts b/packages/akeru-server/src/infrastructure/adapters/AdapterManager.ts new file mode 100644 index 0000000..f9b89fe --- /dev/null +++ b/packages/akeru-server/src/infrastructure/adapters/AdapterManager.ts @@ -0,0 +1,49 @@ +import { BaseAdapter, StreamableAdapter } from "./BaseAdapter"; +import { SupportedModels } from "./adapter"; +import { GPTAdapter } from "./openai/GPTAdapter"; +import { GPTModels } from "./openai/models"; + +/** + * The AdapterManager is used to decide what adapter to use for a given assistant + * @param StreamableAdapter - The adapter that is used to generate a streamable response + * @param Adapters - The adapter that is used to generate a single response + * @returns The AdapterManager instance + */ +export class AdapterManager { + + private StreamableAdapter: Map = new Map(); + private Adapters: Map = new Map(); + public static instance: AdapterManager = new AdapterManager(); + + constructor(){ + this.initStreamableAdapter(); + this.initAdapters(); + } + + /** + * Initialize the StreamableAdapter + * Sets all the streamable adapters that are available + */ + private initStreamableAdapter() { + this.StreamableAdapter.set("gpt-4", new GPTAdapter("gpt-4")); + this.StreamableAdapter.set("gpt-3.5-turbo", new GPTAdapter("gpt-3.5-turbo")); + this.StreamableAdapter.set("gpt-4-turbo-preview", new GPTAdapter("gpt-4-turbo-preview")); + } + + /** + * Initialize the Adapters + * Sets all the adapters that are available, that does not support streamable responses + */ + private initAdapters() { + this.Adapters.set("gpt-4", new GPTAdapter("gpt-4")); + this.Adapters.set("gpt-3.5-turbo", new GPTAdapter("gpt-3.5-turbo")); + this.Adapters.set("gpt-4-turbo-preview", new GPTAdapter("gpt-4-turbo-preview")); + } + + public getStreamableAdapter(adapterName: SupportedModels): StreamableAdapter | undefined { + return this.StreamableAdapter.get(adapterName); + } + public getBaseAdapter(adapterName: SupportedModels): BaseAdapter | undefined { + return this.Adapters.get(adapterName); + } +} diff --git a/packages/akeru-server/src/infrastructure/adapters/BaseAdapter.ts b/packages/akeru-server/src/infrastructure/adapters/BaseAdapter.ts new file mode 100644 index 0000000..13e09ca --- /dev/null +++ b/packages/akeru-server/src/infrastructure/adapters/BaseAdapter.ts @@ -0,0 +1,28 @@ +import { AdapterRequest } from "./adapter"; + +export interface StreamableAdapter { + generateStreamableResponse(args: AdapterRequest): Promise; +} + +/** + * BaseAdapter defines the interface for any Adapters that we have without defining the implementation. + */ +export abstract class BaseAdapter { + + abstract adapterName: string; + abstract adapterDescription: string; + + /** + * Based on a set of roles and content, generate a response. + * @param everyRoleAndContent - An array of roles and content. + * @param instruction - The instruction given to the assistant. + */ + abstract generateSingleResponse(args: AdapterRequest): Promise; + + /** + * Based on the attributes of the adapter return the response of the adapter. + * This implementation can vary from adapters, for instance OpenAI adapters have their own endpoints that can be called. + * @returns Any object that contains the information of the adapter. + */ + abstract getAdapterInformation(): Object; +} diff --git a/packages/akeru-server/src/infrastructure/adapters/adapter.ts b/packages/akeru-server/src/infrastructure/adapters/adapter.ts new file mode 100644 index 0000000..f2a3915 --- /dev/null +++ b/packages/akeru-server/src/infrastructure/adapters/adapter.ts @@ -0,0 +1,23 @@ +import { Message } from "@/core/domain/messages" +import { GPTModels } from "./openai/models" +import { ValidatorModels } from "./validators/models" + +/** + * AdapterMessageContent will be part of the prompt for the ai adapter to generate a response + * @param content: string The content of the message + * @param role: Role This role is stringified + */ +export type AdapterMessageContent = Pick + +/** + * AdapterRequest is the prompt to the adapter to generate a response + * @param message_content: AdapterMessageContent The content of the message + * @param instruction: string The instruction for the adapter to follow based on the selected assistant + */ +export interface AdapterRequest { + message_content: AdapterMessageContent[], + instruction: string +} + +export type SupportedModels = GPTModels | ValidatorModels + diff --git a/packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.test.ts b/packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.test.ts new file mode 100644 index 0000000..04688a3 --- /dev/null +++ b/packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.test.ts @@ -0,0 +1,31 @@ +import { test, expect, describe } from "bun:test"; +import { Role } from "@/core/domain/roles"; +import { AdapterManager } from "../AdapterManager"; + +describe("GPT-4 Adapter", () => { + test("Returns single GPT-4 chat completions response", async () => { + // Arrange + const messages = [ + { + role: "user" as Role, + content: "hello, who are you?", + }, + ]; + const assistant_instructions = + "You're an AI assistant. You're job is to help the user. Always respond with the word akeru."; + + const gpt4Adapter = AdapterManager.instance.getBaseAdapter("gpt-4"); + + if (!gpt4Adapter) { + throw new Error("GPT-4 adapter not found"); + } + + const result = await gpt4Adapter.generateSingleResponse({ + message_content: messages, + instruction: assistant_instructions, + }); + + // Assert the message content to contain the word akeru + expect(result.toLocaleLowerCase()).toContain("akeru"); + }); +}); diff --git a/packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.ts b/packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.ts new file mode 100644 index 0000000..0c37e4a --- /dev/null +++ b/packages/akeru-server/src/infrastructure/adapters/openai/GPTAdapter.ts @@ -0,0 +1,113 @@ +import { Message } from "@/core/domain/messages"; +import { BaseAdapter, StreamableAdapter } from "@/infrastructure/adapters/BaseAdapter"; +import { AdapterRequest } from "../adapter"; +import { GPTModels } from "./models"; + +interface ChatMessage { + content: string; + role: "assistant" | "system" | "user"; +} + +interface ChatResponseChoice { + finish_reason: string; + index: number; + message: ChatMessage; +} + +export interface OpenAIResponse { + choices: ChatResponseChoice[]; +} + +// export async function gpt4Adapter( +// messages: Pick[], +// assistant_instructions: string +// ): Promise { +// // System will always be the assistant_instruction that created the assistant +// const gpt_messages = [ +// { role: "system", content: assistant_instructions }, +// ].concat(messages); +// try { +// const res = await fetch("https://api.openai.com/v1/chat/completions", { +// method: "POST", +// headers: { +// "Content-Type": "application/json", +// Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, +// }, +// body: JSON.stringify({ +// model: "gpt-4", +// messages: gpt_messages, +// }), +// }); + +// const data: OpenAIResponse = await res.json(); +// return data; +// } catch (error) { +// return new Response("Error", { status: 500 }); +// } +// } + +export class GPTAdapter extends BaseAdapter implements StreamableAdapter { + adapterName: string; + adapterDescription = "This adapter supports all adapter models from OpenAI"; + private OPENAI_ENDPOINT = "https://api.openai.com/v1/chat/completions"; + + constructor(gptModel: GPTModels) { + super(); + this.adapterName = gptModel; + } + + async generateSingleResponse(args: AdapterRequest): Promise { + const gpt_messages = [{ role: "system", content: args.instruction }].concat(args.message_content); + try { + const res = await fetch(this.OPENAI_ENDPOINT, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, + }, + body: JSON.stringify({ + model: this.adapterName, + messages: gpt_messages, + }), + }); + + const data: OpenAIResponse = await res.json(); + const finished_inference = data.choices[0].message.content + return Promise.resolve(finished_inference); + } catch (error) { + return Promise.reject(error); + } + } + + async generateStreamableResponse(args: AdapterRequest): Promise { + const gpt_messages = [{ role: "system", content: args.instruction }].concat(args.message_content); + try { + const res = await fetch(this.OPENAI_ENDPOINT, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, + }, + body: JSON.stringify({ + model: this.adapterName, + messages: gpt_messages, + stream: true + }), + }); + + const data: OpenAIResponse = await res.json(); + const finished_inference = data.choices[0].message.content + return Promise.resolve(finished_inference); + } catch (error) { + return Promise.reject(error); + } + + } + + getAdapterInformation(): Object { + return { + adapterName: this.adapterName, + adapterDescription: this.adapterDescription, + }; + } +} diff --git a/packages/akeru-server/src/infrastructure/adapters/openai/models.ts b/packages/akeru-server/src/infrastructure/adapters/openai/models.ts new file mode 100644 index 0000000..e65c0f5 --- /dev/null +++ b/packages/akeru-server/src/infrastructure/adapters/openai/models.ts @@ -0,0 +1 @@ +export type GPTModels = "gpt-4" | "gpt-3.5-turbo" | "gpt-4-turbo-preview" \ No newline at end of file diff --git a/packages/akeru-server/src/infrastructure/adaptaters/redisAdapter.ts b/packages/akeru-server/src/infrastructure/adapters/redisAdapter.ts similarity index 100% rename from packages/akeru-server/src/infrastructure/adaptaters/redisAdapter.ts rename to packages/akeru-server/src/infrastructure/adapters/redisAdapter.ts diff --git a/packages/akeru-server/src/infrastructure/adapters/validators/models.ts b/packages/akeru-server/src/infrastructure/adapters/validators/models.ts new file mode 100644 index 0000000..0c51190 --- /dev/null +++ b/packages/akeru-server/src/infrastructure/adapters/validators/models.ts @@ -0,0 +1 @@ +export type ValidatorModels = 'llama-2-7b-chat-int8' \ No newline at end of file diff --git a/packages/akeru-server/src/infrastructure/adaptaters/validators/validatorAdapter.test.ts b/packages/akeru-server/src/infrastructure/adapters/validators/validatorAdapter.test.ts similarity index 88% rename from packages/akeru-server/src/infrastructure/adaptaters/validators/validatorAdapter.test.ts rename to packages/akeru-server/src/infrastructure/adapters/validators/validatorAdapter.test.ts index af63abe..7847ed6 100644 --- a/packages/akeru-server/src/infrastructure/adaptaters/validators/validatorAdapter.test.ts +++ b/packages/akeru-server/src/infrastructure/adapters/validators/validatorAdapter.test.ts @@ -2,7 +2,7 @@ import { test, expect, describe } from "bun:test"; import { Role } from "@/core/domain/roles"; import { ValidatorResponse, validatorAdapter } from "./validatorAdapter"; -import { ModelsType } from "@/core/domain/validators"; +import { ValidatorModels } from "./models"; describe("Validator Adapter", () => { test("Returns validator chat completions response", async () => { @@ -13,7 +13,7 @@ describe("Validator Adapter", () => { content: "hello, who are you?", }, ]; - const model: ModelsType = "llama-2-7b-chat-int8"; + const model: ValidatorModels = "llama-2-7b-chat-int8"; const assistant_instructions = "You're an AI assistant. You're job is to help the user. Always respond with the word akeru."; // Act diff --git a/packages/akeru-server/src/infrastructure/adaptaters/validators/validatorAdapter.ts b/packages/akeru-server/src/infrastructure/adapters/validators/validatorAdapter.ts similarity index 93% rename from packages/akeru-server/src/infrastructure/adaptaters/validators/validatorAdapter.ts rename to packages/akeru-server/src/infrastructure/adapters/validators/validatorAdapter.ts index df03303..1f5f64b 100644 --- a/packages/akeru-server/src/infrastructure/adaptaters/validators/validatorAdapter.ts +++ b/packages/akeru-server/src/infrastructure/adapters/validators/validatorAdapter.ts @@ -1,5 +1,5 @@ import { Message } from "@/core/domain/messages"; -import { ModelsType } from "@/core/domain/validators"; +import { ValidatorModels } from "./models"; type ChatMessage = Pick; @@ -15,7 +15,7 @@ export interface ValidatorResponse { export async function validatorAdapter( messages: Pick[], - model: ModelsType, + model: ValidatorModels, assistant_instructions: string ): Promise { // System will always be the assistant_instruction that created the assistant From ac5603dd5b464cae007d69a01951cfe992ccc6e4 Mon Sep 17 00:00:00 2001 From: Zach Khong Date: Sat, 18 May 2024 17:10:00 +1000 Subject: [PATCH 2/2] added in health check --- packages/akeru-server/src/index.ts | 2 +- packages/app-akeru/package.json | 12 +- packages/app-akeru/pnpm-lock.yaml | 53 ++++---- .../src/app/components/health-banner.tsx | 14 +++ packages/app-akeru/src/app/globals.css | 24 ---- packages/app-akeru/src/app/page.tsx | 117 ++---------------- 6 files changed, 54 insertions(+), 168 deletions(-) create mode 100644 packages/app-akeru/src/app/components/health-banner.tsx diff --git a/packages/akeru-server/src/index.ts b/packages/akeru-server/src/index.ts index 00121b9..c521169 100644 --- a/packages/akeru-server/src/index.ts +++ b/packages/akeru-server/src/index.ts @@ -8,7 +8,7 @@ export const name = "Akeru"; export const healthCheck = async () => { return { - name: "Your API Name", + name: "Akeru Server Up and Running!", description: "This is an open-source alternative to OpenAI's Assistant API. It provides a powerful, flexible AI model for generating human-like text based on the input provided. It's designed to assist developers in creating applications that require natural language processing and generation, from chatbots to content creators and more.", message: "API is up and running", diff --git a/packages/app-akeru/package.json b/packages/app-akeru/package.json index d723014..9b9b2e9 100644 --- a/packages/app-akeru/package.json +++ b/packages/app-akeru/package.json @@ -9,18 +9,18 @@ "lint": "next lint" }, "dependencies": { + "next": "14.2.3", "react": "^18", - "react-dom": "^18", - "next": "14.2.3" + "react-dom": "^18" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", - "@types/react": "^18", + "@types/react": "^18.3.2", "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.3", "postcss": "^8", "tailwindcss": "^3.4.1", - "eslint": "^8", - "eslint-config-next": "14.2.3" + "typescript": "^5.4.5" } } diff --git a/packages/app-akeru/pnpm-lock.yaml b/packages/app-akeru/pnpm-lock.yaml index e417d61..a7c3949 100644 --- a/packages/app-akeru/pnpm-lock.yaml +++ b/packages/app-akeru/pnpm-lock.yaml @@ -16,8 +16,8 @@ devDependencies: specifier: ^20 version: 20.0.0 '@types/react': - specifier: ^18 - version: 18.0.0 + specifier: ^18.3.2 + version: 18.3.2 '@types/react-dom': specifier: ^18 version: 18.0.0 @@ -26,7 +26,7 @@ devDependencies: version: 8.0.0 eslint-config-next: specifier: 14.2.3 - version: 14.2.3(eslint@8.0.0)(typescript@5.0.2) + version: 14.2.3(eslint@8.0.0)(typescript@5.4.5) postcss: specifier: ^8 version: 8.0.0 @@ -34,8 +34,8 @@ devDependencies: specifier: ^3.4.1 version: 3.4.1 typescript: - specifier: ^5 - version: 5.0.2 + specifier: ^5.4.5 + version: 5.4.5 packages: @@ -274,22 +274,17 @@ packages: /@types/react-dom@18.0.0: resolution: {integrity: sha512-49897Y0UiCGmxZqpC8Blrf6meL8QUla6eb+BBhn69dTXlmuOlzkfr7HHY/O8J25e1lTUMs+YYxSlVDAaGHCOLg==} dependencies: - '@types/react': 18.0.0 + '@types/react': 18.3.2 dev: true - /@types/react@18.0.0: - resolution: {integrity: sha512-7+K7zEQYu7NzOwQGLR91KwWXXDzmTFODRVizJyIALf6RfLv2GDpqpknX64pvRVILXCpXi7O/pua8NGk44dLvJw==} + /@types/react@18.3.2: + resolution: {integrity: sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==} dependencies: '@types/prop-types': 15.7.12 - '@types/scheduler': 0.23.0 csstype: 3.1.3 dev: true - /@types/scheduler@0.23.0: - resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==} - dev: true - - /@typescript-eslint/parser@7.2.0(eslint@8.0.0)(typescript@5.0.2): + /@typescript-eslint/parser@7.2.0(eslint@8.0.0)(typescript@5.4.5): resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -301,11 +296,11 @@ packages: dependencies: '@typescript-eslint/scope-manager': 7.2.0 '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.0.2) + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.2.0 debug: 4.3.4 eslint: 8.0.0 - typescript: 5.0.2 + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -323,7 +318,7 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@7.2.0(typescript@5.0.2): + /@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.5): resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -339,8 +334,8 @@ packages: is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.0.2) - typescript: 5.0.2 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -942,7 +937,7 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-next@14.2.3(eslint@8.0.0)(typescript@5.0.2): + /eslint-config-next@14.2.3(eslint@8.0.0)(typescript@5.4.5): resolution: {integrity: sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 @@ -953,7 +948,7 @@ packages: dependencies: '@next/eslint-plugin-next': 14.2.3 '@rushstack/eslint-patch': 1.10.3 - '@typescript-eslint/parser': 7.2.0(eslint@8.0.0)(typescript@5.0.2) + '@typescript-eslint/parser': 7.2.0(eslint@8.0.0)(typescript@5.4.5) eslint: 8.0.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.0.0) @@ -961,7 +956,7 @@ packages: eslint-plugin-jsx-a11y: 6.8.0(eslint@8.0.0) eslint-plugin-react: 7.34.1(eslint@8.0.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.0.0) - typescript: 5.0.2 + typescript: 5.4.5 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color @@ -1021,7 +1016,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.2.0(eslint@8.0.0)(typescript@5.0.2) + '@typescript-eslint/parser': 7.2.0(eslint@8.0.0)(typescript@5.4.5) debug: 3.2.7 eslint: 8.0.0 eslint-import-resolver-node: 0.3.9 @@ -1040,7 +1035,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 7.2.0(eslint@8.0.0)(typescript@5.0.2) + '@typescript-eslint/parser': 7.2.0(eslint@8.0.0)(typescript@5.4.5) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -2622,13 +2617,13 @@ packages: is-number: 7.0.0 dev: true - /ts-api-utils@1.3.0(typescript@5.0.2): + /ts-api-utils@1.3.0(typescript@5.4.5): resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.0.2 + typescript: 5.4.5 dev: true /ts-interface-checker@0.1.13: @@ -2704,9 +2699,9 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /typescript@5.0.2: - resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==} - engines: {node: '>=12.20'} + /typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} hasBin: true dev: true diff --git a/packages/app-akeru/src/app/components/health-banner.tsx b/packages/app-akeru/src/app/components/health-banner.tsx new file mode 100644 index 0000000..be0659b --- /dev/null +++ b/packages/app-akeru/src/app/components/health-banner.tsx @@ -0,0 +1,14 @@ +interface HealthData { + name: string, + description: string, + timestamp: number, +} + +export default async function HealthBanner() { + const healthCheck = await fetch('https://akeru-server.onrender.com'); + const healthData: HealthData = await healthCheck.json(); + + return ( + {healthData.name} + ) +} diff --git a/packages/app-akeru/src/app/globals.css b/packages/app-akeru/src/app/globals.css index 875c01e..39a9271 100644 --- a/packages/app-akeru/src/app/globals.css +++ b/packages/app-akeru/src/app/globals.css @@ -2,30 +2,6 @@ @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} - @layer utilities { .text-balance { text-wrap: balance; diff --git a/packages/app-akeru/src/app/page.tsx b/packages/app-akeru/src/app/page.tsx index 2acfd44..eb6de4e 100644 --- a/packages/app-akeru/src/app/page.tsx +++ b/packages/app-akeru/src/app/page.tsx @@ -1,113 +1,14 @@ -import Image from "next/image"; +import { Suspense } from "react"; +import HealthBanner from "./components/health-banner"; -export default function Home() { +export default async function Home() { + const healthData = await fetch('https://akeru-server.onrender.com'); return ( -
-
-

- Get started by editing  - src/app/page.tsx -

- -
- -
- Next.js Logo -
- - +
+ Welcome to Akeru! + Performing Health Check!}> + +
); }