-
Notifications
You must be signed in to change notification settings - Fork 0
/
Oracle.sol
217 lines (188 loc) · 8.08 KB
/
Oracle.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
// Uncomment this line to use console.log
// import "hardhat/console.sol";
import "./interfaces/IOracle.sol";
// @title OracleAgent
// @notice This contract handles chat interactions and integrates with teeML oracle for LLM and knowledge base queries.
contract OracleAgent {
struct Message {
string role;
string content;
}
struct ChatRun {
address owner;
IOracle.Message[] messages;
uint messagesCount;
}
// @notice Mapping from chat ID to ChatRun
mapping(uint => ChatRun) public chatRuns;
uint private chatRunsCount;
// @notice Event emitted when a new chat is created
event ChatCreated(address indexed owner, uint indexed chatId);
// @notice Address of the contract owner
address private owner;
// @notice Address of the oracle contract
address public oracleAddress;
// @notice CID of the knowledge base
string public knowledgeBase;
// @notice Event emitted when the oracle address is updated
event OracleAddressUpdated(address indexed newOracleAddress);
// @notice Configuration for the Groq request
IOracle.GroqRequest private config;
// @param initialOracleAddress Initial address of the oracle contract
// @param initialOracleAddress Initial address of the oracle contract
constructor(address initialOracleAddress) {
owner = msg.sender;
oracleAddress = initialOracleAddress;
chatRunsCount = 0;
config = IOracle.GroqRequest({
model: "llama-3.1-8b-instant",
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
});
}
// @notice Ensures the caller is the contract owner
modifier onlyOwner() {
require(msg.sender == owner, "Caller is not owner");
_;
}
// @notice Ensures the caller is the oracle contract
modifier onlyOracle() {
require(msg.sender == oracleAddress, "Caller is not oracle");
_;
}
// @notice Sets a new oracle address
// @param newOracleAddress The new oracle address
function setOracleAddress(address newOracleAddress) public onlyOwner {
oracleAddress = newOracleAddress;
emit OracleAddressUpdated(newOracleAddress);
}
function RunOracleNPC() public returns (uint i) {
string
memory query = "You are an NPC whose name is Ava 'Oracle' Donovan. \n Renowned NFT artist Elysia Solis has been found dead under suspicious circumstances, her final work left unfinished. \n Characteristic: A former blockchain journalist turned investigative reporter, Ava was working on an expose about the dark side of the NFT world. She had recently discovered some disturbing truths about Elysia's past dealings and was planning to confront her. Ava is driven by a quest for truth, but her methods are often aggressive, and she has a history of crossing ethical lines to get her story. \n You are suspect and you always distract the investigator with some random news of crypto and start talking about it. \n You try you best to change the topic and not get caught. \n Task: \n You will act like the NPC character AT ALL TIMES. Keep your response concise within 50 words";
return startChat(query);
}
// @notice Starts a new chat
// @param message The initial message to start the chat with
// @return The ID of the newly created chat
function startChat(string memory message) public returns (uint) {
ChatRun storage run = chatRuns[chatRunsCount];
run.owner = msg.sender;
IOracle.Message memory newMessage = createTextMessage("user", message);
run.messages.push(newMessage);
run.messagesCount = 1;
uint currentId = chatRunsCount;
chatRunsCount = chatRunsCount + 1;
IOracle(oracleAddress).createGroqLlmCall(currentId, config);
emit ChatCreated(msg.sender, currentId);
return currentId;
}
// @notice Handles the response from the oracle for a Groq LLM call
// @param runId The ID of the chat run
// @param response The response from the oracle
// @param errorMessage Any error message
// @dev Called by teeML oracle
function onOracleGroqLlmResponse(
uint runId,
IOracle.GroqResponse memory response,
string memory errorMessage
) public onlyOracle {
ChatRun storage run = chatRuns[runId];
require(
keccak256(
abi.encodePacked(run.messages[run.messagesCount - 1].role)
) == keccak256(abi.encodePacked("user")),
"No message to respond to"
);
if (!compareStrings(errorMessage, "")) {
IOracle.Message memory newMessage = createTextMessage(
"assistant",
errorMessage
);
run.messages.push(newMessage);
run.messagesCount++;
} else {
IOracle.Message memory newMessage = createTextMessage(
"assistant",
response.content
);
run.messages.push(newMessage);
run.messagesCount++;
}
}
// @notice Adds a new message to an existing chat run
// @param message The new message to add
// @param runId The ID of the chat run
function addMessage(string memory message, uint runId) public {
ChatRun storage run = chatRuns[runId];
require(
keccak256(
abi.encodePacked(run.messages[run.messagesCount - 1].role)
) == keccak256(abi.encodePacked("assistant")),
"No response to previous message"
);
require(run.owner == msg.sender, "Only chat owner can add messages");
IOracle.Message memory newMessage = createTextMessage("user", message);
run.messages.push(newMessage);
run.messagesCount++;
IOracle(oracleAddress).createGroqLlmCall(runId, config);
}
// @notice Retrieves the message history of a chat run
// @param chatId The ID of the chat run
// @return An array of messages
// @dev Called by teeML oracle
function getMessageHistory(
uint chatId
) public view returns (IOracle.Message[] memory) {
return chatRuns[chatId].messages;
}
// @notice Retrieves the roles of the messages in a chat run
// @param chatId The ID of the chat run
// @return An array of message roles
// @dev Called by teeML oracle
function getMessageHistoryRoles(
uint chatId
) public view returns (string[] memory) {
string[] memory roles = new string[](chatRuns[chatId].messages.length);
for (uint i = 0; i < chatRuns[chatId].messages.length; i++) {
roles[i] = chatRuns[chatId].messages[i].role;
}
return roles;
}
// @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;
}
// @notice Compares two strings for equality
// @param a The first string
// @param b The second string
// @return True if the strings are equal, false otherwise
function compareStrings(
string memory a,
string memory b
) private pure returns (bool) {
return (keccak256(abi.encodePacked((a))) ==
keccak256(abi.encodePacked((b))));
}
}