Skip to content

Commit

Permalink
Merge pull request #117 from galadriel-ai/kristjan/openai-simple-llm-…
Browse files Browse the repository at this point in the history
…chat

OpenAI simple LLM contract and example script
  • Loading branch information
kristjanpeterson1 authored Jul 5, 2024
2 parents 35b14a0 + 9ce7244 commit b2b0c3e
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 0 deletions.
73 changes: 73 additions & 0 deletions contracts/contracts/OpenAiSimpleLlm.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "./interfaces/IOracle.sol";

contract OpenAiSimpleLLM {
address private oracleAddress; // use latest: https://docs.galadriel.com/oracle-address
IOracle.Message public message;
string public response;
IOracle.OpenAiRequest private config;

constructor(address initialOracleAddress) {
oracleAddress = initialOracleAddress;

config = IOracle.OpenAiRequest({
model : "gpt-4-turbo", // gpt-4-turbo gpt-4o
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
});
}

function sendMessage(string memory _message) public {
message = createTextMessage("user", _message);
IOracle(oracleAddress).createOpenAiLlmCall(0, config);
}

// required for Oracle
function onOracleOpenAiLlmResponse(
uint runId,
IOracle.OpenAiResponse memory _response,
string memory _errorMessage
) public {
require(msg.sender == oracleAddress, "Caller is not oracle");
if (bytes(_errorMessage).length > 0) {
response = _errorMessage;
} else {
response = _response.content;
}
}

// required for Oracle
function getMessageHistory(
uint /*_runId*/
) public view returns (IOracle.Message[] memory) {
IOracle.Message[] memory messages = new IOracle.Message[](1);
messages[0] = message;
return messages;
}

// @notice Creates a text message with the given role and content
// @param role The role of the message
// @param content The content of the message
// @return The created message
function createTextMessage(string memory role, string memory content) private pure returns (IOracle.Message memory) {
IOracle.Message memory newMessage = IOracle.Message({
role : role,
content : new IOracle.Content[](1)
});
newMessage.content[0].contentType = "text";
newMessage.content[0].value = content;
return newMessage;
}
}
1 change: 1 addition & 0 deletions examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "",
"scripts": {
"chat": "ts-node chat.ts",
"simpleChat": "ts-node simpleLlmChat.ts",
"chat_vision": "ts-node chat_vision.ts",
"chat_anthropic": "ts-node chat_anthropic.ts",
"agent": "ts-node agent.ts"
Expand Down
67 changes: 67 additions & 0 deletions examples/simpleLlmChat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {Contract, ethers, TransactionReceipt, Wallet} from "ethers";
import ABI from "./abis/dAGILLM.json";
import * as readline from 'readline';

require("dotenv").config()

async function main() {
const rpcUrl = process.env.RPC_URL
if (!rpcUrl) throw Error("Missing RPC_URL in .env")
const privateKey = process.env.PRIVATE_KEY
if (!privateKey) throw Error("Missing PRIVATE_KEY in .env")
const contractAddress = process.env.SIMPLE_LLM_CONTRACT_ADDRESS
if (!contractAddress) throw Error("Missing SIMPLE_LLM_CONTRACT_ADDRESS in .env")

const provider = new ethers.JsonRpcProvider(rpcUrl)
const wallet = new Wallet(
privateKey, provider
)
const contract = new Contract(contractAddress, ABI, wallet)

// The message you want to start the chat with
const message = await getUserInput()

// Call the sendMessage function
const transactionResponse = await contract.sendMessage(message)
const receipt = await transactionResponse.wait()
console.log(`Message sent, tx hash: ${receipt.hash}`)
console.log(`Chat started with message: "${message}"`)

// Read the LLM response on-chain
while (true) {
const response = await contract.response();
if (response) {
console.log("Response from contract:", response);
break;
}
await new Promise(resolve => setTimeout(resolve, 2000))
}
}

async function getUserInput(): Promise<string | undefined> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})

const question = (query: string): Promise<string> => {
return new Promise((resolve) => {
rl.question(query, (answer) => {
resolve(answer)
})
})
}

try {
const input = await question("Message ChatGPT: ")
rl.close()
return input
} catch (err) {
console.error('Error getting user input:', err)
rl.close()
}
}


main()
.then(() => console.log("Done"))

0 comments on commit b2b0c3e

Please sign in to comment.