diff --git a/README.md b/README.md
index 21009c6..a5bf678 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,13 @@
AI Agent Contract Template with OpenAI
-
-
-
-
-
-
+
+
+
+
+
Host your AI Agent Contract on Phala's decentralized serverless cloud.
Explore the docs ยป
diff --git a/package.json b/package.json
index b65f2e1..506f2dc 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,9 @@
"node": ">=18"
},
"scripts": {
- "build": "phat-fn build --experimentalAsync",
- "test": "tsx tests/test.ts",
+ "build": "tsup --config tsup.config.ts",
+ "test": "vitest",
+ "dev": "tsx watch src/index.ts",
"set-secrets": "tsx scripts/setSecrets.ts",
"lint": "tsc --noEmit",
"publish-agent": "phat-fn build --experimentalAsync && tsx scripts/publish.ts"
@@ -21,9 +22,13 @@
"thirdweb": "^5.32.3",
"tsx": "^4.7.1",
"typescript": "^5.3.3",
+ "vitest": "^2.1.5",
"wyhash": "^1.0.0"
},
"dependencies": {
- "openai": "^4.56.0"
+ "@hono/node-server": "^1.13.7",
+ "hono": "^4.6.11",
+ "openai": "^4.56.0",
+ "tsup": "^8.3.5"
}
}
diff --git a/src/httpSupport.ts b/src/httpSupport.ts
deleted file mode 100644
index d4fc048..0000000
--- a/src/httpSupport.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-// DO NOT MODIFY THIS INTERFACE
-export interface SerializedRequest {
- method: 'GET' | 'POST' | 'PATCH' | 'PUT';
- path: string;
- queries: Record;
- headers: Record;
- body?: string;
- secret?: Record;
-}
-
-// DO NOT MODIFY THIS CLASS
-export class Request implements SerializedRequest {
- method: 'GET' | 'POST' | 'PATCH' | 'PUT';
- path: string;
- queries: Record;
- headers: Record;
- body?: string;
- secret?: Record;
- constructor(raw: SerializedRequest) {
- this.body = raw.body;
- this.queries = raw.queries;
- this.headers = raw.headers;
- this.method = raw.method;
- this.path = raw.path;
- this.secret = raw.secret;
- }
- async json(): Promise {
- return JSON.parse(this.body!)
- }
-}
-
-type ResponseOption = {
- status?: number,
- headers?: Record
-}
-export class Response {
- status: number;
- body?: string;
- headers: Record;
- constructor(body: string, options?: ResponseOption) {
- this.status = options?.status ?? 200;
- this.body = body;
- this.headers = {
- // To change the response type, change the Content-Type header
- 'Content-Type': 'application/json',
- 'Access-Control-Allow-Origin': '*',
- ...options?.headers
- }
- }
-}
-
-export type RouteConfig = {
- GET?: (req: Request) => Promise,
- POST?: (req: Request) => Promise,
- PATCH?: (req: Request) => Promise,
- PUT?: (req: Request) => Promise,
-}
-
-export async function route(config: RouteConfig, request: string) {
- const reqObj: SerializedRequest = JSON.parse(request)
- let response: Response;
- const method = reqObj.method
- const req = new Request(reqObj)
- if (method == 'GET' && config.GET) {
- response = await config.GET(req);
- } else if (method == 'POST' && config.POST) {
- response = await config.POST(req);
- } else if (method == 'PATCH' && config.PATCH) {
- response = await config.PATCH(req);
- } else if (method == 'PUT' && config.PUT) {
- response = await config.PUT(req);
- } else {
- response = new Response('Not Found');
- response.status = 404
- }
- return JSON.stringify(response)
-}
-
-// Only works for ascii string
-export function stringToHex(str: string): string {
- let hex = '';
- for (let i = 0; i < str.length; i++) {
- hex += str.charCodeAt(i).toString(16).padStart(2, '0');
- }
- return '0x' + hex;
-}
diff --git a/src/index.ts b/src/index.ts
index a94531b..6ad6134 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,31 +1,68 @@
-import { Request, Response, route } from './httpSupport'
-
-import OpenAI from 'openai'
-
-async function GET(req: Request): Promise {
- let result = { message: '' }
- const secrets = req.secret || {}
- const queries = req.queries
- const openaiApiKey = (secrets.openaiApiKey) ? secrets.openaiApiKey as string : ''
- const openai = new OpenAI({ apiKey: openaiApiKey })
- // Choose from any model listed here https://platform.openai.com/docs/models
- const openAiModel = (queries.openAiModel) ? queries.openAiModel[0] : 'gpt-4o';
+import { serve } from '@hono/node-server'
+import { Hono } from 'hono'
+import OpenAI from "openai";
+
+export const app = new Hono()
+
+const getAPIKey = () => {
+ console.log('Getting API Key from vault...')
+ let vault: Record = {};
+ try {
+ vault = JSON.parse(process.env.secret || '')
+ } catch (e) {
+ console.error(e)
+ }
+ return vault.openaiApiKey || ''
+}
+
+function buildHtmlResponse(message: string) {
+ const htmlResponse = `
+
+
+
+
+
+ OpenAI Response
+
+
+
+
+
Response
+
${message}
+
+
+
+ `;
+ return htmlResponse;
+ }
+
+app.get('/', async (c) => {
+ let result = {message: ''}
+ const openaiApiKey = getAPIKey()
+ const queries = c.req.queries() || {}
+ const openai = new OpenAI({apiKey: openaiApiKey})
+ const openAiModel = (queries.openAiModel) ? queries.openAiModel[0] : 'gpt-3.5-turbo';
const query = (queries.chatQuery) ? queries.chatQuery[0] as string : 'Who are you?'
const completion = await openai.chat.completions.create({
- messages: [{ role: "system", content: `${query}` }],
+ messages: [{role: "system", content: `${query}`}],
model: `${openAiModel}`,
})
-
result.message = (completion.choices) ? completion.choices[0].message.content as string : 'Failed to get result'
- return new Response(JSON.stringify(result))
-}
+ return c.html(buildHtmlResponse(result.message))
-async function POST(req: Request): Promise {
- return new Response(JSON.stringify({message: 'Not Implemented'}))
-}
+})
-export default async function main(request: string) {
- return await route({ GET, POST }, request)
-}
+const port = 3000
+console.log(`Server is running on http://localhost:${port}`)
+
+serve({
+ fetch: app.fetch,
+ port
+})
diff --git a/tests/index.test.ts b/tests/index.test.ts
new file mode 100644
index 0000000..76c0546
--- /dev/null
+++ b/tests/index.test.ts
@@ -0,0 +1,32 @@
+import {afterAll, describe, test, vi, expect, beforeAll} from 'vitest'
+import { app } from '../src/'
+import * as path from "node:path";
+
+const chatQuery = 'Who are you two?';
+const model = 'claude-3-5-sonnet-20241022';
+
+// Set Testing env secrets
+const secretsFile = '../secrets/default.json'
+vi.stubEnv('secret', JSON.stringify(require(path.join(__dirname, secretsFile))))
+
+describe('Test OpenAI Agent Contract', () => {
+ test('returns default response when no query parameters are provided', async () => {
+ const resp = await app.request('/')
+ expect(resp.status).toBe(200)
+ const text = await resp.text();
+ expect(text).toContain('Response
')
+ }, 10000) // Define o timeout para 10 segundos
+
+ test('returns response with provided query parameters', async () => {
+ const resp = await app.request('/?chatQuery=Hello&openAiModel=gpt-3.5-turbo')
+ expect(resp.status).toBe(200)
+ const text = await resp.text();
+ expect(text).toContain('Response
')
+
+ }, 10000) // Define o timeout para 10 segundos
+// Define o timeout para 10 segundos
+})
+
+afterAll(async () => {
+ console.log(`\nNow you are ready to publish your agent, add secrets, and interact with your agent in the following steps:\n- Execute: 'npm run publish-agent'\n- Set secrets: 'npm run set-secrets'\n- Go to the url produced by setting the secrets (e.g. https://wapo-testnet.phala.network/ipfs/QmPQJD5zv3cYDRM25uGAVjLvXGNyQf9Vonz7rqkQB52Jae?key=b092532592cbd0cf)`)
+}) ;
\ No newline at end of file