diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml new file mode 100644 index 0000000..2b7c2c4 --- /dev/null +++ b/.github/workflows/e2e_tests.yml @@ -0,0 +1,125 @@ + +name: e2e tests +on: + pull_request: + branches: + - main +jobs: + e2e-tests: + runs-on: ubuntu-latest + env: + NETWORK: "localhost" + ORACLE_ADDRESS: "0x5FbDB2315678afecb367f032d93F642f64180aa3" + TEST_CONTRACT_ADDRESS: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + steps: + - uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + cache-dependency-path: contracts/package-lock.json + - name: "Install Dependencies" + id: install + run: cd contracts && npm ci + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Cache pip + uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + !~/.cache/pip/log + key: ${{ runner.os }}-pip-${{ hashFiles('oracles/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install Python Dependencies + run: pip install -r oracles/requirements.txt + - name: Run Hardhat Node + run: cd contracts && npx hardhat node & + - name: "Deploy Contracts" + run: + cd contracts && npm run deployAll:localhost + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Whitelist the address" + run: + cd contracts && npx hardhat whitelist --oracle-address ${{ env.ORACLE_ADDRESS }} --whitelist-address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --network localhost + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Deploy Test Contract" + run: + cd contracts && npm run deployTest:localhost + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + ORACLE_ADDRESS: ${{ env.ORACLE_ADDRESS }} + - name: Run Python Oracle + run: | + cd oracles && python oracle.py > oracle_output.txt 2>&1 & + echo $! > python_oracle.pid + env: + CHAIN_ID: 1337 + WEB3_RPC_URL: "http://127.0.0.1:8545/" + ORACLE_ADDRESS: ${{ env.ORACLE_ADDRESS }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + OPEN_AI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + SERPER_API_KEY: ${{ secrets.SERPER_API_KEY }} + BEARLY_API_KEY: ${{ secrets.BEARLY_API_KEY }} + PINATA_GATEWAY_TOKEN: ${{ secrets.PINATA_GATEWAY_TOKEN }} + PINATA_API_JWT: ${{ secrets.PINATA_API_JWT }} + - name: "Test 1 - OpenAI gpt-4-turbo-preview" + run: + cd contracts && npx hardhat openai --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --model gpt-4-turbo-preview --message "Who is the president of USA?" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 2 - OpenAI gpt-3.5-turbo-1106" + run: + cd contracts && npx hardhat openai --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --model gpt-3.5-turbo-1106 --message "Who is the president of USA?" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 3 - Groq llama2-70b-4096" + run: + cd contracts && npx hardhat groq --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --model llama2-70b-4096 --message "Who is the president of USA?" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 4 - Groq mixtral-8x7b-32768" + run: + cd contracts && npx hardhat groq --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --model mixtral-8x7b-32768 --message "Who is the president of USA?" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 5 - gemma-7b-it" + run: + cd contracts && npx hardhat groq --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --model gemma-7b-it --message "Who is the president of USA?" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 6 - Image Generation" + run: + cd contracts && npx hardhat image_generation --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --query "Red rose" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 7 - Web search" + run: + cd contracts && npx hardhat web_search --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --query "Capital of Germany" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 8 - Code interpreter" + run: + cd contracts && npx hardhat code_interpreter --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --query "print(2+2)" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 9 - Add knowledge base" + run: + cd contracts && npx hardhat add_knowledge_base --oracle-address ${{ env.ORACLE_ADDRESS }} --cid QmdCgbMawRVE6Kc1joZmhgDo2mSZFgRgWvBCqUvJV9JwkF --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: "Test 10 - Query knowledge base" + run: + cd contracts && npx hardhat query_knowledge_base --contract-address ${{ env.TEST_CONTRACT_ADDRESS }} --cid QmdCgbMawRVE6Kc1joZmhgDo2mSZFgRgWvBCqUvJV9JwkF --query "What is the oracle smart contract address?" --network ${{ env.NETWORK }} + env: + PRIVATE_KEY_LOCALHOST: ${{ secrets.PRIVATE_KEY }} + - name: Display Oracle Output + if: always() # Ensures this step runs even if a previous step fails + run: cat oracles/oracle_output.txt \ No newline at end of file diff --git a/contracts/README.md b/contracts/README.md index fdc22af..69c9b39 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -77,3 +77,27 @@ npm run generateStandardJson This generated JSON files are in `./contracts/artifacts/solidity-json/contracts` +### Running e2e validation tests + +**Deploy test contract to relevant network** +``` +npm run deployTest:localhost +``` +``` +npm run deployTest:galadriel +``` + +**Single run** +``` +npx hardhat e2e --contract-address --oracle-address --network +``` + +**Cron job with Slack** +``` +ts-node tasks/e2eCron.ts +``` + +**Cron job with Slack in docker** +``` +docker compose -f docker/docker-compose-e2e.yml up --build -d +``` \ No newline at end of file diff --git a/contracts/contracts/Test.sol b/contracts/contracts/Test.sol index c8e8414..7e03e87 100644 --- a/contracts/contracts/Test.sol +++ b/contracts/contracts/Test.sol @@ -6,6 +6,78 @@ pragma solidity ^0.8.20; interface IOracle { + struct GroqRequest { + string model; + int8 frequencyPenalty; + string logitBias; + uint32 maxTokens; + int8 presencePenalty; + string responseFormat; + uint seed; + string stop; + uint temperature; + uint topP; + string user; + } + + struct GroqResponse { + string id; + + string content; + + uint64 created; + string model; + string systemFingerprint; + string object; + + uint32 completionTokens; + uint32 promptTokens; + uint32 totalTokens; + } + + function createGroqLlmCall( + uint promptId, + GroqRequest memory request + ) external returns (uint); + + struct OpenAiRequest { + string model; + int8 frequencyPenalty; + string logitBias; + uint32 maxTokens; + int8 presencePenalty; + string responseFormat; + uint seed; + string stop; + uint temperature; + uint topP; + string tools; + string toolChoice; + string user; + } + + struct OpenAiResponse { + string id; + + string content; + string functionName; + string functionArguments; + + uint64 created; + string model; + string systemFingerprint; + string object; + + uint32 completionTokens; + uint32 promptTokens; + uint32 totalTokens; + } + + function createOpenAiLlmCall( + uint promptId, + OpenAiRequest memory request + ) external returns (uint); + function createFunctionCall( uint functionCallbackId, string memory functionType, @@ -23,6 +95,7 @@ interface IOracle { contract Test { address private owner; address public oracleAddress; + string public llmMessage; string public lastResponse; string public lastError; uint private callsCount; @@ -67,6 +140,76 @@ contract Test { return currentId; } + function callOpenAiLLM(string memory model, string memory message) public returns (uint i) { + uint currentId = callsCount; + callsCount = currentId + 1; + + llmMessage = message; + lastResponse = ""; + lastError = ""; + + IOracle(oracleAddress).createOpenAiLlmCall( + currentId, + IOracle.OpenAiRequest({ + model: model, + frequencyPenalty : 21, // > 20 for null + logitBias : "", // empty str for null + maxTokens : 1000, // 0 for null + presencePenalty : 21, // > 20 for null + responseFormat : "{\"type\":\"text\"}", + seed : 0, // null + stop : "", // null + temperature : 10, // Example temperature (scaled up, 10 means 1.0), > 20 means null + topP : 101, // Percentage 0-100, > 100 means null + tools : "", + toolChoice : "", // "none" or "auto" + user : "" // null + }) + ); + + return currentId; + } + + function callGroqLLM(string memory model, string memory message) public returns (uint i) { + uint currentId = callsCount; + callsCount = currentId + 1; + + llmMessage = message; + lastResponse = ""; + lastError = ""; + + IOracle(oracleAddress).createGroqLlmCall( + currentId, + IOracle.GroqRequest({ + model : model, + frequencyPenalty : 21, // > 20 for null + logitBias : "", // empty str for null + maxTokens : 1000, // 0 for null + presencePenalty : 21, // > 20 for null + responseFormat : "{\"type\":\"text\"}", + seed : 0, // null + stop : "", // null + temperature : 10, // Example temperature (scaled up, 10 means 1.0), > 20 means null + topP : 101, // Percentage 0-100, > 100 means null + user : "" // null + }) + ); + + return currentId; + } + + function getMessageHistoryContents(uint chatId) public view returns (string[] memory) { + string[] memory messages = new string[](1); + messages[0] = llmMessage; + return messages; + } + + function getMessageHistoryRoles(uint chatId) public view returns (string[] memory) { + string[] memory roles = new string[](1); + roles[0] = "user"; + return roles; + } + function queryKnowledgeBase(string memory cid, string memory query) public returns (uint i) { uint currentId = callsCount; callsCount = currentId + 1; @@ -97,11 +240,29 @@ contract Test { string [] memory documents, string memory errorMessage ) public onlyOracle { - string memory newContent = ""; - for (uint i = 0; i < documents.length; i++) { - newContent = string(abi.encodePacked(newContent, documents[i], "\n")); - } - lastResponse = newContent; - lastError = errorMessage; + string memory newContent = ""; + for (uint i = 0; i < documents.length; i++) { + newContent = string(abi.encodePacked(newContent, documents[i], "\n")); } + lastResponse = newContent; + lastError = errorMessage; + } + + function onOracleOpenAiLlmResponse( + uint runId, + IOracle.OpenAiResponse memory response, + string memory errorMessage + ) public onlyOracle { + lastResponse = response.content; + lastError = errorMessage; } + + function onOracleGroqLlmResponse( + uint runId, + IOracle.GroqResponse memory response, + string memory errorMessage + ) public onlyOracle { + lastResponse = response.content; + lastError = errorMessage; + } +} diff --git a/contracts/docker/Dockerfile b/contracts/docker/Dockerfile new file mode 100644 index 0000000..4cd988b --- /dev/null +++ b/contracts/docker/Dockerfile @@ -0,0 +1,11 @@ +FROM node:20-alpine + +# Set the working directory in the container +WORKDIR /app + +COPY . . + +# Install any dependencies +RUN npm ci + +CMD ["npx", "ts-node", "tasks/e2eCron.ts"] diff --git a/contracts/docker/docker-compose-e2e.yml b/contracts/docker/docker-compose-e2e.yml new file mode 100644 index 0000000..336b910 --- /dev/null +++ b/contracts/docker/docker-compose-e2e.yml @@ -0,0 +1,9 @@ +version: '3' +services: + app: + build: + context: .. + dockerfile: docker/Dockerfile + volumes: + - ..:/app + restart: "unless-stopped" diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 05f145f..379795d 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -3,6 +3,7 @@ import "@nomicfoundation/hardhat-toolbox"; import "@xyrusworx/hardhat-solidity-json"; import "./tasks/whitelist"; import "./tasks/deployments"; +import "./tasks/e2e"; import "./tasks/functions"; require('dotenv').config() diff --git a/contracts/tasks/e2e.ts b/contracts/tasks/e2e.ts new file mode 100644 index 0000000..67b8572 --- /dev/null +++ b/contracts/tasks/e2e.ts @@ -0,0 +1,174 @@ +import {task} from "hardhat/config"; +import {HardhatRuntimeEnvironment} from "hardhat/types"; + +const TIMEOUT_SECONDS: number = 300 +const green = "\x1b[32m" +const reset = "\x1b[0m" + +class TimeoutError extends Error { + constructor(message: string) { + super(message); + this.name = "TimeoutError" + } +} + +task("e2e", "Runs all e2e tests") + .addParam("contractAddress", "The address of the Test contract") + .addParam("oracleAddress", "The address of the Oracle contract") + .setAction(async (taskArgs: any, hre: HardhatRuntimeEnvironment) => { + const contractAddress = taskArgs.contractAddress; + const oracleAddress = taskArgs.oracleAddress; + + await runOpenAi( + contractAddress, + "gpt-4-turbo-preview", + "Who is the president of USA?", + hre, + ) + await runOpenAi( + contractAddress, + "gpt-3.5-turbo-1106", + "Who is the president of USA?", + hre, + ) + await runGroq( + contractAddress, + "llama2-70b-4096", + "Who is the president of USA?", + hre, + ) + await runGroq( + contractAddress, + "mixtral-8x7b-32768", + "Who is the president of USA?", + hre, + ) + await runGroq( + contractAddress, + "gemma-7b-it", + "Who is the president of USA?", + hre, + ) + + console.log(`Running "image_generation"`) + await runTaskWithTimeout( + "image_generation", + { + contractAddress, + query: "Red rose", + }, + hre, + ) + console.log(`DONE Running "image_generation"`) + + console.log(`Running "web_search"`) + await runTaskWithTimeout( + "web_search", + { + contractAddress, + query: "Capital of Germany", + }, + hre, + ) + console.log(`DONE Running "web_search"`) + + console.log(`Running "code_interpreter"`) + await runTaskWithTimeout( + "code_interpreter", + { + contractAddress, + query: "print(2+2)", + }, + hre, + ) + console.log(`DONE Running "code_interpreter"`) + + // console.log(`Running "add_knowledge_base"`) + // await runTaskWithTimeout( + // "add_knowledge_base", + // { + // oracleAddress, + // cid: "QmdCgbMawRVE6Kc1joZmhgDo2mSZFgRgWvBCqUvJV9JwkF", + // }, + // hre, + // ) + // console.log(`DONE Running "add_knowledge_base"`) + // console.log(`Running "query_knowledge_base"`) + // await runTaskWithTimeout( + // "query_knowledge_base", + // { + // contractAddress, + // cid: "QmdCgbMawRVE6Kc1joZmhgDo2mSZFgRgWvBCqUvJV9JwkF", + // query: "What is the oracle smart contract address?", + // }, + // hre, + // ) + // console.log(`DONE Running "query_knowledge_base"`) + + + console.log("================================================") + console.log(green, "e2e run done", reset) + }); + +async function runTaskWithTimeout( + taskIdentifier: string, + taskArguments: any, + hre: HardhatRuntimeEnvironment, +) { + try { + const timeoutPromise = new Promise((resolve, reject) => { + const id = setTimeout(() => { + clearTimeout(id); + reject(new TimeoutError(taskIdentifier)); + }, TIMEOUT_SECONDS * 1000); + }); + + await Promise.race([ + timeoutPromise, + hre.run(taskIdentifier, taskArguments), + ]); + } catch (e: any) { + process.stderr.write(e.message + " ") + throw e + } + +} + + +async function runOpenAi( + contractAddress: string, + model: string, + message: string, + hre: HardhatRuntimeEnvironment, +) { + console.log(`Running "openai", with model: ${model}`) + await runTaskWithTimeout( + "openai", + { + contractAddress, + model, + message, + }, + hre, + ) + console.log(`DONE Running "openai", with model: ${model}.`) +} + +async function runGroq( + contractAddress: string, + model: string, + message: string, + hre: HardhatRuntimeEnvironment, +) { + console.log(`Running "groq", with model: ${model}`) + await runTaskWithTimeout( + "groq", + { + contractAddress, + model, + message, + }, + hre, + ) + console.log(`DONE Running "groq", with model: ${model}.`) +} \ No newline at end of file diff --git a/contracts/tasks/e2eCron.ts b/contracts/tasks/e2eCron.ts new file mode 100644 index 0000000..c701b4c --- /dev/null +++ b/contracts/tasks/e2eCron.ts @@ -0,0 +1,112 @@ +require('dotenv').config() + +import {spawn} from 'child_process'; + +const TIMEOUT_SECONDS: number = 60 * 60 + +async function main(): Promise { + const command = 'npx'; + const contractAddress = process.env.TEST_CONTRACT_ADDRESS + const oracleAddress = process.env.TEST_ORACLE_ADDRESS + const network = process.env.TEST_NETWORK + const slackWebHookUrl = process.env.TEST_SLACK_WEBHOOK_URL + if (!contractAddress) { + console.log(".env is missing TEST_CONTRACT_ADDRESS") + return + } + if (!oracleAddress) { + console.log(".env is missing ORACLE_ADDRESS") + return + } + if (!network) { + console.log(".env is missing TEST_NETWORK") + return + } + if (!slackWebHookUrl) { + console.log(".env is missing TEST_SLACK_WEBHOOK_URL") + return + } + const args: string[] = [ + "hardhat", + "e2e", + "--contract-address", contractAddress, + "--oracle-address", oracleAddress, + "--network", network + ]; + + while (true) { + console.log(`Running e2e tests with `) + await runTests(command, args, slackWebHookUrl) + console.log(`Run done, sleeping for ${TIMEOUT_SECONDS} seconds`) + await new Promise((resolve) => setTimeout(resolve, TIMEOUT_SECONDS * 1000)); + } +} + +async function runTests( + command: string, + args: string[], + slackWebHookUrl: string, +): Promise { + try { + await new Promise((resolve, reject) => { + const childProcess = spawn(command, args, {shell: true}); + + let stdoutData = ''; + let stderrData = ''; + + childProcess.stdout.on('data', (data) => { + console.log(data.toString()) + }); + + childProcess.stderr.on('data', (data) => { + stderrData += data.toString(); + }); + + childProcess.on('close', (code) => { + if (code === 0) { + resolve(stdoutData); + } else { + reject(new Error(stderrData)); + } + }); + }) + } catch (e: any) { + console.error(e.message) + await postSlackMessage( + `e2e blockchain tests failed\n` + + "```" + + `${e.message}` + + "```", + slackWebHookUrl, + ) + } +} + +async function postSlackMessage( + text: string, + webhookUrl: string, +) { + try { + await fetch( + webhookUrl, + { + method: "POST", + headers: { + "Content-type": "application/json" + }, + body: JSON.stringify({ + text + }) + } + ) + } catch (e: any) { + console.error("Failed to post msg to slack", e.message) + } +} + +main() + .then(() => process.exit(0)) + .catch((error: any) => { + console.error(error); + process.exit(1); + }); \ No newline at end of file diff --git a/contracts/tasks/functions.ts b/contracts/tasks/functions.ts index 6a8ae2b..99939ef 100644 --- a/contracts/tasks/functions.ts +++ b/contracts/tasks/functions.ts @@ -1,12 +1,45 @@ import { task } from "hardhat/config"; import { Contract, TransactionReceipt } from "ethers"; import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { Func } from "mocha"; interface FunctionResponse { - response: string, - error: string, - } + response: string, + error: string, +} + +task("openai", "Calls the OpenAI LLM") + .addParam("contractAddress", "The address of the Test contract") + .addParam("model", "The model to use") + .addParam("message", "The message to send to the model") + .setAction(async (taskArgs, hre) => { + const contractAddress = taskArgs.contractAddress; + const model = taskArgs.model; + const message = taskArgs.message; + + const contract = await getContract("Test", contractAddress, hre); + const response = await queryOpenAiLLM(contract, model, message, hre); + console.log(response); + if (response.error.length > 0) { + process.exit(1); + } + }); + +task("groq", "Calls the Groq LLM") + .addParam("contractAddress", "The address of the Test contract") + .addParam("model", "The model to use") + .addParam("message", "The message to send to the model") + .setAction(async (taskArgs, hre) => { + const contractAddress = taskArgs.contractAddress; + const model = taskArgs.model; + const message = taskArgs.message; + + const contract = await getContract("Test", contractAddress, hre); + const response = await queryGroqLLM(contract, model, message, hre); + console.log(response) + if (response.error.length > 0) { + process.exit(1); + } + }); task("web_search", "Calls the web search function") .addParam("contractAddress", "The address of the Test contract") @@ -21,7 +54,7 @@ task("web_search", "Calls the web search function") if (response.error.length > 0) { process.exit(1); } -}); + }); task("image_generation", "Calls the image generation function") .addParam("contractAddress", "The address of the Test contract") @@ -38,7 +71,7 @@ task("image_generation", "Calls the image generation function") } }); - task("code_interpreter", "Calls the code interpreter function") +task("code_interpreter", "Calls the code interpreter function") .addParam("contractAddress", "The address of the Test contract") .addParam("query", "The query to ask the contract") .setAction(async (taskArgs, hre) => { @@ -53,7 +86,33 @@ task("image_generation", "Calls the image generation function") } }); - task("knowledge_base", "Queries a knowledge base") +task("add_knowledge_base", "Adds a knowledge base to the contract") + .addParam("oracleAddress", "The address of the Oracle contract") + .addParam("cid", "The CID of the knowledge base") + .setAction(async (taskArgs, hre) => { + const oracleAddress = taskArgs.oracleAddress; + const cid = taskArgs.cid; + + const contract = await getContract("ChatOracle", oracleAddress, hre); + const txResponse = await contract.addKnowledgeBase(cid); + const txReceipt = await txResponse.wait(); + let runId = getRunId(txReceipt, contract, hre); + let isProcessed = await contract.isKbIndexingRequestProcessed(runId); + let response = await contract.kbIndexes(cid); + let error = await contract.kbIndexingRequestErrors(runId); + while (!isProcessed) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + isProcessed = await contract.isKbIndexingRequestProcessed(runId); + response = await contract.kbIndexes(cid); + error = await contract.kbIndexingRequestErrors(runId); + } + console.log({ response: response, error: error }); + if (error.length > 0) { + process.exit(1); + } + }); + +task("query_knowledge_base", "Queries a knowledge base") .addParam("contractAddress", "The address of the Test contract") .addParam("cid", "The CID of the knowledge base") .addParam("query", "The query to ask the knowledge base") @@ -70,40 +129,92 @@ task("image_generation", "Calls the image generation function") } }); - async function getContract( - name: string, - contractAddress: string, - hre: HardhatRuntimeEnvironment - ): Promise { - const signer = (await hre.ethers.getSigners())[0]; - const ContractArtifact = await hre.artifacts.readArtifact(name); - return new hre.ethers.Contract(contractAddress, ContractArtifact.abi, signer); +async function getContract( + name: string, + contractAddress: string, + hre: HardhatRuntimeEnvironment +): Promise { + const signer = (await hre.ethers.getSigners())[0]; + const ContractArtifact = await hre.artifacts.readArtifact(name); + return new hre.ethers.Contract(contractAddress, ContractArtifact.abi, signer); +} + +async function queryOpenAiLLM( + contract: Contract, + model: string, + message: string, + hre: HardhatRuntimeEnvironment +): Promise { + try { + const txResponse = await contract.callOpenAiLLM(model, message); + await txResponse.wait(); + process.stdout.write("Waiting for response"); + let response = await contract.lastResponse(); + let error = await contract.lastError(); + while (response.length === 0 && error.length === 0) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + response = await contract.lastResponse(); + error = await contract.lastError(); + process.stdout.write("."); + } + console.log(""); + return { response: response, error: error }; + } catch (error) { + console.error(`Error calling contract function: ${error}`); + } + return { response: "", error: "Failed XX" }; +} + +async function queryGroqLLM( + contract: Contract, + model: string, + message: string, + hre: HardhatRuntimeEnvironment +): Promise { + try { + const txResponse = await contract.callGroqLLM(model, message); + await txResponse.wait(); + process.stdout.write("Waiting for response"); + let response = await contract.lastResponse(); + let error = await contract.lastError(); + while (response.length === 0 && error.length === 0) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + response = await contract.lastResponse(); + error = await contract.lastError(); + process.stdout.write("."); + } + console.log(""); + return { response: response, error: error }; + } catch (error) { + console.error(`Error calling contract function: ${error}`); } + return { response: "", error: "Failed XX" }; +} async function queryContractFunction( - contract: Contract, - tool: string, - query: string, - hre: HardhatRuntimeEnvironment + contract: Contract, + tool: string, + query: string, + hre: HardhatRuntimeEnvironment ): Promise { - try { - const txResponse = await contract.callFunction(tool, query); - await txResponse.wait(); - process.stdout.write("Waiting for response"); - let response = await contract.lastResponse(); - let error = await contract.lastError(); - while (response.length === 0 && error.length === 0) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - response = await contract.lastResponse(); - error = await contract.lastError(); - process.stdout.write("."); - } - console.log(""); - return { response: response, error: error }; - } catch (error) { - console.error(`Error calling contract function: ${error}`); + try { + const txResponse = await contract.callFunction(tool, query); + await txResponse.wait(); + process.stdout.write("Waiting for response"); + let response = await contract.lastResponse(); + let error = await contract.lastError(); + while (response.length === 0 && error.length === 0) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + response = await contract.lastResponse(); + error = await contract.lastError(); + process.stdout.write("."); } - return { response: "", error: "Failed XX"}; + console.log(""); + return { response: response, error: error }; + } catch (error) { + console.error(`Error calling contract function: ${error}`); + } + return { response: "", error: "Failed XX" }; } async function queryContractKnowledgeBase( @@ -113,21 +224,43 @@ async function queryContractKnowledgeBase( hre: HardhatRuntimeEnvironment ): Promise { try { - const txResponse = await contract.queryKnowledgeBase(cid, query); - await txResponse.wait(); - process.stdout.write("Waiting for response"); - let response = await contract.lastResponse(); - let error = await contract.lastError(); - while (response.length === 0 && error.length === 0) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - response = await contract.lastResponse(); - error = await contract.lastError(); - process.stdout.write("."); - } - console.log(""); - return { response: response, error: error }; + const txResponse = await contract.queryKnowledgeBase(cid, query); + await txResponse.wait(); + process.stdout.write("Waiting for response"); + let response = await contract.lastResponse(); + let error = await contract.lastError(); + while (response.length === 0 && error.length === 0) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + response = await contract.lastResponse(); + error = await contract.lastError(); + process.stdout.write("."); + } + console.log(""); + return { response: response, error: error }; } catch (error) { - console.error(`Error calling contract function: ${error}`); + console.error(`Error calling contract function: ${error}`); + } + return { response: "", error: "Failed XX" }; +} + +function getRunId( + receipt: TransactionReceipt, + contract: Contract, + hre: HardhatRuntimeEnvironment, + eventName: string = "KnowledgeBaseIndexRequestAdded", + eventArgIndex: number = 0 + ): number | undefined { + let runId + for (const log of receipt.logs) { + try { + const parsedLog = contract.interface.parseLog(log) + if (parsedLog && parsedLog.name === eventName) { + runId = hre.ethers.toNumber(parsedLog.args[eventArgIndex]) + } + } catch (error) { + // This log might not have been from your contract, or it might be an anonymous log + console.log("Could not parse log:", log) + } } - return { response: "", error: "Failed XX"}; + return runId; } \ No newline at end of file diff --git a/contracts/tasks/whitelist.ts b/contracts/tasks/whitelist.ts index 8197154..671211f 100644 --- a/contracts/tasks/whitelist.ts +++ b/contracts/tasks/whitelist.ts @@ -14,7 +14,7 @@ export const whitelistTask = task("whitelist", "Whitelists an address in the Ora ]; const [signer] = await hre.ethers.getSigners(); - const contract = new ethers.Contract(oracleContractAddress, contractABI, signer); + const contract = new hre.ethers.Contract(oracleContractAddress, contractABI, signer); console.log(`Whitelisting address: "${whitelistAddress}"...`); const updateTx = await contract.updateWhitelist(whitelistAddress, true); diff --git a/oracles/src/repositories/ipfs_repository.py b/oracles/src/repositories/ipfs_repository.py index 43f971f..64f8b64 100644 --- a/oracles/src/repositories/ipfs_repository.py +++ b/oracles/src/repositories/ipfs_repository.py @@ -11,7 +11,7 @@ async def read_file(self, cid: str, max_bytes: int = 0) -> bytes: headers = { "x-pinata-gateway-token": settings.PINATA_GATEWAY_TOKEN } - async with session.get(PINATA_LINK_BASE.format(cid)) as response: + async with session.get(PINATA_LINK_BASE.format(cid), headers=headers) as response: response.raise_for_status() data = bytearray() while True: