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

Feature/open ai #15

Open
wants to merge 8 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
3 changes: 1 addition & 2 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ SENTRY_DSN=""
ECDSA_PRIVATE_KEY="" # for verity analysis.

# Integrations
X_API_KEY=""
X_API_SECRET=""
X_BEARER_TOKEN=""

OPEN_AI_TOKEN=""
61 changes: 56 additions & 5 deletions docs/ROOCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,37 @@ Export the Rooch Private Key:

```bash
rooch account export --address <Rooch Address>
```

```


To connect to the local Rooch node, set `ROOCH_CHAIN_ID` to `"localnet"`.
Otherwise, connect to testNet by setting `ROOCH_CHAIN_ID` to `"testnet"`, or to TestNet by setting `ROOCH_CHAIN_ID` to `"testnet"`.
Ensure that `ROOCH_ORACLE_ADDRESS` is set to the address of the deployed module, e.g., `"0x85859e45551846d9ab8651bb0b6f6e1740c9d758cfda05cfc39d49e2a604d783"`.

#### Step 7: Run Orchestrator
#### Step 7: Register supported URL
Additional steps for managing supported orchestrator URL

- To add URL

```bash
rooch move run --function <oracle_address>::registry::add_supported_url --sender-account <orchestrator_address> --args 'string:https://api.x.com/2/users/' --args 'u256:400000' --args 'u64:0' --args 'u256:0' --args 'u256:0'
```

- to remove URLs

```bash
rooch move run --function <oracle_address>::registry::remove_supported_url --sender-account <orchestrator_address> --args 'string:api.twitter.com/2/users/'
```

- To view supported URLS

```bash
rooch move view --function <oracle_address>::registry::get_supported_urls --args 'address:<orchestrator_address>'
```


#### Step 8: Run Orchestrator

Start the development server for your application. This step might vary depending on your project setup; the command below assumes a typical setup.

Expand All @@ -103,7 +127,7 @@ yarn dev
pnpm dev
```

#### Step 8: Send New Request Transaction
#### Step 9: Send New Request Transaction

Finally, send a new request transaction to have it indexed. Make sure to replace placeholders with actual values relevant to your setup.

Expand All @@ -114,7 +138,15 @@ rooch move run --function <contractAddress>::example_caller::request_data --sen
Here's an example of requesting the Twitter Followers Count on a Local Rooch Node:

```bash
rooch move run --function 0x9ce8eaf2166e9a6d4e8f1d27626297a0cf5ba1eaeb31137e08cc8f7773fb83f8::example_caller::request_data --sender-account default --args 'string:https://api.x.com/2/users/by/username/elonmusk?user.fields=public_metrics' --args 'string:GET' --args 'string:{}' --args 'string:{}' --args 'string:.data.public_metrics.followers_count' --args 'address:0x694cbe655b126e9e6a997e86aaab39e538abf30a8c78669ce23a98740b47b65d'
rooch move run --function 0x0d6144b074dd19a9ff581abd5bf7815a39222c8b3ac68ce5938c9d9723544e08::example_caller::request_data --sender-account default --args 'string:https://api.x.com/2/users/by/username/elonmusk?user.fields=public_metrics' --args 'string:GET' --args 'string:{}' --args 'string:{}' --args 'string:.data.public_metrics.followers_count' --args 'address:0x694cbe655b126e9e6a997e86aaab39e538abf30a8c78669ce23a98740b47b65d' --args 'u256:50000000'
```
or
```bash
rooch move run --function 0x0d6144b074dd19a9ff581abd5bf7815a39222c8b3ac68ce5938c9d9723544e08::example_caller::request_data --sender-account default --args 'string:https://api.openai.com/v1/chat/completions' --args 'string:POST' --args 'string:{}' --args 'string:{
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Say this is a test!"}],
"temperature": 0.7
}' --args 'string:.choices[].message.content' --args 'address:0x694cbe655b126e9e6a997e86aaab39e538abf30a8c78669ce23a98740b47b65d' --args 'u256:50000000'
```

