-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from usherlabs/feature/open_ai
Feature/open ai
- Loading branch information
Showing
9 changed files
with
952 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { log } from "@/logger"; | ||
import type { ProcessedRequestAdded } from "@/types"; | ||
import { isValidJson } from "@/util"; | ||
import axios, { type AxiosResponse } from "axios"; | ||
import { run as jqRun } from "node-jq"; | ||
|
||
export abstract class BasicBearerAPIHandler { | ||
constructor( | ||
protected accessToken: string, | ||
protected supported_host: string[], | ||
protected supported_paths: string[], | ||
protected rate: number, | ||
) {} | ||
|
||
get hosts() { | ||
return this.supported_host; | ||
} | ||
|
||
get paths() { | ||
return this.supported_paths; | ||
} | ||
|
||
get getRequestRate() { | ||
return this.rate; | ||
} | ||
|
||
isApprovedPath(url: URL): boolean { | ||
return ( | ||
this.hosts.includes(url.hostname.toLowerCase()) && | ||
this.supported_paths.filter((path) => url.pathname.toLowerCase().startsWith(path)).length > 0 | ||
); | ||
} | ||
|
||
getAccessToken(): string | null { | ||
return this.accessToken; | ||
} | ||
|
||
abstract validatePayload(path: string, payload: string | null): boolean; | ||
|
||
async submitRequest(data: ProcessedRequestAdded<any>): Promise<{ status: number; message: string }> { | ||
try { | ||
const url = data.params.url?.includes("http") ? data.params.url : `https://${data.params.url}`; | ||
try { | ||
const url_object = new URL(url); | ||
if (!this.isApprovedPath(url_object)) { | ||
return { status: 406, message: `${url_object} is supposed by this orchestrator` }; | ||
} | ||
if (this.validatePayload(url_object.pathname, data.params.body)) { | ||
return { status: 406, message: `Invalid Payload` }; | ||
} | ||
} catch (err) { | ||
return { status: 406, message: `Invalid Domain Name` }; | ||
} | ||
|
||
const token = this.getAccessToken(); | ||
let request: AxiosResponse<any, any>; | ||
if (isValidJson(data.params.headers)) { | ||
// TODO: Replace direct requests via axios with requests via VerityClient TS module | ||
request = await axios({ | ||
method: data.params.method, | ||
data: data.params.body, | ||
url: url, | ||
headers: { | ||
...JSON.parse(data.params.headers), | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
}); | ||
// return { status: request.status, message: request.data }; | ||
} else { | ||
request = await axios({ | ||
method: data.params.method, | ||
data: data.params.body, | ||
url: url, | ||
headers: { | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
}); | ||
} | ||
|
||
try { | ||
const result = (await jqRun(data.pick, JSON.stringify(request.data), { input: "string" })) as string; | ||
return { status: request.status, message: result }; | ||
} catch { | ||
return { status: 409, message: "`Pick` value provided could not be resolved on the returned response" }; | ||
} | ||
// return { status: request.status, message: result }; | ||
} catch (error: any) { | ||
log.debug( | ||
JSON.stringify({ | ||
error: error.message, | ||
}), | ||
); | ||
|
||
if (axios.isAxiosError(error)) { | ||
// Handle Axios-specific errors | ||
if (error.response) { | ||
// Server responded with a status other than 2xx | ||
return { status: error.response.status, message: error.response.data }; | ||
} else if (error.request) { | ||
// No response received | ||
return { status: 504, message: "No response received" }; | ||
} else { | ||
// Handle non-Axios errors | ||
return { status: 500, message: "Unexpected error" }; | ||
} | ||
} | ||
} | ||
return { status: 500, message: "Something unexpected Happened" }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import env from "@/env"; | ||
import Joi from "joi"; | ||
import { BasicBearerAPIHandler } from "./base"; | ||
|
||
const chatSchema = Joi.object({ | ||
model: Joi.string().required(), | ||
messages: Joi.array().items( | ||
Joi.object({ | ||
role: Joi.string().valid("developer", "user").required(), | ||
content: Joi.string().required(), | ||
}).required(), | ||
), | ||
}); | ||
export default class TwitterIntegration extends BasicBearerAPIHandler { | ||
validatePayload(path: string, payload: string): boolean { | ||
try { | ||
if (this.supported_paths.includes(path)) { | ||
if (path === "/v1/chat/completions") { | ||
const { error, value } = chatSchema.validate(JSON.parse(payload)); | ||
if (error) { | ||
return false; | ||
} else { | ||
if (value.model === "gpt-4o") { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
return false; | ||
} catch { | ||
return false; | ||
} | ||
} | ||
} | ||
|
||
export const instance = new TwitterIntegration( | ||
env.integrations.xBearerToken, | ||
["api.openai.com"], | ||
["/v1/chat/completions"], | ||
60 * 1000, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,15 @@ | ||
import env from "@/env"; | ||
import { BasicBearerAPIHandler } from "./base"; | ||
|
||
class XfkaTwitter { | ||
private SERVER_DOMAIN = "api.twitter.com"; | ||
|
||
constructor(private accessToken: string) {} | ||
|
||
get hosts() { | ||
return ["x.com", "api.x.com", "twitter.com", "api.twitter.com"]; | ||
} | ||
|
||
get getRequestRate() { | ||
return 60 * 1000; // | ||
} | ||
getAccessToken(): string | null { | ||
return this.accessToken; | ||
export default class TwitterIntegration extends BasicBearerAPIHandler { | ||
validatePayload(path: string): boolean { | ||
return true; | ||
} | ||
} | ||
|
||
export const instance = new XfkaTwitter(env.integrations.xBearerToken); | ||
export const instance = new TwitterIntegration( | ||
env.integrations.openAIToken, | ||
["api.x.com", "api.twitter.com"], | ||
["/2/tweets"], | ||
60 * 1000, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.