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

Add ChatGPT protocol #42

Merged
merged 4 commits into from Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Expand Up @@ -19,7 +19,7 @@ jobs:

- name: Run suave
run: |
suave-geth --suave.dev &
suave-geth --suave.dev --suave.eth.external-whitelist='*' &

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
Expand All @@ -30,4 +30,6 @@ jobs:
run: forge install

- name: Run tests
env:
CHATGPT_API_KEY: ${{ secrets.CHATGPT_API_KEY }}
run: forge test --ffi
19 changes: 19 additions & 0 deletions README.md
Expand Up @@ -84,6 +84,25 @@ contract Example {
}
```

### protocols/ChatGPT.sol

Helper library to send completion requests to ChatGPT.

#### Example usage

```solidity
import "suave-std/protocols/ChatGPT.sol";

contract Example {
function example() {
ChatGPT.Message[] memory messages = new ChatGPT.Message[](1);
messages[0] = ChatGPT.Message(ChatGPT.Role.User, "How do I write a Suapp with suave-std?");

chatgpt.complete(messages);
}
}
```

## Forge integration

In order to use `forge`, you need to have a running `Suave` node and the `suave` binary in your path.
Expand Down
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 55 files
+1 −0 .gitattributes
+134 −0 .github/workflows/ci.yml
+29 −0 .github/workflows/sync.yml
+0 −21 .github/workflows/tests.yml
+2 −0 .gitignore
+1 −1 LICENSE-APACHE
+1 −1 LICENSE-MIT
+42 −18 README.md
+21 −0 foundry.toml
+1 −1 lib/ds-test
+16 −0 package.json
+635 −0 scripts/vm.py
+35 −0 src/Base.sol
+27 −0 src/Script.sol
+376 −0 src/StdAssertions.sol
+248 −0 src/StdChains.sol
+817 −0 src/StdCheats.sol
+15 −0 src/StdError.sol
+107 −0 src/StdInvariant.sol
+183 −0 src/StdJson.sol
+43 −0 src/StdMath.sol
+378 −0 src/StdStorage.sol
+333 −0 src/StdStyle.sol
+226 −0 src/StdUtils.sol
+32 −370 src/Test.sol
+1,109 −59 src/Vm.sol
+1 −0 src/console.sol
+1,558 −0 src/console2.sol
+105 −0 src/interfaces/IERC1155.sol
+12 −0 src/interfaces/IERC165.sol
+43 −0 src/interfaces/IERC20.sol
+190 −0 src/interfaces/IERC4626.sol
+164 −0 src/interfaces/IERC721.sol
+73 −0 src/interfaces/IMulticall3.sol
+216 −0 src/mocks/MockERC20.sol
+221 −0 src/mocks/MockERC721.sol
+13,248 −0 src/safeconsole.sol
+0 −129 src/test/StdCheats.t.sol
+0 −276 src/test/StdStorage.t.sol
+1,015 −0 test/StdAssertions.t.sol
+216 −0 test/StdChains.t.sol
+610 −0 test/StdCheats.t.sol
+16 −19 test/StdError.t.sol
+212 −0 test/StdMath.t.sol
+315 −0 test/StdStorage.t.sol
+110 −0 test/StdStyle.t.sol
+342 −0 test/StdUtils.t.sol
+15 −0 test/Vm.t.sol
+10 −0 test/compilation/CompilationScript.sol
+10 −0 test/compilation/CompilationScriptBase.sol
+10 −0 test/compilation/CompilationTest.sol
+10 −0 test/compilation/CompilationTestBase.sol
+187 −0 test/fixtures/broadcast.log.json
+441 −0 test/mocks/MockERC20.t.sol
+721 −0 test/mocks/MockERC721.t.sol
76 changes: 76 additions & 0 deletions src/protocols/ChatGPT.sol
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;

import "src/suavelib/Suave.sol";
import "solady/src/utils/JSONParserLib.sol";

contract ChatGPT {
using JSONParserLib for *;

string apiKey;
dmarzzz marked this conversation as resolved.
Show resolved Hide resolved

enum Role {
User,
System
}

struct Message {
Role role;
string content;
}

constructor(string memory _apiKey) {
apiKey = _apiKey;
}

// https://platform.openai.com/docs/api-reference/making-requests
function complete(Message[] memory messages) public returns (string memory) {
bytes memory body;
body = abi.encodePacked('{"model": "gpt-3.5-turbo", "messages": [');
for (uint256 i = 0; i < messages.length; i++) {
body = abi.encodePacked(
body,
'{"role": "',
messages[i].role == Role.User ? "user" : "system",
'", "content": "',
messages[i].content,
'"}'
);
if (i < messages.length - 1) {
body = abi.encodePacked(body, ",");
}
}
body = abi.encodePacked(body, '], "temperature": 0.7}');

Suave.HttpRequest memory request;
request.method = "POST";
request.url = "https://api.openai.com/v1/chat/completions";
request.headers = new string[](2);
request.headers[0] = string.concat("Authorization: Bearer ", apiKey);
request.headers[1] = "Content-Type: application/json";
request.body = body;

bytes memory output = Suave.doHTTPRequest(request);

// decode responses
JSONParserLib.Item memory item = string(output).parse();
string memory result = trimQuotes(item.at('"choices"').at(0).at('"message"').at('"content"').value());

return result;
}

function trimQuotes(string memory input) private pure returns (string memory) {
bytes memory inputBytes = bytes(input);
require(
inputBytes.length >= 2 && inputBytes[0] == '"' && inputBytes[inputBytes.length - 1] == '"', "Invalid input"
);

bytes memory result = new bytes(inputBytes.length - 2);

for (uint256 i = 1; i < inputBytes.length - 1; i++) {
result[i - 1] = inputBytes[i];
}

return string(result);
}
}
29 changes: 29 additions & 0 deletions test/protocols/ChatGPT.t.sol
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "src/Test.sol";
import "src/protocols/ChatGPT.sol";

contract ChatGPTTest is Test, SuaveEnabled {
function testChatGPT() public {
ChatGPT chatgpt = getChatGPT();

ChatGPT.Message[] memory messages = new ChatGPT.Message[](1);
messages[0] = ChatGPT.Message(ChatGPT.Role.User, "Say this is a test!");

string memory expected = "This is a test!";
string memory found = chatgpt.complete(messages);

assertEq(found, expected, "ChatGPT did not return the expected result");
}

function getChatGPT() public returns (ChatGPT chatgpt) {
// NOTE: tried to do it with envOr but it did not worked
try vm.envString("CHATGPT_API_KEY") returns (string memory apiKey) {
chatgpt = new ChatGPT(apiKey);
} catch {
vm.skip(true);
}
}
}