To check the state of the response object on a local Rooch node, use the following command:
Expand All @@ -123,6 +155,25 @@ To check the state of the response object on a local Rooch node, use the followi
rooch state -a /object/0x7a01ddf194f8a1c19212d56f747294352bf2e5cf23e6e10e64937aa1955704b0
```


#### Step 10: Manage Escrow balance

- To view balance

```bash
rooch move view --function 0x0d6144b074dd19a9ff581abd5bf7815a39222c8b3ac68ce5938c9d9723544e08::oracles::get_user_balance --args 'address:<your_address>'
```

- To withdraw Balance
```bash
rooch move run --function 0x0d6144b074dd19a9ff581abd5bf7815a39222c8b3ac68ce5938c9d9723544e08::oracles::withdraw_from_escrow --args 'u256:<amount>'
```

- To Deposit
```bash
rooch move run --function 0x0d6144b074dd19a9ff581abd5bf7815a39222c8b3ac68ce5938c9d9723544e08::oracles::deposit_to_escrow --args 'u256:<amount>'
```

To confirm the `Request` Object State, use the Object ID generated from the initial transaction to query the state of the response object.
This allows you to verify that the request was processed successfully and that the response object is correctly stored in the Rooch Network state.

Expand All @@ -131,7 +182,7 @@ This allows you to verify that the request was processed successfully and that t
An example of requesting the Twitter Followers Count on a Rooch Testnet:

```bash
rooch move run --function 0x9ce8eaf2166e9a6d4e8f1d27626297a0cf5ba1eaeb31137e08cc8f7773fb83f8::example_caller::request_data --sender-account default --args 'string:https://api.x.com/2/users/by/username/elonmusk?user.fields=public_metrics' --args 'string:GET' --args 'string:{}' --args 'string:{}' --args 'string:.data.public_metrics.followers_count' --args 'address:0x694cbe655b126e9e6a997e86aaab39e538abf30a8c78669ce23a98740b47b65d'
rooch move run --function 0x0d6144b074dd19a9ff581abd5bf7815a39222c8b3ac68ce5938c9d9723544e08::example_caller::request_data --sender-account default --args 'string:https://api.x.com/2/users/by/username/elonmusk?user.fields=public_metrics' --args 'string:GET' --args 'string:{}' --args 'string:{}' --args 'string:.data.public_metrics.followers_count' --args 'address:0x694cbe655b126e9e6a997e86aaab39e538abf30a8c78669ce23a98740b47b65d'
```

To check the state of the response object on testnet, devnet, or mainnet,
Expand Down
2 changes: 1 addition & 1 deletion orchestrator/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const baseConfig = {
aptosNoditKey: process.env.APTOS_NODIT_KEY,
// Common
sentryDSN: process.env.SENTRY_DSN ?? "",
ecdsaPrivateKey: process.env.SENTRY_DSN ?? "",
ecdsaPrivateKey: process.env.ECDSA_PRIVATE_KEY ?? "",
batchSize: process.env.BATCH_SIZE ?? 1000,
// Integrations
xBearerToken: process.env.X_BEARER_TOKEN ?? "",
Expand Down
4 changes: 2 additions & 2 deletions orchestrator/src/indexer/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ export abstract class Indexer {
const newRequestsEvents = await this.fetchRequestAddedEvents(Number(latestCommit?.eventSeq ?? 0) ?? 0);
for (let i = 0; i < newRequestsEvents.length; i++) {
try {
if (i > 0) await new Promise((resolve) => setTimeout(resolve, xTwitterInstance.getRequestRate));

const event = newRequestsEvents[i];
const data = await this.processRequestAddedEvent(event);

log.info({ data });

if (data) {
try {
await this.sendFulfillment(event, data.status, JSON.stringify(data.message));
Expand Down
1 change: 1 addition & 0 deletions orchestrator/src/indexer/rooch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ export default class RoochIndexer extends Indexer {
const tx = new Transaction();
tx.callFunction({
target: data.notify ?? "",
maxGas: 10_000,
});

const receipt = await client.signAndExecuteTransaction({
Expand Down
20 changes: 17 additions & 3 deletions orchestrator/src/integrations/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import axios, { type AxiosResponse } from "axios";
import { run as jqRun } from "node-jq";

export abstract class BasicBearerAPIHandler {
protected last_executed = 0;

constructor(
protected accessToken: string,
protected supported_host: string[],
Expand Down Expand Up @@ -39,13 +41,24 @@ export abstract class BasicBearerAPIHandler {

async submitRequest(data: ProcessedRequestAdded<any>): Promise<{ status: number; message: string }> {
try {
const currentTime = Date.now();
const timeSinceLastExecution = currentTime - this.last_executed;

if (timeSinceLastExecution < this.getRequestRate) {
const waitTime = this.getRequestRate - timeSinceLastExecution;
await new Promise((resolve) => setTimeout(resolve, waitTime));
}

this.last_executed = Date.now();

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)) {
if (!this.validatePayload(url_object.pathname, data.params.body)) {
return { status: 406, message: `Invalid Payload` };
}
} catch (err) {
Expand All @@ -54,11 +67,11 @@ export abstract class BasicBearerAPIHandler {

const token = this.getAccessToken();
let request: AxiosResponse<any, any>;
if (isValidJson(data.params.headers)) {
if (isValidJson(data.params.headers) && isValidJson(data.params.body)) {
// TODO: Replace direct requests via axios with requests via VerityClient TS module
request = await axios({
method: data.params.method,
data: data.params.body,
data: JSON.parse(data.params.body),
url: url,
headers: {
...JSON.parse(data.params.headers),
Expand All @@ -79,6 +92,7 @@ export abstract class BasicBearerAPIHandler {

try {
const result = (await jqRun(data.pick, JSON.stringify(request.data), { input: "string" })) as string;
log.info({ status: request.status, message: result });
return { status: request.status, message: result };
} catch {
return { status: 409, message: "`Pick` value provided could not be resolved on the returned response" };
Expand Down
9 changes: 6 additions & 3 deletions orchestrator/src/integrations/openAI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const chatSchema = Joi.object({
model: Joi.string().required(),
messages: Joi.array().items(
Joi.object({
role: Joi.string().valid("developer", "user").required(),
role: Joi.string().required(),
content: Joi.string().required(),
}).required(),
),
Expand All @@ -16,7 +16,10 @@ export default class OpenAIIntegration extends BasicBearerAPIHandler {
try {
if (this.supported_paths.includes(path)) {
if (path === "/v1/chat/completions") {
const { error, value } = chatSchema.validate(JSON.parse(payload));
const { error, value } = chatSchema.validate(JSON.parse(payload), {
allowUnknown: true,
});
console.log({ value, error });
if (error) {
return false;
} else {
Expand All @@ -34,7 +37,7 @@ export default class OpenAIIntegration extends BasicBearerAPIHandler {
}

export const instance = new OpenAIIntegration(
env.integrations.xBearerToken,
env.integrations.openAIToken,
["api.openai.com"],
["/v1/chat/completions"],
60 * 1000,
Expand Down
2 changes: 1 addition & 1 deletion orchestrator/src/integrations/xtwitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export default class TwitterIntegration extends BasicBearerAPIHandler {
export const instance = new TwitterIntegration(
env.integrations.openAIToken,
["api.x.com", "api.twitter.com"],
["/2/tweets"],
["/2/tweets", "/2/users/"],
60 * 1000,
);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"dependencies": {
"@aptos-labs/ts-sdk": "^1.29.1",
"@prisma/client": "5.19.1",
"@roochnetwork/rooch-sdk": "^0.2.7",
"@roochnetwork/rooch-sdk": "^0.2.9",
"@sentry/node": "^8.26.0",
"@sentry/profiling-node": "^8.26.0",
"axios": "^1.7.4",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions rooch/Move.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "verity-move-oracles"
name = "VerityMoveOracles"
version = "0.0.1"

[dependencies]
Expand All @@ -9,11 +9,10 @@ RoochFramework = { git = "https://github.com/rooch-network/rooch.git", subdir =

[addresses]
verity = "_"
verity_test_foreign_module = "_"
verity_test_foreign_module = "0x553"
std = "0x1"
moveos_std = "0x2"
rooch_framework = "0x3"

[dev-addresses]
verity = "0x42"
verity_test_foreign_module = "0x43"
verity = "0x42"
14 changes: 7 additions & 7 deletions rooch/sources/oracle_registry.move
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ module verity::registry {
/// Cost per token for payload beyond minimum length
cost_per_payload_token: u256,
/// Cost per token for response data
cost_per_respond_token: u256,
cost_per_response_token: u256,
}

/// Global storage for oracle registry
Expand Down Expand Up @@ -72,7 +72,7 @@ module verity::registry {
orchestrator: address,
url: String,
payload_length: u64,
respond_length: u64
response_length: u64
): Option<u256> {
let supported_urls = &account::borrow_resource<GlobalParams>(@verity).supported_urls;

Expand All @@ -95,7 +95,7 @@ module verity::registry {
return option::some(
orchestrator_url.base_fee +
(chargeable_token * orchestrator_url.cost_per_payload_token) +
(orchestrator_url.cost_per_respond_token * (respond_length as u256))
(orchestrator_url.cost_per_response_token * (response_length as u256))
)
};
i = i + 1;
Expand All @@ -106,13 +106,13 @@ module verity::registry {

/// Add support for a new URL endpoint with specified pricing parameters
/// If URL already exists, updates the existing metadata
public fun add_supported_url(
public entry fun add_supported_url(
caller: &signer,
url_prefix: String,
base_fee: u256,
minimum_payload_length: u64,
cost_per_payload_token: u256,
cost_per_respond_token: u256
cost_per_response_token: u256
) {
let sender = signer::address_of(caller);
let global_params = account::borrow_mut_resource<GlobalParams>(@verity);
Expand All @@ -128,7 +128,7 @@ module verity::registry {
base_fee,
minimum_payload_length,
cost_per_payload_token,
cost_per_respond_token
cost_per_response_token
};

// Check if URL prefix already exists
Expand Down Expand Up @@ -163,7 +163,7 @@ module verity::registry {

/// Remove support for a URL endpoint
/// Aborts if URL is not found or caller is not the oracle
public fun remove_supported_url(
public entry fun remove_supported_url(
caller: &signer,
url_prefix: String
) {
Expand Down
Loading