-
Notifications
You must be signed in to change notification settings - Fork 30
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 #117 from galadriel-ai/kristjan/openai-simple-llm-…
…chat OpenAI simple LLM contract and example script
- Loading branch information
Showing
3 changed files
with
141 additions
and
0 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
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; | ||
} | ||
} |
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,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")) |