Skip to content

Commit

Permalink
Turn llm into an NFT
Browse files Browse the repository at this point in the history
whoami - a new endpoint that returns principal of caller in text format
http_request endpoint


handle preflight request (OPTIONS)
charles
NFTCollection
nft_mint ; http_request
Cleanup and fix all tests
  • Loading branch information
icppWorld committed Dec 23, 2023
1 parent f3d7b2d commit 8c0f7a5
Show file tree
Hide file tree
Showing 25 changed files with 25,283 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
Expand Down
46 changes: 43 additions & 3 deletions icpp_llama2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,31 @@
![icpp_llama2_without_limits](../assets/icpp_llama2_without_limits.png)
- To deploy all the LLM canisters and upload their models + tokenizers:
```bash
# Build WASM & deploy all the canisters listed in dfx.json
icpp build-wasm
dfx deploy
# Upload the models & tokenizers to the canisters
# Notes:
# (-) The storiesXX.bin files are not included in the github repo
# (-) See `Get a model checkpoint` above
# (-) The default tokenizer.bin is included in the github repo
# (-) See `stories260k` below how to build the tok512.bin for the stories260K model
#
python -m scripts.upload --canister llama2_260K --model stories260K/stories260K.bin --tokenizer stories260K/tok512.bin
python -m scripts.upload --canister llama2 --model models/stories15M.bin --tokenizer tokenizer/tokenizer.bin
python -m scripts.upload --canister llama2_42M --model models/stories42M.bin --tokenizer tokenizer/tokenizer.bin
python -m scripts.upload --canister llama2_110M --model models/stories110M.bin --tokenizer tokenizer/tokenizer.bin
```
# stories260K
The default model is`stories15M.bin`, with `tokenizer.bin`, which contains the default llama2 tokenizer using 32000 tokens.
The default model is `stories15M.bin`, with `tokenizer.bin`, which contains the default llama2 tokenizer using 32000 tokens.
For testing, it is nice to be able to work with a smaller model & tokenizer:
- Download the model & tokenizer from [huggingface stories260K](https://huggingface.co/karpathy/tinyllamas/tree/main/stories260K) and store them in:
Expand All @@ -61,16 +82,35 @@ For testing, it is nice to be able to work with a smaller model & tokenizer:
```
- Upload the model & tokenizer:
```bash
python -m scripts.upload --model stories260K/stories260K.bin --tokenizer stories260K/tok512.bin
python -m scripts.upload --canister llama2_260K --model stories260K/stories260K.bin --tokenizer stories260K/tok512.bin
```
- Inference is now possible with many more tokens before hitting the instruction limit, but off course, the stories are not as good:
```bash
$ dfx canister call llama2 inference '(record {prompt = "Lilly went swimming yesterday " : text; steps = 100 : nat64; temperature = 0.9 : float32; topp = 0.9 : float32; rng_seed = 0 : nat64;})'
# Create a new chat
$ dfx canister call llama2_260K new_chat '()'
# Start the chat by providing the starting prompt
$ dfx canister call llama2_260K inference '(record {prompt = "Lilly went swimming yesterday " : text; steps = 100 : nat64; temperature = 0.9 : float32; topp = 0.9 : float32; rng_seed = 0 : nat64;})'
(
variant {
ok = "Lilly went swimming yesterday order. She had a great eyes that was closed. One day, she asked her mom why the cloud was close to the pond. \n\"Mommy, I will take clothes away,\" Lila said. \"Th\n"
},
)
# Continue the current chat by calling again, with an empty prompt
$ dfx canister call llama2_260K inference '(record {prompt = "" : text; steps = 100 : nat64; temperature = 0.9 : float32; topp = 0.9 : float32; rng_seed = 0 : nat64;})'
(
variant {
ok = "eone replace it.\nThe fox agreed to go as fast as they set on the other birds. They searched, and it didn\'t give up. They started to scared the bird. The forest was so careful and jumped up."
},
)
# Retrieve your full story, by calling with curl, passing the principal by which the LLM knows you in the body
$ dfx canister call llama2_260K whoami
'("<your-principal>")'
$ curl -X GET -d '{"principal":"<your-principal>"}' http://localhost:$(dfx info webserver-port)?canisterId=$(dfx canister id llama2_260K)
```
# Fine tuning
Expand Down
3 changes: 3 additions & 0 deletions icpp_llama2/canister_ids.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"charles": {
"ic": "lkh5o-3yaaa-aaaag-acguq-cai"
},
"llama2": {
"ic": "4c4bn-daaaa-aaaag-abvcq-cai"
},
Expand Down
5 changes: 3 additions & 2 deletions icpp_llama2/demo.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ wsl --% dfx canister call llama2 health
#######################################################################
Write-Host " "
Write-Host "--------------------------------------------------"
Write-Host "Upload the model & tokenizer"
python -m scripts.upload
Write-Host "Upload the model & tokenizer for 260K and 15M"
python -m scripts.upload --canister llama2_260K --model stories260K/stories260K.bin --tokenizer stories260K/tok512.bin
python -m scripts.upload --canister llama2 --model models/stories15M.bin --tokenizer tokenizer/tokenizer.bin

#######################################################################
Write-Host " "
Expand Down
5 changes: 3 additions & 2 deletions icpp_llama2/demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ dfx canister call llama2 health
#######################################################################
echo " "
echo "--------------------------------------------------"
echo "Upload the model & tokenizer"
python -m scripts.upload
echo "Upload the model & tokenizer for 260K and 15M"
python -m scripts.upload --canister llama2_260K --model stories260K/stories260K.bin --tokenizer stories260K/tok512.bin
python -m scripts.upload --canister llama2 --model models/stories15M.bin --tokenizer tokenizer/tokenizer.bin

#######################################################################
echo " "
Expand Down
5 changes: 5 additions & 0 deletions icpp_llama2/dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
"type": "custom",
"candid": "src/llama2.did",
"wasm": "build/llama2.wasm"
},
"charles": {
"type": "custom",
"candid": "src/llama2.did",
"wasm": "build/llama2.wasm"
}
},
"defaults": {
Expand Down
27 changes: 12 additions & 15 deletions icpp_llama2/icpp.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
[build-wasm]
canister = "llama2"
did_path = "src/llama2.did"
cpp_paths = ["src/*.cpp"]
cpp_header_paths = ["src/*.h"]
cpp_compile_flags = []
cpp_link_flags = []
c_paths = ["src/run.c"]
c_header_paths = ["src/run.h"]
c_compile_flags = []
canister = "llama2"
did_path = "src/llama2.did"
cpp_paths = ["src/*.cpp"]
cpp_include_dirs = ["src/vendors/*"]
cpp_compile_flags = ["-D JSON_HAS_FILESYSTEM=0"]
cpp_link_flags = []
c_paths = ["src/run.c"]
c_compile_flags = []
[build-native]
cpp_paths = ["native/main.cpp"]
cpp_header_paths = ["native/main.h"]
cpp_paths = ["native/main.cpp"]
cpp_compile_flags = []
cpp_link_flags = []
c_paths = []
c_header_paths = []
c_compile_flags = []
cpp_link_flags = []
c_paths = []
c_compile_flags = []
85 changes: 82 additions & 3 deletions icpp_llama2/native/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@

#include "../src/canister.h"
#include "../src/chats.h"
#include "../src/http.h"
#include "../src/inference.h"
#include "../src/initialize.h"
#include "../src/nfts.h"
#include "../src/upload.h"
#include "../src/users.h"

Expand Down Expand Up @@ -66,7 +68,7 @@ int main() {
bool silent_on_trap = true;

// The model & tokenizer to use
int model_to_use = 2; // 1=260K, 2=15M, 3=42M, 4=110M (TinyStories)
int model_to_use = 1; // 1=260K, 2=15M, 3=42M, 4=110M (TinyStories)

std::string model_path;
std::string tokenizer_path;
Expand Down Expand Up @@ -177,6 +179,12 @@ int main() {
mockIC.run_test("ready", ready, "4449444c0000", "4449444c00017e00",
silent_on_trap, anonymous_principal);

// '()' -> '("expmt-gtxsw-inftj-ttabj-qhp5s-nozup-n3bbo-k7zvn-dg4he-knac3-lae")'
mockIC.run_test(
"whoami", whoami, "4449444c0000",
"4449444c0001713f6578706d742d67747873772d696e66746a2d747461626a2d71687035732d6e6f7a75702d6e3362626f2d6b377a766e2d64673468652d6b6e6163332d6c6165",
silent_on_trap, my_principal);

// '()' -> '(variant { err = 401 : nat16 })'
// Call with non owner principal must be denied
mockIC.run_test("reset_model err", reset_model, "4449444c0000",
Expand Down Expand Up @@ -461,7 +469,7 @@ int main() {
// '()' -> '(variant { ok = 200 : nat16 })'
mockIC.run_test("new_chat", new_chat, "4449444c0000",
"4449444c016b019cc2017a010000c800", silent_on_trap,
my_principal);
your_principal);

// Loop to create 1000 token long story, 10 tokens at a time
// With temperature=0.0: greedy argmax sampling -> the story will be the same every time
Expand All @@ -484,7 +492,7 @@ int main() {

std::string candid_out;
mockIC.run_test("inference 0b", inference, candid_in, "", silent_on_trap,
my_principal, &candid_out);
your_principal, &candid_out);

std::string err_text;
CandidTypeVariant v_out;
Expand All @@ -505,6 +513,77 @@ int main() {
std::cout << story;
}

// ------------------------------------------------------------------------
// Mint current conversation of 'your_principal'

// Verify it traps when not owner of the canister
// '(record {nft_owner = principal "2ibo7-dia"})' -> trap
mockIC.run_trap_test("nft_mint trap test", nft_mint,
"4449444c016c01b08bb1cf07680100010100", silent_on_trap,
your_principal);

// '(record {nft_owner = principal "2ibo7-dia"})'
// ->
// (
// record {
// 51_777_086 = 1 : nat; // nft_id
// 2_045_527_472 = principal "2ibo7-dia"; // nft_owner
// 3_314_270_578 = "Once upon a time, ..."; // nft_story
// },
// )
expected_response = "-to-do-";
if (model_to_use == 1) {
expected_response =
"4449444c016c03be9cd8187db08bb1cf0768f282afac0c7101000101010087064f6e63652075706f6e20612074696d652c207468657265207761732061206c6974746c65206769726c206e616d6564204c696c792e20536865206c6f76656420746f20706c6179206f75747369646520696e20746865207061726b2e204f6e65206461792c20736865207361772061206269672c207265642062616c6c2e205368652077616e74656420746f20706c617920776974682069742c206275742069742077617320746f6f20686967682e0a4c696c792773206d6f6d20736169642c20224c696c792c206c6574277320676f20746f20746865207061726b2e22204c696c79207761732073616420616e64206469646e2774206b6e6f77207768617420746f20646f2e2053686520736169642c2022492077616e7420746f20706c6179207769746820796f75722062616c6c2c2062757420492063616e27742066696e642069742e220a4c696c79207761732073616420616e64206469646e2774206b6e6f77207768617420746f20646f2e2053686520736169642c202249276d20736f7272792c204c696c792e2049206469646e2774206b6e6f77207768617420746f20646f2e220a4c696c79206469646e27742077616e7420746f2068656c7020686572206d6f6d2c20736f2073686520736169642c202249276d20736f7272792c206d6f6d2e2049206469646e2774206b6e6f77207768617420746f20646f2e2220486572206d6f6d20736169642c2022446f6e277420776f7272792c204c696c792e2057652063616e2068656c7020796f752e220a4c696c7920616e6420686572206d6f6d2077656e7420746f20746865207061726b20746f20706c61792e205468657920706c6179656420746f67657468657220616e64206861642066756e2e2041667465722061207768696c652c204c696c792773206d6f6d2063616d6520696e746f2074686520726f6f6d20616e642073617720746865207061726b2e205368652077617320736f20686170707920616e6420736169642c20225468616e6b20796f752c204c696c792120596f7520617265206120676f6f6420667269656e642e22";
} else if (model_to_use == 2) {
} else if (model_to_use == 3) {
} else if (model_to_use == 4) {
}
mockIC.run_test("nft_mint", nft_mint, "4449444c016c01b08bb1cf07680100010100",
expected_response, silent_on_trap, my_principal);

// ------------------------------------------------------------------------
// Call to the http_request endpoint, to get the story of nft with id "1"
// (
// record { // IC_HttpRequest
// 5_843_823 = "/api/nft/1"; // url
// 156_956_385 = "GET"; // method
// 1_092_319_906 = vec {}; // body
// 1_661_489_734 = vec { record {...} }; // headers
// 1_661_892_784 = opt (2 : nat16); // certificate_version
// },
// )
// I encoded the anonimized version of an actual request to MAINNET:
// '(record { 5_843_823 = "/api/nft/1"; 156_956_385 = "GET"; 1_092_319_906 = blob "{}"; 1_661_489_734 = vec { record { "host"; "xxxxx-xxxxx-xxxxx-xxxxx-cai.icp0.io";}; record { "x-real-ip"; "xx.xx.xxx.xxx";}; record { "x-forwarded-for"; "xx.xx.xxx.xxx";}; record { "x-forwarded-proto"; "https";}; record { "x-request-id"; "15aad0a6-e422-26e5-8992-c3fe477ffedd";}; record { "x-icx-require-certification"; "1";}; record { "pragma"; "no-cache";}; record { "cache-control"; "no-cache";}; record { "sec-ch-ua"; "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Brave\";v=\"120\"";}; record { "sec-ch-ua-mobile"; "?0";}; record { "user-agent"; "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";}; record { "sec-ch-ua-platform"; "\"Linux\"";}; record { "accept"; "*/*";}; record { "sec-gpc"; "1";}; record { "origin"; "null";}; record { "sec-fetch-site"; "cross-site";}; record { "sec-fetch-mode"; "cors";}; record { "sec-fetch-dest"; "empty";}; record { "accept-encoding"; "gzip, deflate, br, identity";}; record { "accept-language"; "en-US,en;q=0.9";};}; 1_661_892_784 = opt (2 : nat16); }, )'
//
// Note: Encoding this gives an error on the empty vec, not sure yet why:
// '(record { 5_843_823 = "/api/nft/1"; 156_956_385 = "GET"; 1_092_319_906 = vec {}; 1_661_489_734 = vec { record { "host"; "xxxxx-xxxxx-xxxxx-xxxxx-cai.icp0.io";}; record { "x-real-ip"; "xx.xx.xxx.xxx";}; record { "x-forwarded-for"; "xx.xx.xxx.xxx";}; record { "x-forwarded-proto"; "https";}; record { "x-request-id"; "15aad0a6-e422-26e5-8992-c3fe477ffedd";}; record { "x-icx-require-certification"; "1";}; record { "pragma"; "no-cache";}; record { "cache-control"; "no-cache";}; record { "sec-ch-ua"; "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Brave\";v=\"120\"";}; record { "sec-ch-ua-mobile"; "?0";}; record { "user-agent"; "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";}; record { "sec-ch-ua-platform"; "\"Linux\"";}; record { "accept"; "*/*";}; record { "sec-gpc"; "1";}; record { "origin"; "null";}; record { "sec-fetch-site"; "cross-site";}; record { "sec-fetch-mode"; "cors";}; record { "sec-fetch-dest"; "empty";}; record { "accept-encoding"; "gzip, deflate, br, identity";}; record { "accept-language"; "en-US,en;q=0.9";};}; 1_661_892_784 = opt (2 : nat16); }, )'
//
// -> Returns an IC_HttpResponse
// (
// record { // IC_HttpResponse
// 1_092_319_906 = blob "{\22story\22:\22Once upon a time, ...\5c\22\22}"; // body
// 1_661_489_734 = vec { // headers
// record { "Content-Type"; "application/json" };
// record { "Content-Length"; "803" };
// };
// 1_664_201_884 = opt false; // upgrade
// 3_475_804_314 = 200 : nat16; // status_code
// },
// )
expected_response = "-to-do-";
if (model_to_use == 1) {
expected_response =
"4449444c0a6c02000101016d716c006c02007101716d036c02007101716c02007101716c04a2f5ed880408c6a4a19806049ce9c69906099aa1b2f90c7a6d7b6e7e0107a3067b2273746f7279223a224f6e63652075706f6e20612074696d652c207468657265207761732061206c6974746c65206769726c206e616d6564204c696c792e20536865206c6f76656420746f20706c6179206f75747369646520696e20746865207061726b2e204f6e65206461792c20736865207361772061206269672c207265642062616c6c2e205368652077616e74656420746f20706c617920776974682069742c206275742069742077617320746f6f20686967682e5c6e4c696c792773206d6f6d20736169642c205c224c696c792c206c6574277320676f20746f20746865207061726b2e5c22204c696c79207761732073616420616e64206469646e2774206b6e6f77207768617420746f20646f2e2053686520736169642c205c22492077616e7420746f20706c6179207769746820796f75722062616c6c2c2062757420492063616e27742066696e642069742e5c225c6e4c696c79207761732073616420616e64206469646e2774206b6e6f77207768617420746f20646f2e2053686520736169642c205c2249276d20736f7272792c204c696c792e2049206469646e2774206b6e6f77207768617420746f20646f2e5c225c6e4c696c79206469646e27742077616e7420746f2068656c7020686572206d6f6d2c20736f2073686520736169642c205c2249276d20736f7272792c206d6f6d2e2049206469646e2774206b6e6f77207768617420746f20646f2e5c2220486572206d6f6d20736169642c205c22446f6e277420776f7272792c204c696c792e2057652063616e2068656c7020796f752e5c225c6e4c696c7920616e6420686572206d6f6d2077656e7420746f20746865207061726b20746f20706c61792e205468657920706c6179656420746f67657468657220616e64206861642066756e2e2041667465722061207768696c652c204c696c792773206d6f6d2063616d6520696e746f2074686520726f6f6d20616e642073617720746865207061726b2e205368652077617320736f20686170707920616e6420736169642c205c225468616e6b20796f752c204c696c792120596f7520617265206120676f6f6420667269656e642e5c22227d020c436f6e74656e742d54797065106170706c69636174696f6e2f6a736f6e0e436f6e74656e742d4c656e677468033830330100c800";
} else if (model_to_use == 2) {
} else if (model_to_use == 3) {
} else if (model_to_use == 4) {
}
mockIC.run_test(
"http_request anonimized with blob body", http_request,
"4449444c056c05efd6e40271e1edeb4a71a2f5ed880401c6a4a1980602b0f1b99806046d7b6d036c02007101716e7a01000a2f6170692f6e66742f3103474554027b7d1404686f73742378787878782d78787878782d78787878782d78787878782d6361692e696370302e696f09782d7265616c2d69700d78782e78782e7878782e7878780f782d666f727761726465642d666f720d78782e78782e7878782e78787811782d666f727761726465642d70726f746f0568747470730c782d726571756573742d69642431356161643061362d653432322d323665352d383939322d6333666534373766666564641b782d6963782d726571756972652d63657274696669636174696f6e013106707261676d61086e6f2d63616368650d63616368652d636f6e74726f6c086e6f2d6361636865097365632d63682d756138224e6f745f41204272616e64223b763d2238222c20224368726f6d69756d223b763d22313230222c20224272617665223b763d2231323022107365632d63682d75612d6d6f62696c65023f300a757365722d6167656e74654d6f7a696c6c612f352e3020285831313b204c696e7578207838365f363429204170706c655765624b69742f3533372e333620284b48544d4c2c206c696b65204765636b6f29204368726f6d652f3132302e302e302e30205361666172692f3533372e3336127365632d63682d75612d706c6174666f726d07224c696e75782206616363657074032a2f2a077365632d6770630131066f726967696e046e756c6c0e7365632d66657463682d736974650a63726f73732d736974650e7365632d66657463682d6d6f646504636f72730e7365632d66657463682d6465737405656d7074790f6163636570742d656e636f64696e671b677a69702c206465666c6174652c2062722c206964656e746974790f6163636570742d6c616e67756167650e656e2d55532c656e3b713d302e39010200",
expected_response, silent_on_trap, my_principal);

// -----------------------------------------------------------------------------------------
// A new chat
// '()' -> '(variant { ok = 200 : nat16 })'
Expand Down
Loading

0 comments on commit 8c0f7a5

Please sign in to comment.