diff --git a/.gitignore b/.gitignore index fc98d44..2d90ce3 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,10 @@ venv # sync scripts remote_sync + +# forge generated files +**/broadcast +**/out + +# secrets +*-key.json diff --git a/.gitmodules b/.gitmodules index 89b8d8b..571554a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,16 +3,16 @@ url = https://github.com/foundry-rs/forge-std [submodule "projects/hello-world/contracts/lib/infernet-sdk"] path = projects/hello-world/contracts/lib/infernet-sdk - url = https://github.com/ritual-net/infernet-sdk + url = https://github.com/ritual-net/infernet-sdk.git [submodule "projects/torch-iris/contracts/lib/infernet-sdk"] path = projects/torch-iris/contracts/lib/infernet-sdk - url = https://github.com/ritual-net/infernet-sdk + url = https://github.com/ritual-net/infernet-sdk.git [submodule "projects/torch-iris/contracts/lib/forge-std"] path = projects/torch-iris/contracts/lib/forge-std url = https://github.com/foundry-rs/forge-std [submodule "projects/onnx-iris/contracts/lib/infernet-sdk"] path = projects/onnx-iris/contracts/lib/infernet-sdk - url = https://github.com/ritual-net/infernet-sdk + url = https://github.com/ritual-net/infernet-sdk.git [submodule "projects/onnx-iris/contracts/lib/forge-std"] path = projects/onnx-iris/contracts/lib/forge-std url = https://github.com/foundry-rs/forge-std @@ -21,13 +21,13 @@ url = https://github.com/foundry-rs/forge-std [submodule "projects/prompt-to-nft/contracts/lib/infernet-sdk"] path = projects/prompt-to-nft/contracts/lib/infernet-sdk - url = https://github.com/ritual-net/infernet-sdk + url = https://github.com/ritual-net/infernet-sdk.git [submodule "projects/prompt-to-nft/contracts/lib/solmate"] path = projects/prompt-to-nft/contracts/lib/solmate url = https://github.com/transmissions11/solmate [submodule "projects/gpt4/contracts/lib/infernet-sdk"] path = projects/gpt4/contracts/lib/infernet-sdk - url = https://github.com/ritual-net/infernet-sdk + url = https://github.com/ritual-net/infernet-sdk.git [submodule "projects/gpt4/contracts/lib/forge-std"] path = projects/gpt4/contracts/lib/forge-std url = https://github.com/foundry-rs/forge-std @@ -36,4 +36,10 @@ url = https://github.com/foundry-rs/forge-std [submodule "projects/tgi-llm/contracts/lib/infernet-sdk"] path = projects/tgi-llm/contracts/lib/infernet-sdk + url = https://github.com/ritual-net/infernet-sdk.git +[submodule "projects/payment/contracts/lib/forge-std"] + path = projects/payment/contracts/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "projects/payment/contracts/lib/infernet-sdk"] + path = projects/payment/contracts/lib/infernet-sdk url = https://github.com/ritual-net/infernet-sdk diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c001448 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +- ##### The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +- ##### This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2024-06-06 + +### Added +- New project `payment` for an end-to-end flow of the payments feature of `infernet + 1.0.0`. + +### Changed +- All workflows are updated to use `infernet-ml 1.0.0` +- All contracts are updated to use `infernet-sdk 1.0.0` + +### Fixed +- Recursive submodule cloning issue with forge's libraries. + +## [0.1.0] - 2024-03-21 + +### Added +- Initial release of the Infernet Container Starter repository. diff --git a/Makefile b/Makefile index f9730cc..cee1735 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,9 @@ -build-container: - $(MAKE) -C ./projects/$(project)/container build +include internal.mk + +index_url ?= '' + +build-container: get_index_url + $(MAKE) -C ./projects/$(project)/container build index_url=$(index_url) remove-containers: docker compose -f deploy/docker-compose.yaml down || true @@ -8,10 +12,18 @@ remove-containers: build-multiplatform: $(MAKE) -C ./projects/$(project)/container build-multiplatform -deploy-container: - $(MAKE) remove-containers +deploy-container: stop-container cp ./projects/$(project)/container/config.json deploy/config.json docker compose -f deploy/docker-compose.yaml up -d + docker logs infernet-node -f + +stop-container: + docker compose -f deploy/docker-compose.yaml kill || true + docker compose -f deploy/docker-compose.yaml rm -f || true + docker kill $(project) || true + docker rm $(project) || true + +watch-logs: docker compose -f deploy/docker-compose.yaml logs -f deploy-contracts: diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 0000000..3667609 --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,6 @@ + +## Get Rid Of +- [ ] build versions in libraries +- [ ] Node version in dockerfiles +- [ ] `get_index_url`, `index_url` can stay +- [ ] change ci not to use extra index url (requires pypi release) diff --git a/deploy/docker-compose.yaml b/deploy/docker-compose.yaml index 6cc532c..e0be765 100644 --- a/deploy/docker-compose.yaml +++ b/deploy/docker-compose.yaml @@ -2,28 +2,30 @@ version: '3' services: node: - image: ritualnetwork/infernet-node:latest + image: ritualnetwork/infernet-node:1.0.0 ports: - "0.0.0.0:4000:4000" volumes: - ./config.json:/app/config.json - node-logs:/logs - /var/run/docker.sock:/var/run/docker.sock + tty: true networks: - network depends_on: - redis + - infernet-anvil restart: on-failure extra_hosts: - "host.docker.internal:host-gateway" stop_grace_period: 1m - tty: true + container_name: infernet-node redis: image: redis:latest - expose: - - "6379" + ports: + - "6379:6379" networks: - network volumes: @@ -46,10 +48,18 @@ services: restart: on-failure + infernet-anvil: + image: ritualnetwork/infernet-anvil:1.0.0 + command: --host 0.0.0.0 --port 3000 --load-state infernet_deployed.json -b 1 + ports: + - "8545:3000" + networks: + - network + container_name: infernet-anvil + networks: network: - volumes: node-logs: redis-data: diff --git a/internal.mk b/internal.mk new file mode 100644 index 0000000..e94a24f --- /dev/null +++ b/internal.mk @@ -0,0 +1,10 @@ +ifneq ("$(wildcard gcp.env)","") +include gcp.env +endif + +get_index_url: + $(eval token := $(shell gcloud auth print-access-token)) + $(eval index_url := "https://_token:$(token)@$(artifact_location)-python.pkg.dev/$(gcp_project)/$(artifact_repo)/simple") + +generate-uv-env-file: get_index_url + @echo "`echo $(export_prefix)`UV_EXTRA_INDEX_URL=$(index_url)" > uv.env diff --git a/projects/gpt4/container/Dockerfile b/projects/gpt4/container/Dockerfile index 57add3f..92d4f77 100644 --- a/projects/gpt4/container/Dockerfile +++ b/projects/gpt4/container/Dockerfile @@ -7,12 +7,15 @@ ENV PYTHONDONTWRITEBYTECODE 1 ENV PIP_NO_CACHE_DIR 1 ENV RUNTIME docker ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} RUN apt-get update RUN apt-get install -y git curl # install uv -ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh +ADD https://astral.sh/uv/install.sh /install.sh +RUN chmod 755 /install.sh RUN /install.sh && rm /install.sh COPY src/requirements.txt . diff --git a/projects/gpt4/container/Makefile b/projects/gpt4/container/Makefile index 0f0c698..1808671 100644 --- a/projects/gpt4/container/Makefile +++ b/projects/gpt4/container/Makefile @@ -5,8 +5,7 @@ TAG := $(DOCKER_ORG)/example-$(EXAMPLE_NAME)-infernet:latest .phony: build run build-multiplatform try-prompt build: - mkdir -p root-config - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . run: build @docker run --env-file $(EXAMPLE_NAME).env -p 3000:3000 $(TAG) diff --git a/projects/gpt4/container/README.md b/projects/gpt4/container/README.md index f6912d5..5d3ff7e 100644 --- a/projects/gpt4/container/README.md +++ b/projects/gpt4/container/README.md @@ -16,5 +16,5 @@ make run ## Test the Container ```bash curl -X POST localhost:3000/service_output -H "Content-Type: application/json" \ - -d '{"source": 1, "data": {"text": "can shrimps actually fry rice?"}}' + -d '{"source": 1, "data": {"prompt": "can shrimps actually fry rice?"}}' ``` diff --git a/projects/gpt4/container/config.sample.json b/projects/gpt4/container/config.sample.json index 151e5ad..7fc49e8 100644 --- a/projects/gpt4/container/config.sample.json +++ b/projects/gpt4/container/config.sample.json @@ -7,7 +7,7 @@ "enabled": true, "trail_head_blocks": 0, "rpc_url": "http://host.docker.internal:8545", - "coordinator_address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "registry_address": "0x663F3ad617193148711d28f5334eE4Ed07016602", "wallet": { "max_gas_limit": 4000000, "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" @@ -23,6 +23,10 @@ "port": 6379 }, "forward_stats": true, + "snapshot_sync": { + "sleep": 3, + "batch_size": 100 + }, "containers": [ { "id": "gpt4", @@ -34,19 +38,9 @@ "allowed_ips": [], "command": "--bind=0.0.0.0:3000 --workers=2", "env": { - "OPENAI_API_KEY": "barabeem baraboom" - } - }, - { - "id": "anvil-node", - "image": "ritualnetwork/infernet-anvil:0.0.0", - "external": true, - "port": "8545", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "", - "env": {} + "OPENAI_API_KEY": "your-key" + }, + "accepted_payments": {} } ] } diff --git a/projects/gpt4/container/src/app.py b/projects/gpt4/container/src/app.py index 17e4694..dae3eae 100644 --- a/projects/gpt4/container/src/app.py +++ b/projects/gpt4/container/src/app.py @@ -1,8 +1,16 @@ import logging +import os from typing import Any, cast from eth_abi import decode, encode # type: ignore -from infernet_ml.utils.service_models import InfernetInput, InfernetInputSource +from infernet_ml.utils.css_mux import ( + ConvoMessage, + CSSCompletionParams, + CSSRequest, + Provider, +) +from infernet_ml.utils.service_models import InfernetInput +from infernet_ml.utils.service_models import JobLocation from infernet_ml.workflows.inference.css_inference_workflow import CSSInferenceWorkflow from quart import Quart, request @@ -12,7 +20,9 @@ log = logging.getLogger(__name__) def create_app() -> Quart: app = Quart(__name__) - workflow = CSSInferenceWorkflow(provider="OPENAI", endpoint="completions") + workflow = CSSInferenceWorkflow( + api_keys={Provider.OPENAI: os.environ["OPENAI_API_KEY"]} + ) workflow.setup() @@ -24,7 +34,7 @@ def create_app() -> Quart: return "GPT4 Example Program" @app.route("/service_output", methods=["POST"]) - async def inference() -> dict[str, Any]: + async def inference() -> Any: req_data = await request.get_json() """ InfernetInput has the format: @@ -33,52 +43,62 @@ def create_app() -> Quart: """ infernet_input: InfernetInput = InfernetInput(**req_data) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - prompt = cast(dict[str, Any], infernet_input.data).get("prompt") - else: - # On-chain requests are sent as a generalized hex-string which we will - # decode to the appropriate format. - (prompt,) = decode( - ["string"], bytes.fromhex(cast(str, infernet_input.data)) - ) + match infernet_input: + case InfernetInput(source=JobLocation.OFFCHAIN): + prompt = cast(dict[str, Any], infernet_input.data).get("prompt") + case InfernetInput(source=JobLocation.ONCHAIN): + # On-chain requests are sent as a generalized hex-string which we will + # decode to the appropriate format. + (prompt,) = decode( + ["string"], bytes.fromhex(cast(str, infernet_input.data)) + ) + case _: + raise ValueError("Invalid source") - result: dict[str, Any] = workflow.inference( - { - "model": "gpt-4-0613", - "params": { - "endpoint": "completions", - "messages": [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": prompt}, - ], - }, - } + result = workflow.inference( + CSSRequest( + provider=Provider.OPENAI, + endpoint="completions", + model="gpt-4-0613", + params=CSSCompletionParams( + messages=[ + ConvoMessage( + role="system", content="you are a helpful " "assistant." + ), + ConvoMessage(role="user", content=cast(str, prompt)), + ] + ), + ) ) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - """ - In case of an off-chain request, the result is returned as is. - """ - return {"message": result} - else: - """ - In case of an on-chain request, the result is returned in the format: - { - "raw_input": str, - "processed_input": str, - "raw_output": str, - "processed_output": str, - "proof": str, - } - refer to: https://docs.ritual.net/infernet/node/containers for more info. - """ - return { - "raw_input": "", - "processed_input": "", - "raw_output": encode(["string"], [result]).hex(), - "processed_output": "", - "proof": "", - } + match infernet_input: + case InfernetInput(destination=JobLocation.OFFCHAIN): + """ + In case of an off-chain request, the result is returned as is. + """ + return {"message": result} + case InfernetInput(destination=JobLocation.ONCHAIN): + """ + In case of an on-chain request, the result is returned in the format: + { + "raw_input": str, + "processed_input": str, + "raw_output": str, + "processed_output": str, + "proof": str, + } + refer to: https://docs.ritual.net/infernet/node/containers for more + info. + """ + return { + "raw_input": "", + "processed_input": "", + "raw_output": encode(["string"], [result]).hex(), + "processed_output": "", + "proof": "", + } + case _: + raise ValueError("Invalid destination") return app diff --git a/projects/gpt4/container/src/requirements.txt b/projects/gpt4/container/src/requirements.txt index 12cb6d3..4c914e9 100644 --- a/projects/gpt4/container/src/requirements.txt +++ b/projects/gpt4/container/src/requirements.txt @@ -1,5 +1,4 @@ quart==0.19.4 -infernet_ml==0.1.0 -PyArweave @ git+https://github.com/ritual-net/pyarweave.git +infernet-ml==1.0.0 +infernet-ml[css_inference]==1.0.0 web3==6.15.0 -retry2==0.9.5 diff --git a/projects/gpt4/contracts/lib/forge-std b/projects/gpt4/contracts/lib/forge-std index e4aef94..52715a2 160000 --- a/projects/gpt4/contracts/lib/forge-std +++ b/projects/gpt4/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/projects/gpt4/contracts/lib/infernet-sdk b/projects/gpt4/contracts/lib/infernet-sdk index 2d04a7f..8e6cd6f 160000 --- a/projects/gpt4/contracts/lib/infernet-sdk +++ b/projects/gpt4/contracts/lib/infernet-sdk @@ -1 +1 @@ -Subproject commit 2d04a7f5ed64738218941e5d7a7270382f191a01 +Subproject commit 8e6cd6f5cbd66dc9baacb895a2ed8fe2c9ee3b6f diff --git a/projects/gpt4/contracts/script/CallContract.s.sol b/projects/gpt4/contracts/script/CallContract.s.sol index a6cf018..705abce 100644 --- a/projects/gpt4/contracts/script/CallContract.s.sol +++ b/projects/gpt4/contracts/script/CallContract.s.sol @@ -10,7 +10,7 @@ contract CallContract is Script { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); - PromptsGPT promptsGpt = PromptsGPT(0x663F3ad617193148711d28f5334eE4Ed07016602); + PromptsGPT promptsGpt = PromptsGPT(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e); promptsGpt.promptGPT(vm.envString("prompt")); diff --git a/projects/gpt4/contracts/script/Deploy.s.sol b/projects/gpt4/contracts/script/Deploy.s.sol index 98e086d..26d1114 100644 --- a/projects/gpt4/contracts/script/Deploy.s.sol +++ b/projects/gpt4/contracts/script/Deploy.s.sol @@ -14,10 +14,10 @@ contract Deploy is Script { address deployerAddress = vm.addr(deployerPrivateKey); console2.log("Loaded deployer: ", deployerAddress); - address coordinator = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; // Create consumer - PromptsGPT promptsGPT = new PromptsGPT(coordinator); + PromptsGPT promptsGPT = new PromptsGPT(registry); console2.log("Deployed PromptsGPT: ", address(promptsGPT)); // Execute diff --git a/projects/gpt4/contracts/src/PromptsGPT.sol b/projects/gpt4/contracts/src/PromptsGPT.sol index 3676f72..a721740 100644 --- a/projects/gpt4/contracts/src/PromptsGPT.sol +++ b/projects/gpt4/contracts/src/PromptsGPT.sol @@ -12,15 +12,17 @@ contract PromptsGPT is CallbackConsumer { "| _ / | | | | | | | |/ /\\ \\ | | \n" "| | \\ \\ _| |_ | | | |__| / ____ \\| |____ \n" "|_| \\_\\_____| |_| \\____/_/ \\_\\______| \n\n"; - constructor(address coordinator) CallbackConsumer(coordinator) {} + constructor(address registry) CallbackConsumer(registry) {} function promptGPT(string calldata prompt) public { _requestCompute( "gpt4", abi.encode(prompt), - 20 gwei, - 1_000_000, - 1 + 1, // redundancy + address(0), // paymentToken + 0, // paymentAmount + address(0), // wallet + address(0) // prover ); } @@ -31,7 +33,9 @@ contract PromptsGPT is CallbackConsumer { address node, bytes calldata input, bytes calldata output, - bytes calldata proof + bytes calldata proof, + bytes32 containerId, + uint256 index ) internal override { console2.log(EXTREMELY_COOL_BANNER); (bytes memory raw_output, bytes memory processed_output) = abi.decode(output, (bytes, bytes)); diff --git a/projects/gpt4/gpt4.md b/projects/gpt4/gpt4.md index 5389c55..2f3acbc 100644 --- a/projects/gpt4/gpt4.md +++ b/projects/gpt4/gpt4.md @@ -129,7 +129,7 @@ curl -X GET http://127.0.0.1:4000/api/jobs\?id\=cab6eea8-8b1e-4144-9a70-f905c5ef And if you have `jq` installed and piped the last output to a file, you can instead run: ```bash -curl -X GET "http://127.0.0.1:4000/api/jobs?id=$(cat last-request.uuid)" | jq . +curl -X GET "http://127.0.0.1:4000/api/jobs?id=$(cat last-job.uuid)" | jq . # returns something like: [ { diff --git a/projects/hello-world/container/Dockerfile b/projects/hello-world/container/Dockerfile index 9a143fd..d4deb01 100644 --- a/projects/hello-world/container/Dockerfile +++ b/projects/hello-world/container/Dockerfile @@ -7,12 +7,15 @@ ENV PYTHONDONTWRITEBYTECODE 1 ENV PIP_NO_CACHE_DIR 1 ENV RUNTIME docker ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} RUN apt-get update RUN apt-get install -y git curl # install uv -ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh +ADD https://astral.sh/uv/install.sh /install.sh +RUN chmod 755 /install.sh RUN /install.sh && rm /install.sh COPY src/requirements.txt . diff --git a/projects/hello-world/container/Makefile b/projects/hello-world/container/Makefile index e87f59e..256a0b4 100644 --- a/projects/hello-world/container/Makefile +++ b/projects/hello-world/container/Makefile @@ -4,7 +4,7 @@ TAG := $(DOCKER_ORG)/hello-world-infernet:latest .phony: build run publish build: - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . update-tag: jq ".containers[0].image = \"$(TAG)\"" config.json > updated_config.json && mv updated_config.json config.json diff --git a/projects/hello-world/container/README.md b/projects/hello-world/container/README.md index 4772e92..e1517c5 100644 --- a/projects/hello-world/container/README.md +++ b/projects/hello-world/container/README.md @@ -50,7 +50,7 @@ file with the following content: ``` Flask>=3.0.0,<4.0.0 -gunicorn>=21.2.0,<22.0.0 +gunicorn>=22.0.0,<23.0.0 ``` ## Step 2: create a Dockerfile diff --git a/projects/hello-world/container/config.json b/projects/hello-world/container/config.json index e5763db..233cb07 100644 --- a/projects/hello-world/container/config.json +++ b/projects/hello-world/container/config.json @@ -1,50 +1,44 @@ { - "log_path": "infernet_node.log", - "server": { - "port": 4000 - }, - "chain": { - "enabled": true, - "trail_head_blocks": 0, - "rpc_url": "http://host.docker.internal:8545", - "coordinator_address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "wallet": { - "max_gas_limit": 4000000, - "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" - } - }, - "startup_wait": 1.0, - "docker": { - "username": "your-username", - "password": "" - }, - "redis": { - "host": "redis", - "port": 6379 - }, - "forward_stats": true, - "containers": [ - { - "id": "hello-world", - "image": "ritualnetwork/hello-world-infernet:latest", - "external": true, - "port": "3000", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "--bind=0.0.0.0:3000 --workers=2", - "env": {} + "log_path": "infernet_node.log", + "server": { + "port": 4000 }, - { - "id": "anvil-node", - "image": "ritualnetwork/infernet-anvil:0.0.0", - "external": true, - "port": "8545", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "", - "env": {} - } - ] + "chain": { + "enabled": true, + "trail_head_blocks": 0, + "rpc_url": "http://host.docker.internal:8545", + "registry_address": "0x663F3ad617193148711d28f5334eE4Ed07016602", + "wallet": { + "max_gas_limit": 4000000, + "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" + } + }, + "startup_wait": 1.0, + "docker": { + "username": "your-username", + "password": "" + }, + "redis": { + "host": "redis", + "port": 6379 + }, + "forward_stats": true, + "snapshot_sync": { + "sleep": 3, + "batch_size": 100 + }, + "containers": [ + { + "id": "hello-world", + "image": "ritualnetwork/hello-world-infernet:latest", + "external": true, + "port": "3000", + "allowed_delegate_addresses": [], + "allowed_addresses": [], + "allowed_ips": [], + "command": "--bind=0.0.0.0:3000 --workers=2", + "env": {}, + "accepted_payments": {} + } + ] } diff --git a/projects/hello-world/container/src/requirements.txt b/projects/hello-world/container/src/requirements.txt index 5d42a79..c0e618a 100644 --- a/projects/hello-world/container/src/requirements.txt +++ b/projects/hello-world/container/src/requirements.txt @@ -1,2 +1,2 @@ Flask>=3.0.0,<4.0.0 -gunicorn>=21.2.0,<22.0.0 +gunicorn>=22.0.0,<23.0.0 diff --git a/projects/hello-world/contracts/Tutorial.md b/projects/hello-world/contracts/Tutorial.md index 174d3b7..bc70744 100644 --- a/projects/hello-world/contracts/Tutorial.md +++ b/projects/hello-world/contracts/Tutorial.md @@ -58,7 +58,7 @@ import {console2} from "forge-std/console2.sol"; import {CallbackConsumer} from "infernet-sdk/consumer/Callback.sol"; contract SaysGM is CallbackConsumer { - constructor(address coordinator) CallbackConsumer(coordinator) {} + constructor(address registry) CallbackConsumer(registry) {} function sayGM() public { _requestCompute( @@ -128,9 +128,9 @@ contract Deploy is Script { address deployerAddress = vm.addr(deployerPrivateKey); console2.log("Loaded deployer: ", deployerAddress); - address coordinator = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; // Create consumer - SaysGM saysGm = new SaysGM(coordinator); + SaysGM saysGm = new SaysGM(registry); console2.log("Deployed SaysHello: ", address(saysGm)); // Execute @@ -160,7 +160,7 @@ contract CallContract is Script { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); - SaysGM saysGm = SaysGM(0x663F3ad617193148711d28f5334eE4Ed07016602); + SaysGM saysGm = SaysGM(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e); saysGm.sayGM(); diff --git a/projects/hello-world/contracts/lib/forge-std b/projects/hello-world/contracts/lib/forge-std index e4aef94..52715a2 160000 --- a/projects/hello-world/contracts/lib/forge-std +++ b/projects/hello-world/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/projects/hello-world/contracts/lib/infernet-sdk b/projects/hello-world/contracts/lib/infernet-sdk index 2d04a7f..8e6cd6f 160000 --- a/projects/hello-world/contracts/lib/infernet-sdk +++ b/projects/hello-world/contracts/lib/infernet-sdk @@ -1 +1 @@ -Subproject commit 2d04a7f5ed64738218941e5d7a7270382f191a01 +Subproject commit 8e6cd6f5cbd66dc9baacb895a2ed8fe2c9ee3b6f diff --git a/projects/hello-world/contracts/remappings.txt b/projects/hello-world/contracts/remappings.txt index c788350..9b4e295 100644 --- a/projects/hello-world/contracts/remappings.txt +++ b/projects/hello-world/contracts/remappings.txt @@ -1,2 +1,3 @@ forge-std/=lib/forge-std/src infernet-sdk/=lib/infernet-sdk/src +solady/=lib/infernet-sdk/lib/solady/src diff --git a/projects/hello-world/contracts/script/CallContract.s.sol b/projects/hello-world/contracts/script/CallContract.s.sol index 61a8090..628a910 100644 --- a/projects/hello-world/contracts/script/CallContract.s.sol +++ b/projects/hello-world/contracts/script/CallContract.s.sol @@ -10,7 +10,7 @@ contract CallContract is Script { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); - SaysGM saysGm = SaysGM(0x663F3ad617193148711d28f5334eE4Ed07016602); + SaysGM saysGm = SaysGM(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e); saysGm.sayGM(); diff --git a/projects/hello-world/contracts/script/Deploy.s.sol b/projects/hello-world/contracts/script/Deploy.s.sol index 8dda890..c60a423 100644 --- a/projects/hello-world/contracts/script/Deploy.s.sol +++ b/projects/hello-world/contracts/script/Deploy.s.sol @@ -14,9 +14,9 @@ contract Deploy is Script { address deployerAddress = vm.addr(deployerPrivateKey); console2.log("Loaded deployer: ", deployerAddress); - address coordinator = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; // Create consumer - SaysGM saysGm = new SaysGM(coordinator); + SaysGM saysGm = new SaysGM(registry); console2.log("Deployed SaysHello: ", address(saysGm)); // Execute diff --git a/projects/hello-world/contracts/src/SaysGM.sol b/projects/hello-world/contracts/src/SaysGM.sol index 0369854..d44bd9e 100644 --- a/projects/hello-world/contracts/src/SaysGM.sol +++ b/projects/hello-world/contracts/src/SaysGM.sol @@ -5,15 +5,17 @@ import {console2} from "forge-std/console2.sol"; import {CallbackConsumer} from "infernet-sdk/consumer/Callback.sol"; contract SaysGM is CallbackConsumer { - constructor(address coordinator) CallbackConsumer(coordinator) {} + constructor(address registry) CallbackConsumer(registry) {} function sayGM() public { _requestCompute( "hello-world", bytes("Good morning!"), - 20 gwei, - 1_000_000, - 1 + 1, // redundancy + address(0), // paymentToken + 0, // paymentAmount + address(0), // wallet + address(0) // prover ); } @@ -24,7 +26,9 @@ contract SaysGM is CallbackConsumer { address node, bytes calldata input, bytes calldata output, - bytes calldata proof + bytes calldata proof, + bytes32 containerId, + uint256 index ) internal override { console2.log("\n\n" "_____ _____ _______ _ _ _\n" @@ -43,6 +47,8 @@ contract SaysGM is CallbackConsumer { console2.logBytes(input); console2.log("output:"); console2.logBytes(output); + (string memory decoded)= abi.decode(output, (string)); + console2.log("decoded output: ", decoded); console2.log("proof:"); console2.logBytes(proof); } diff --git a/projects/hello-world/hello-world.md b/projects/hello-world/hello-world.md index 74626c9..0611aab 100644 --- a/projects/hello-world/hello-world.md +++ b/projects/hello-world/hello-world.md @@ -78,7 +78,7 @@ This project already comes with a pre-filled config file. The config file for th [here](container/config.json): ```bash -projects/hello-world/config.json +projects/hello-world/container/config.json ``` ## Requesting an on-chain job @@ -92,7 +92,7 @@ We already have a public [anvil node](https://hub.docker.com/r/ritualnetwork/inf corresponding infernet sdk contracts deployed, along with a node that has registered itself to listen to on-chain subscription events. -* Coordinator Address: `0x5FbDB2315678afecb367f032d93F642f64180aa3` +* Registry Address: `0x663F3ad617193148711d28f5334eE4Ed07016602` * Node Address: `0x70997970C51812dc3A010C7d01b50e0d17dc79C8` (This is the second account in the anvil's accounts.) ### Deploying Infernet Node & Infernet's Anvil Testnet @@ -140,7 +140,7 @@ eth_sendRawTransaction eth_getTransactionReceipt Transaction: 0x23ca6b1d1823ad5af175c207c2505112f60038fc000e1e22509816fa29a3afd6 - Contract created: 0x663f3ad617193148711d28f5334ee4ed07016602 + Contract created: 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e Gas used: 476669 Block Number: 1 @@ -152,7 +152,7 @@ eth_getTransactionReceipt eth_blockNumber ``` -We can see that a new contract has been created at `0x663f3ad617193148711d28f5334ee4ed07016602`. +We can see that a new contract has been created at `0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e`. That's the address of the `SaysGM` contract. ### Calling the contract diff --git a/projects/onnx-iris/container/Dockerfile b/projects/onnx-iris/container/Dockerfile index 57add3f..92d4f77 100644 --- a/projects/onnx-iris/container/Dockerfile +++ b/projects/onnx-iris/container/Dockerfile @@ -7,12 +7,15 @@ ENV PYTHONDONTWRITEBYTECODE 1 ENV PIP_NO_CACHE_DIR 1 ENV RUNTIME docker ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} RUN apt-get update RUN apt-get install -y git curl # install uv -ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh +ADD https://astral.sh/uv/install.sh /install.sh +RUN chmod 755 /install.sh RUN /install.sh && rm /install.sh COPY src/requirements.txt . diff --git a/projects/onnx-iris/container/Makefile b/projects/onnx-iris/container/Makefile index f392f14..5dd13ae 100644 --- a/projects/onnx-iris/container/Makefile +++ b/projects/onnx-iris/container/Makefile @@ -5,7 +5,7 @@ TAG := $(DOCKER_ORG)/example-$(EXAMPLE_NAME)-infernet:latest .phony: build run build-multiplatform build: - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . run: docker run -p 3000:3000 $(TAG) diff --git a/projects/onnx-iris/container/README.md b/projects/onnx-iris/container/README.md index 753d4ec..ab34725 100644 --- a/projects/onnx-iris/container/README.md +++ b/projects/onnx-iris/container/README.md @@ -8,9 +8,9 @@ repository. ## Overview We're making use of -the [ONNXInferenceWorkflow](https://github.com/ritual-net/infernet-ml-internal/blob/main/src/ml/workflows/inference/onnx_inference_workflow.py) +the [ONNXInferenceWorkflow](https://github.com/ritual-net/infernet-ml/blob/main/src/ml/workflows/inference/onnx_inference_workflow.py) class to run the model. This is one of many workflows that we currently support in our -[infernet-ml](https://github.com/ritual-net/infernet-ml-internal). Consult the library's +[infernet-ml](https://github.com/ritual-net/infernet-ml). Consult the library's documentation for more info on workflows that are supported. diff --git a/projects/onnx-iris/container/config.json b/projects/onnx-iris/container/config.json index aa9856a..4204c48 100644 --- a/projects/onnx-iris/container/config.json +++ b/projects/onnx-iris/container/config.json @@ -7,7 +7,7 @@ "enabled": true, "trail_head_blocks": 0, "rpc_url": "http://host.docker.internal:8545", - "coordinator_address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "registry_address": "0x663F3ad617193148711d28f5334eE4Ed07016602", "wallet": { "max_gas_limit": 4000000, "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" @@ -23,6 +23,10 @@ "port": 6379 }, "forward_stats": true, + "snapshot_sync": { + "sleep": 3, + "batch_size": 100 + }, "containers": [ { "id": "onnx-iris", @@ -33,18 +37,8 @@ "allowed_addresses": [], "allowed_ips": [], "command": "--bind=0.0.0.0:3000 --workers=2", - "env": {} - }, - { - "id": "anvil-node", - "image": "ritualnetwork/infernet-anvil:0.0.0", - "external": true, - "port": "8545", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "", - "env": {} + "env": {}, + "accepted_payments": {} } ] } diff --git a/projects/onnx-iris/container/src/app.py b/projects/onnx-iris/container/src/app.py index 46cfde9..67dc259 100644 --- a/projects/onnx-iris/container/src/app.py +++ b/projects/onnx-iris/container/src/app.py @@ -1,12 +1,18 @@ import logging from typing import Any, cast, List +from infernet_ml.utils.common_types import TensorInput import numpy as np from eth_abi import decode, encode # type: ignore -from infernet_ml.utils.model_loader import ModelSource -from infernet_ml.utils.service_models import InfernetInput, InfernetInputSource +from infernet_ml.utils.model_loader import ( + HFLoadArgs, + ModelSource, +) +from infernet_ml.utils.service_models import InfernetInput, JobLocation from infernet_ml.workflows.inference.onnx_inference_workflow import ( ONNXInferenceWorkflow, + ONNXInferenceInput, + ONNXInferenceResult, ) from quart import Quart, request from quart.json.provider import DefaultJSONProvider @@ -29,10 +35,11 @@ def create_app() -> Quart: app = Quart(__name__) # we are downloading the model from the hub. # model repo is located at: https://huggingface.co/Ritual-Net/iris-dataset - model_source = ModelSource.HUGGINGFACE_HUB - model_args = {"repo_id": "Ritual-Net/iris-dataset", "filename": "iris.onnx"} - workflow = ONNXInferenceWorkflow(model_source=model_source, model_args=model_args) + workflow = ONNXInferenceWorkflow( + model_source=ModelSource.HUGGINGFACE_HUB, + load_args=HFLoadArgs(repo_id="Ritual-Net/iris-dataset", filename="iris.onnx"), + ) workflow.setup() @app.route("/") @@ -43,7 +50,7 @@ def create_app() -> Quart: return "ONNX Iris Classifier Example Program" @app.route("/service_output", methods=["POST"]) - async def inference() -> dict[str, Any]: + async def inference() -> Any: req_data = await request.get_json() """ InfernetInput has the format: @@ -52,50 +59,56 @@ def create_app() -> Quart: """ infernet_input: InfernetInput = InfernetInput(**req_data) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - web2_input = cast(dict[str, Any], infernet_input.data) - values = cast(List[List[float]], web2_input["input"]) - else: - # On-chain requests are sent as a generalized hex-string which we will - # decode to the appropriate format. - web3_input: List[int] = decode( - ["uint256[]"], bytes.fromhex(cast(str, infernet_input.data)) - )[0] - values = [[float(v) / 1e6 for v in web3_input]] + match infernet_input: + case InfernetInput(source=JobLocation.OFFCHAIN): + web2_input = cast(dict[str, Any], infernet_input.data) + values = cast(List[List[float]], web2_input["input"]) + case InfernetInput(source=JobLocation.ONCHAIN): + web3_input: List[int] = decode( + ["uint256[]"], bytes.fromhex(cast(str, infernet_input.data)) + )[0] + values = [[float(v) / 1e6 for v in web3_input]] """ The input to the onnx inference workflow needs to conform to ONNX runtime's input_feed format. For more information refer to: https://docs.ritual.net/ml-workflows/inference-workflows/onnx_inference_workflow """ - result: dict[str, Any] = workflow.inference({"input": values}) + _input = ONNXInferenceInput( + inputs={"input": TensorInput(shape=(1, 4), dtype="float", values=values)}, + ) + result: ONNXInferenceResult = workflow.inference(_input) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - """ - In case of an off-chain request, the result is returned as is. - """ - return result - else: - """ - In case of an on-chain request, the result is returned in the format: - { - "raw_input": str, - "processed_input": str, - "raw_output": str, - "processed_output": str, - "proof": str, - } - refer to: https://docs.ritual.net/infernet/node/containers for more info. - """ - predictions = cast(List[List[List[float]]], result) - predictions_normalized = [int(p * 1e6) for p in predictions[0][0]] - return { - "raw_input": "", - "processed_input": "", - "raw_output": encode(["uint256[]"], [predictions_normalized]).hex(), - "processed_output": "", - "proof": "", - } + match infernet_input: + case InfernetInput(destination=JobLocation.OFFCHAIN): + """ + In case of an off-chain request, the result is returned as is. + """ + return result + case InfernetInput(destination=JobLocation.ONCHAIN): + """ + In case of an on-chain request, the result is returned in the format: + { + "raw_input": str, + "processed_input": str, + "raw_output": str, + "processed_output": str, + "proof": str, + } + refer to: https://docs.ritual.net/infernet/node/containers for more + info. + """ + predictions = result[0] + predictions_normalized = [int(p * 1e6) for p in predictions.values] + return { + "raw_input": "", + "processed_input": "", + "raw_output": encode(["uint256[]"], [predictions_normalized]).hex(), + "processed_output": "", + "proof": "", + } + case _: + raise ValueError("Invalid destination") return app diff --git a/projects/onnx-iris/container/src/requirements.txt b/projects/onnx-iris/container/src/requirements.txt index be6cb85..2fa2424 100644 --- a/projects/onnx-iris/container/src/requirements.txt +++ b/projects/onnx-iris/container/src/requirements.txt @@ -1,7 +1,4 @@ quart==0.19.4 -infernet_ml==0.1.0 -PyArweave @ git+https://github.com/ritual-net/pyarweave.git +infernet-ml==1.0.0 +infernet-ml[onnx_inference]==1.0.0 web3==6.15.0 -onnx==1.15.0 -onnxruntime==1.16.3 -torch==2.1.2 diff --git a/projects/onnx-iris/contracts/lib/forge-std b/projects/onnx-iris/contracts/lib/forge-std index e4aef94..52715a2 160000 --- a/projects/onnx-iris/contracts/lib/forge-std +++ b/projects/onnx-iris/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/projects/onnx-iris/contracts/lib/infernet-sdk b/projects/onnx-iris/contracts/lib/infernet-sdk index 2d04a7f..8e6cd6f 160000 --- a/projects/onnx-iris/contracts/lib/infernet-sdk +++ b/projects/onnx-iris/contracts/lib/infernet-sdk @@ -1 +1 @@ -Subproject commit 2d04a7f5ed64738218941e5d7a7270382f191a01 +Subproject commit 8e6cd6f5cbd66dc9baacb895a2ed8fe2c9ee3b6f diff --git a/projects/onnx-iris/contracts/script/CallContract.s.sol b/projects/onnx-iris/contracts/script/CallContract.s.sol index 3612da2..6660371 100644 --- a/projects/onnx-iris/contracts/script/CallContract.s.sol +++ b/projects/onnx-iris/contracts/script/CallContract.s.sol @@ -10,7 +10,7 @@ contract CallContract is Script { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); - IrisClassifier irisClassifier = IrisClassifier(0x663F3ad617193148711d28f5334eE4Ed07016602); + IrisClassifier irisClassifier = IrisClassifier(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e); irisClassifier.classifyIris(); diff --git a/projects/onnx-iris/contracts/script/Deploy.s.sol b/projects/onnx-iris/contracts/script/Deploy.s.sol index 94fb53e..9cf4cc6 100644 --- a/projects/onnx-iris/contracts/script/Deploy.s.sol +++ b/projects/onnx-iris/contracts/script/Deploy.s.sol @@ -14,9 +14,9 @@ contract Deploy is Script { address deployerAddress = vm.addr(deployerPrivateKey); console2.log("Loaded deployer: ", deployerAddress); - address coordinator = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; // Create consumer - IrisClassifier classifier = new IrisClassifier(coordinator); + IrisClassifier classifier = new IrisClassifier(registry); console2.log("Deployed IrisClassifier: ", address(classifier)); // Execute diff --git a/projects/onnx-iris/contracts/src/IrisClassifier.sol b/projects/onnx-iris/contracts/src/IrisClassifier.sol index 1fd5eb4..6c29d71 100644 --- a/projects/onnx-iris/contracts/src/IrisClassifier.sol +++ b/projects/onnx-iris/contracts/src/IrisClassifier.sol @@ -14,7 +14,7 @@ contract IrisClassifier is CallbackConsumer { "| | \\ \\ _| |_ | | | |__| / ____ \\| |____\n" "|_| \\_\\_____| |_| \\____/_/ \\_\\______|\n\n"; - constructor(address coordinator) CallbackConsumer(coordinator) {} + constructor(address registry) CallbackConsumer(registry) {} function classifyIris() public { /// @dev Iris data is in the following format: @@ -38,9 +38,11 @@ contract IrisClassifier is CallbackConsumer { _requestCompute( "onnx-iris", abi.encode(iris_data), - 20 gwei, - 1_000_000, - 1 + 1, // redundancy + address(0), // paymentToken + 0, // paymentAmount + address(0), // wallet + address(0) // prover ); } @@ -51,7 +53,9 @@ contract IrisClassifier is CallbackConsumer { address node, bytes calldata input, bytes calldata output, - bytes calldata proof + bytes calldata proof, + bytes32 containerId, + uint256 index ) internal override { console2.log(EXTREMELY_COOL_BANNER); (bytes memory raw_output, bytes memory processed_output) = abi.decode(output, (bytes, bytes)); diff --git a/projects/onnx-iris/onnx-iris.md b/projects/onnx-iris/onnx-iris.md index b3b01cd..e69bfa7 100644 --- a/projects/onnx-iris/onnx-iris.md +++ b/projects/onnx-iris/onnx-iris.md @@ -195,7 +195,7 @@ In your anvil logs you should see the following: eth_getTransactionReceipt Transaction: 0xeed605eacdace39a48635f6d14215b386523766f80a113b4484f542d862889a4 - Contract created: 0x663f3ad617193148711d28f5334ee4ed07016602 + Contract created: 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e Gas used: 714269 Block Number: 1 @@ -206,7 +206,7 @@ eth_blockNumber ``` beautiful, we can see that a new contract has been created -at `0x663f3ad617193148711d28f5334ee4ed07016602`. That's the address of +at `0x663F3ad617193148711d28f5334eE4Ed07016602`. That's the address of the `IrisClassifier` contract. We are now going to call this contract. To do so, we are using the [CallContract.s.sol](contracts/script/CallContract.s.sol) diff --git a/projects/payment/container/config.json b/projects/payment/container/config.json new file mode 100644 index 0000000..80ff3a3 --- /dev/null +++ b/projects/payment/container/config.json @@ -0,0 +1,47 @@ +{ + "log_path": "infernet_node.log", + "server": { + "port": 4000 + }, + "chain": { + "enabled": true, + "trail_head_blocks": 0, + "rpc_url": "http://host.docker.internal:8545", + "registry_address": "0x663F3ad617193148711d28f5334eE4Ed07016602", + "wallet": { + "max_gas_limit": 4000000, + "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "payment_address": "0x60985ee8192B322c3CAbA97A9A9f7298bdc4335C" + } + }, + "startup_wait": 1.0, + "docker": { + "username": "your-username", + "password": "" + }, + "redis": { + "host": "redis", + "port": 6379 + }, + "forward_stats": true, + "snapshot_sync": { + "sleep": 3, + "batch_size": 100 + }, + "containers": [ + { + "id": "hello-world", + "image": "ritualnetwork/hello-world-infernet:latest", + "external": true, + "port": "3000", + "allowed_delegate_addresses": [], + "allowed_addresses": [], + "allowed_ips": [], + "command": "--bind=0.0.0.0:3000 --workers=2", + "env": {}, + "accepted_payments": { + "0x0000000000000000000000000000000000000000": 1000000000000000000 + } + } + ] +} diff --git a/projects/payment/contracts/.gitignore b/projects/payment/contracts/.gitignore new file mode 100644 index 0000000..3dcaa71 --- /dev/null +++ b/projects/payment/contracts/.gitignore @@ -0,0 +1,12 @@ +# Compiler files +cache/ +out/ + +# Ignores broadcast logs +broadcast + +# Docs +docs/ + +# Dotenv file +.env diff --git a/projects/payment/contracts/Makefile b/projects/payment/contracts/Makefile new file mode 100644 index 0000000..2af9de7 --- /dev/null +++ b/projects/payment/contracts/Makefile @@ -0,0 +1,14 @@ +# phony targets are targets that don't actually create a file +.phony: deploy + +# anvil's third default address +sender := 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a +RPC_URL := http://localhost:8545 + +# deploying the contract +deploy: + @PRIVATE_KEY=$(sender) forge script script/Deploy.s.sol:Deploy --broadcast --rpc-url $(RPC_URL) + +# calling sayGM() +call-contract: + @PRIVATE_KEY=$(sender) forge script script/CallContract.s.sol:CallContract --broadcast --rpc-url $(RPC_URL) diff --git a/projects/payment/contracts/README.md b/projects/payment/contracts/README.md new file mode 100644 index 0000000..814736e --- /dev/null +++ b/projects/payment/contracts/README.md @@ -0,0 +1,5 @@ +## Payment Flow Example + +This example contains all of the code for the payments flow example. Refer +to [the official documentation](https://docs.ritual.net/infernet/node/intro_to_payments) +for a step-by-step guide on how to run this example. diff --git a/projects/payment/contracts/foundry.toml b/projects/payment/contracts/foundry.toml new file mode 100644 index 0000000..83816a2 --- /dev/null +++ b/projects/payment/contracts/foundry.toml @@ -0,0 +1,7 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] +via_ir = true + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/projects/payment/contracts/lib/forge-std b/projects/payment/contracts/lib/forge-std new file mode 160000 index 0000000..52715a2 --- /dev/null +++ b/projects/payment/contracts/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/projects/payment/contracts/lib/infernet-sdk b/projects/payment/contracts/lib/infernet-sdk new file mode 160000 index 0000000..8e6cd6f --- /dev/null +++ b/projects/payment/contracts/lib/infernet-sdk @@ -0,0 +1 @@ +Subproject commit 8e6cd6f5cbd66dc9baacb895a2ed8fe2c9ee3b6f diff --git a/projects/payment/contracts/remappings.txt b/projects/payment/contracts/remappings.txt new file mode 100644 index 0000000..c788350 --- /dev/null +++ b/projects/payment/contracts/remappings.txt @@ -0,0 +1,2 @@ +forge-std/=lib/forge-std/src +infernet-sdk/=lib/infernet-sdk/src diff --git a/projects/payment/contracts/script/CallContract.s.sol b/projects/payment/contracts/script/CallContract.s.sol new file mode 100644 index 0000000..e34bf87 --- /dev/null +++ b/projects/payment/contracts/script/CallContract.s.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +pragma solidity ^0.8.0; + +import {Script, console2} from "forge-std/Script.sol"; +import {SaysGM} from "../src/SaysGM.sol"; + +contract CallContract is Script { + function run() public { + // Setup wallet + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + + uint256 amount = vm.envUint("amount"); + address wallet = vm.envAddress("wallet"); + + vm.startBroadcast(deployerPrivateKey); + + address registry = 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e; + SaysGM saysGm = SaysGM(registry); + + saysGm.sayGM(amount, wallet); + + vm.stopBroadcast(); + } +} diff --git a/projects/payment/contracts/script/Deploy.s.sol b/projects/payment/contracts/script/Deploy.s.sol new file mode 100644 index 0000000..c60a423 --- /dev/null +++ b/projects/payment/contracts/script/Deploy.s.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +pragma solidity ^0.8.13; + +import {Script, console2} from "forge-std/Script.sol"; +import {SaysGM} from "../src/SaysGM.sol"; + +contract Deploy is Script { + function run() public { + // Setup wallet + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + // Log address + address deployerAddress = vm.addr(deployerPrivateKey); + console2.log("Loaded deployer: ", deployerAddress); + + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; + // Create consumer + SaysGM saysGm = new SaysGM(registry); + console2.log("Deployed SaysHello: ", address(saysGm)); + + // Execute + vm.stopBroadcast(); + vm.broadcast(); + } +} diff --git a/projects/payment/contracts/src/SaysGM.sol b/projects/payment/contracts/src/SaysGM.sol new file mode 100644 index 0000000..8c4d692 --- /dev/null +++ b/projects/payment/contracts/src/SaysGM.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +pragma solidity ^0.8.13; + +import {console2} from "forge-std/console2.sol"; +import {CallbackConsumer} from "infernet-sdk/consumer/Callback.sol"; + +contract SaysGM is CallbackConsumer { + constructor(address registry) CallbackConsumer(registry) {} + + function sayGM(uint256 paymentAmount, address wallet) public { + _requestCompute( + "hello-world", + bytes("Good morning!"), + 1, // redundancy + address(0), // paymentToken + paymentAmount, + wallet, + address(0) // prover + ); + } + + function _receiveCompute( + uint32 subscriptionId, + uint32 interval, + uint16 redundancy, + address node, + bytes calldata input, + bytes calldata output, + bytes calldata proof, + bytes32 containerId, + uint256 index + ) internal override { + console2.log("\n\n" + "_____ _____ _______ _ _ _\n" + "| __ \\|_ _|__ __| | | | /\\ | |\n" + "| |__) | | | | | | | | | / \\ | |\n" + "| _ / | | | | | | | |/ /\\ \\ | |\n" + "| | \\ \\ _| |_ | | | |__| / ____ \\| |____\n" + "|_| \\_\\_____| |_| \\____/_/ \\_\\______|\n\n"); + + + console2.log("subscription Id", subscriptionId); + console2.log("interval", interval); + console2.log("redundancy", redundancy); + console2.log("node", node); + console2.log("input:"); + console2.logBytes(input); + console2.log("output:"); + console2.logBytes(output); + (string memory decoded)= abi.decode(output, (string)); + console2.log("decoded output: ", decoded); + console2.log("proof:"); + console2.logBytes(proof); + } +} diff --git a/projects/prompt-to-nft/container/Dockerfile b/projects/prompt-to-nft/container/Dockerfile index eb12000..a0c9fe0 100644 --- a/projects/prompt-to-nft/container/Dockerfile +++ b/projects/prompt-to-nft/container/Dockerfile @@ -7,12 +7,15 @@ ENV PYTHONDONTWRITEBYTECODE 1 ENV PIP_NO_CACHE_DIR 1 ENV RUNTIME docker ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} RUN apt-get update RUN apt-get install -y git curl # install uv -ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh +ADD https://astral.sh/uv/install.sh /install.sh +RUN chmod 755 /install.sh RUN /install.sh && rm /install.sh COPY src/requirements.txt . diff --git a/projects/prompt-to-nft/container/Makefile b/projects/prompt-to-nft/container/Makefile index 2c67657..55710ff 100644 --- a/projects/prompt-to-nft/container/Makefile +++ b/projects/prompt-to-nft/container/Makefile @@ -9,7 +9,7 @@ ifdef CI mkdir -p wallet # in CI we don't have a wallet directory. This enables to bypass that and ensure that the image # is built successfully endif - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . wallet_dir ?= /app/wallet diff --git a/projects/prompt-to-nft/container/config.sample.json b/projects/prompt-to-nft/container/config.sample.json index 40f05de..3daf499 100644 --- a/projects/prompt-to-nft/container/config.sample.json +++ b/projects/prompt-to-nft/container/config.sample.json @@ -7,7 +7,7 @@ "enabled": true, "trail_head_blocks": 0, "rpc_url": "http://host.docker.internal:8545", - "coordinator_address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "registry_address": "0x663F3ad617193148711d28f5334eE4Ed07016602", "wallet": { "max_gas_limit": 4000000, "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" @@ -23,6 +23,10 @@ "port": 6379 }, "forward_stats": true, + "snapshot_sync": { + "sleep": 3, + "batch_size": 100 + }, "containers": [ { "id": "prompt-to-nft", @@ -36,18 +40,8 @@ "env": { "ARWEAVE_WALLET_FILE_PATH": "wallet/keyfile-arweave.json", "IMAGE_GEN_SERVICE_URL": "http://your.services.ip:port" - } - }, - { - "id": "anvil-node", - "image": "ritualnetwork/infernet-anvil:0.0.0", - "external": true, - "port": "8545", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "", - "env": {} + }, + "accepted_payments": {} } ] } diff --git a/projects/prompt-to-nft/container/src/app.py b/projects/prompt-to-nft/container/src/app.py index 4ae07c9..480a120 100644 --- a/projects/prompt-to-nft/container/src/app.py +++ b/projects/prompt-to-nft/container/src/app.py @@ -5,9 +5,9 @@ from typing import Any, cast import aiohttp from eth_abi import decode, encode # type: ignore -from infernet_ml.utils.arweave import upload, load_wallet -from infernet_ml.utils.service_models import InfernetInput, InfernetInputSource +from infernet_ml.utils.service_models import InfernetInput, JobLocation from quart import Quart, request +from ritual_arweave.file_manager import FileManager log = logging.getLogger(__name__) @@ -29,7 +29,6 @@ async def run_inference(prompt: str, output_path: str) -> None: def ensure_env_vars() -> None: if not os.getenv("IMAGE_GEN_SERVICE_URL"): raise ValueError("IMAGE_GEN_SERVICE_URL environment variable not set") - load_wallet() def create_app() -> Quart: @@ -54,50 +53,59 @@ def create_app() -> Quart: infernet_input: InfernetInput = InfernetInput(**req_data) temp_file = "image.png" - if infernet_input.source == InfernetInputSource.OFFCHAIN: - prompt: str = cast(dict[str, str], infernet_input.data)["prompt"] - else: - # On-chain requests are sent as a generalized hex-string which we will - # decode to the appropriate format. - (prompt, mintTo) = decode( - ["string", "address"], bytes.fromhex(cast(str, infernet_input.data)) - ) - log.info("mintTo: %s", mintTo) - log.info("prompt: %s", prompt) + match infernet_input: + case InfernetInput(source=JobLocation.OFFCHAIN): + prompt: str = cast(dict[str, str], infernet_input.data)["prompt"] + case InfernetInput(source=JobLocation.ONCHAIN): + # On-chain requests are sent as a generalized hex-string which we will + # decode to the appropriate format. + (prompt, mintTo) = decode( + ["string", "address"], bytes.fromhex(cast(str, infernet_input.data)) + ) + log.info("mintTo: %s", mintTo) + log.info("prompt: %s", prompt) + case _: + raise ValueError("Invalid source") # run the inference and download the image to a temp file await run_inference(prompt, temp_file) - tx = upload(Path(temp_file), {"Content-Type": "image/png"}) + tx = FileManager(wallet_path=os.environ["ARWEAVE_WALLET_FILE_PATH"]).upload( + Path(temp_file), {"Content-Type": "image/png"} + ) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - """ - In case of an off-chain request, the result is returned as is. - """ - return { - "prompt": prompt, - "hash": tx.id, - "image_url": f"https://arweave.net/{tx.id}", - } - else: - """ - In case of an on-chain request, the result is returned in the format: - { - "raw_input": str, - "processed_input": str, - "raw_output": str, - "processed_output": str, - "proof": str, - } - refer to: https://docs.ritual.net/infernet/node/containers for more info. - """ - return { - "raw_input": infernet_input.data, - "processed_input": "", - "raw_output": encode(["string"], [tx.id]).hex(), - "processed_output": "", - "proof": "", - } + match infernet_input: + case InfernetInput(destination=JobLocation.OFFCHAIN): + """ + In case of an off-chain request, the result is returned as is. + """ + return { + "prompt": prompt, + "hash": tx.id, + "image_url": f"https://arweave.net/{tx.id}", + } + case InfernetInput(destination=JobLocation.ONCHAIN): + """ + In case of an on-chain request, the result is returned in the format: + { + "raw_input": str, + "processed_input": str, + "raw_output": str, + "processed_output": str, + "proof": str, + } + refer to: https://docs.ritual.net/infernet/node/containers for more + info. + """ + return { + "raw_input": infernet_input.data, + "processed_input": "", + "raw_output": encode(["string"], [tx.id]).hex(), + "processed_output": "", + "proof": "", + } + case _: + raise ValueError("Invalid destination") return app diff --git a/projects/prompt-to-nft/container/src/requirements.txt b/projects/prompt-to-nft/container/src/requirements.txt index 5ac781d..7b8e3f3 100644 --- a/projects/prompt-to-nft/container/src/requirements.txt +++ b/projects/prompt-to-nft/container/src/requirements.txt @@ -1,5 +1,4 @@ quart==0.19.4 -infernet_ml==0.1.0 -PyArweave @ git+https://github.com/ritual-net/pyarweave.git +infernet-ml==1.0.0 web3==6.15.0 -tqdm==4.66.1 +tqdm==4.66.3 diff --git a/projects/prompt-to-nft/contracts/lib/forge-std b/projects/prompt-to-nft/contracts/lib/forge-std index e4aef94..52715a2 160000 --- a/projects/prompt-to-nft/contracts/lib/forge-std +++ b/projects/prompt-to-nft/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/projects/prompt-to-nft/contracts/lib/infernet-sdk b/projects/prompt-to-nft/contracts/lib/infernet-sdk index 2d04a7f..8e6cd6f 160000 --- a/projects/prompt-to-nft/contracts/lib/infernet-sdk +++ b/projects/prompt-to-nft/contracts/lib/infernet-sdk @@ -1 +1 @@ -Subproject commit 2d04a7f5ed64738218941e5d7a7270382f191a01 +Subproject commit 8e6cd6f5cbd66dc9baacb895a2ed8fe2c9ee3b6f diff --git a/projects/prompt-to-nft/contracts/script/CallContract.s.sol b/projects/prompt-to-nft/contracts/script/CallContract.s.sol index f1c5906..d248bbd 100644 --- a/projects/prompt-to-nft/contracts/script/CallContract.s.sol +++ b/projects/prompt-to-nft/contracts/script/CallContract.s.sol @@ -5,7 +5,8 @@ import {Script, console2} from "forge-std/Script.sol"; import {DiffusionNFT} from "../src/DiffusionNFT.sol"; contract CallContract is Script { -string defaultPrompt = "A picture of a shrimp dunking a basketball"; + string defaultPrompt = "A picture of a shrimp dunking a basketball"; + function run() public { // Setup wallet uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); @@ -13,7 +14,7 @@ string defaultPrompt = "A picture of a shrimp dunking a basketball"; string memory prompt = vm.envOr("prompt", defaultPrompt); vm.startBroadcast(deployerPrivateKey); - DiffusionNFT nft = DiffusionNFT(0x663F3ad617193148711d28f5334eE4Ed07016602); + DiffusionNFT nft = DiffusionNFT(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e); nft.mint(prompt, mintTo); diff --git a/projects/prompt-to-nft/contracts/script/Deploy.s.sol b/projects/prompt-to-nft/contracts/script/Deploy.s.sol index 4520c16..bfaacc0 100644 --- a/projects/prompt-to-nft/contracts/script/Deploy.s.sol +++ b/projects/prompt-to-nft/contracts/script/Deploy.s.sol @@ -14,9 +14,9 @@ contract Deploy is Script { address deployerAddress = vm.addr(deployerPrivateKey); console2.log("Loaded deployer: ", deployerAddress); - address coordinator = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; // Create consumer - DiffusionNFT nft = new DiffusionNFT(coordinator); + DiffusionNFT nft = new DiffusionNFT(registry); console2.log("Deployed IrisClassifier: ", address(nft)); // Execute diff --git a/projects/prompt-to-nft/contracts/src/DiffusionNFT.sol b/projects/prompt-to-nft/contracts/src/DiffusionNFT.sol index 4f6c151..1576bd4 100644 --- a/projects/prompt-to-nft/contracts/src/DiffusionNFT.sol +++ b/projects/prompt-to-nft/contracts/src/DiffusionNFT.sol @@ -11,10 +11,18 @@ contract DiffusionNFT is CallbackConsumer, ERC721 { "| _ / | | | | | | | |/ /\\ \\ | |\n" "| | \\ \\ _| |_ | | | |__| / ____ \\| |____\n" "|_| \\_\\_____| |_| \\____/_/ \\_\\______|\n\n"; - constructor(address coordinator) CallbackConsumer(coordinator) ERC721("DiffusionNFT", "DN") {} + constructor(address registry) CallbackConsumer(registry) ERC721("DiffusionNFT", "DN") {} function mint(string memory prompt, address to) public { - _requestCompute("prompt-to-nft", abi.encode(prompt, to), 20 gwei, 1_000_000, 1); + _requestCompute( + "prompt-to-nft", + abi.encode(prompt, to), + 1, // redundancy + address(0), // paymentToken + 0, // paymentAmount + address(0), // wallet + address(0) // prover + ); } uint256 public counter = 0; @@ -38,7 +46,6 @@ contract DiffusionNFT is CallbackConsumer, ERC721 { return collection; } - function _receiveCompute( uint32 subscriptionId, uint32 interval, @@ -46,7 +53,9 @@ contract DiffusionNFT is CallbackConsumer, ERC721 { address node, bytes calldata input, bytes calldata output, - bytes calldata proof + bytes calldata proof, + bytes32 containerId, + uint256 index ) internal override { console2.log(EXTREMELY_COOL_BANNER); (bytes memory raw_output, bytes memory processed_output) = abi.decode(output, (bytes, bytes)); diff --git a/projects/prompt-to-nft/prompt-to-nft.md b/projects/prompt-to-nft/prompt-to-nft.md index 1df6f7c..fb186c1 100644 --- a/projects/prompt-to-nft/prompt-to-nft.md +++ b/projects/prompt-to-nft/prompt-to-nft.md @@ -262,7 +262,7 @@ Notice that in [one of the steps above](#check-the-running-containers) we have a By default, the [`anvil-node`](https://hub.docker.com/r/ritualnetwork/infernet-anvil) image used deploys the [Infernet SDK](https://docs.ritual.net/infernet/sdk/introduction) and other relevant contracts for you: -- Coordinator: `0x5FbDB2315678afecb367f032d93F642f64180aa3` +- Registry: `0x663F3ad617193148711d28f5334eE4Ed07016602` - Primary node: `0x70997970C51812dc3A010C7d01b50e0d17dc79C8` ### Deploy our NFT Consumer contract @@ -296,7 +296,7 @@ You should expect to see similar Anvil logs: eth_getTransactionReceipt Transaction: 0x0577dc98192d971bafb30d53cb217c9a9c16f92ab435d20a697024a4f122c048 -Contract created: 0x663f3ad617193148711d28f5334ee4ed07016602 +Contract created: 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e Gas used: 1582129 Block Number: 1 @@ -307,7 +307,7 @@ eth_getTransactionByHash ``` From our logs, we can see that the `DiffusionNFT` contract has been deployed to address -`0x663f3ad617193148711d28f5334ee4ed07016602`. +`0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e`. ### Call the contract diff --git a/projects/prompt-to-nft/stablediffusion/Dockerfile b/projects/prompt-to-nft/stablediffusion/Dockerfile index c83a338..2e2852b 100644 --- a/projects/prompt-to-nft/stablediffusion/Dockerfile +++ b/projects/prompt-to-nft/stablediffusion/Dockerfile @@ -5,6 +5,8 @@ WORKDIR /app ENV PYTHONUNBUFFERED 1 ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} WORKDIR /app @@ -12,7 +14,8 @@ RUN apt-get update RUN apt-get install -y git curl ffmpeg libsm6 libxext6 # install uv -ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh +ADD https://astral.sh/uv/install.sh /install.sh +RUN chmod 755 /install.sh RUN /install.sh && rm /install.sh COPY src/requirements.txt . diff --git a/projects/prompt-to-nft/stablediffusion/Makefile b/projects/prompt-to-nft/stablediffusion/Makefile index e86d22c..a6f8288 100644 --- a/projects/prompt-to-nft/stablediffusion/Makefile +++ b/projects/prompt-to-nft/stablediffusion/Makefile @@ -5,7 +5,7 @@ TAG := $(DOCKER_ORG)/example-$(EXAMPLE_NAME)-infernet:latest .phony: build run build-multiplatform build: - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . port_mapping ?= 0.0.0.0:3002:3000 diff --git a/projects/prompt-to-nft/stablediffusion/src/requirements.txt b/projects/prompt-to-nft/stablediffusion/src/requirements.txt index e2c6fae..5443795 100644 --- a/projects/prompt-to-nft/stablediffusion/src/requirements.txt +++ b/projects/prompt-to-nft/stablediffusion/src/requirements.txt @@ -1,10 +1,9 @@ diffusers~=0.19 invisible_watermark~=0.1 -transformers==4.36 +transformers==4.38.0 accelerate~=0.21 safetensors~=0.3 Quart==0.19.4 jmespath==1.0.1 huggingface-hub==0.20.3 -infernet_ml==0.1.0 -PyArweave @ git+https://github.com/ritual-net/pyarweave.git +infernet-ml==1.0.0 diff --git a/projects/prompt-to-nft/ui/package.json b/projects/prompt-to-nft/ui/package.json index 258e3c3..8e6c944 100644 --- a/projects/prompt-to-nft/ui/package.json +++ b/projects/prompt-to-nft/ui/package.json @@ -11,7 +11,7 @@ "dependencies": { "@rainbow-me/rainbowkit": "^2.0.0", "@tanstack/react-query": "^5.22.2", - "next": "14.1.0", + "next": "14.1.1", "prettier": "^3.2.5", "react": "^18", "react-dom": "^18", @@ -27,6 +27,6 @@ "postcss": "^8", "tailwindcss": "^3.3.0", "eslint": "^8", - "eslint-config-next": "14.1.0" + "eslint-config-next": "14.1.1" } } diff --git a/projects/tgi-llm/container/Dockerfile b/projects/tgi-llm/container/Dockerfile index 57add3f..92d4f77 100644 --- a/projects/tgi-llm/container/Dockerfile +++ b/projects/tgi-llm/container/Dockerfile @@ -7,12 +7,15 @@ ENV PYTHONDONTWRITEBYTECODE 1 ENV PIP_NO_CACHE_DIR 1 ENV RUNTIME docker ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} RUN apt-get update RUN apt-get install -y git curl # install uv -ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh +ADD https://astral.sh/uv/install.sh /install.sh +RUN chmod 755 /install.sh RUN /install.sh && rm /install.sh COPY src/requirements.txt . diff --git a/projects/tgi-llm/container/Makefile b/projects/tgi-llm/container/Makefile index 2df9b24..f15c933 100644 --- a/projects/tgi-llm/container/Makefile +++ b/projects/tgi-llm/container/Makefile @@ -5,7 +5,7 @@ TAG := $(DOCKER_ORG)/example-$(EXAMPLE_NAME)-infernet:latest .phony: build run build-multiplatform build: - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . run: docker run -p 3000:3000 --env-file tgi-llm.env $(TAG) diff --git a/projects/tgi-llm/container/config.sample.json b/projects/tgi-llm/container/config.sample.json index cbbb1fb..0e239ff 100644 --- a/projects/tgi-llm/container/config.sample.json +++ b/projects/tgi-llm/container/config.sample.json @@ -1,52 +1,46 @@ { - "log_path": "infernet_node.log", - "server": { - "port": 4000 - }, - "chain": { - "enabled": true, - "trail_head_blocks": 0, - "rpc_url": "http://host.docker.internal:8545", - "coordinator_address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "wallet": { - "max_gas_limit": 4000000, - "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" - } - }, - "startup_wait": 1.0, - "docker": { - "username": "your-username", - "password": "" - }, - "redis": { - "host": "redis", - "port": 6379 - }, - "forward_stats": true, - "containers": [ - { - "id": "tgi-llm", - "image": "ritualnetwork/example-tgi-llm-infernet:latest", - "external": true, - "port": "3000", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "--bind=0.0.0.0:3000 --workers=2", - "env": { - "TGI_SERVICE_URL": "http://{your_service_ip}:{your_service_port}" - } + "log_path": "infernet_node.log", + "server": { + "port": 4000 }, - { - "id": "anvil-node", - "image": "ritualnetwork/infernet-anvil:0.0.0", - "external": true, - "port": "8545", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "", - "env": {} - } - ] + "chain": { + "enabled": true, + "trail_head_blocks": 0, + "rpc_url": "http://host.docker.internal:8545", + "registry_address": "0x663F3ad617193148711d28f5334eE4Ed07016602", + "wallet": { + "max_gas_limit": 4000000, + "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" + } + }, + "startup_wait": 1.0, + "docker": { + "username": "your-username", + "password": "" + }, + "redis": { + "host": "redis", + "port": 6379 + }, + "forward_stats": true, + "snapshot_sync": { + "sleep": 3, + "batch_size": 100 + }, + "containers": [ + { + "id": "tgi-llm", + "image": "ritualnetwork/example-tgi-llm-infernet:latest", + "external": true, + "port": "3000", + "allowed_delegate_addresses": [], + "allowed_addresses": [], + "allowed_ips": [], + "command": "--bind=0.0.0.0:3000 --workers=2", + "env": { + "TGI_SERVICE_URL": "http://{your_service_ip}:{your_service_port}" + }, + "accepted_payments": {} + } + ] } diff --git a/projects/tgi-llm/container/src/app.py b/projects/tgi-llm/container/src/app.py index abcd185..3b293f5 100644 --- a/projects/tgi-llm/container/src/app.py +++ b/projects/tgi-llm/container/src/app.py @@ -2,10 +2,11 @@ import logging import os from typing import Any, cast -from eth_abi import decode, encode # type: ignore -from infernet_ml.utils.service_models import InfernetInput, InfernetInputSource +from eth_abi.abi import decode, encode +from infernet_ml.utils.service_models import InfernetInput, JobLocation from infernet_ml.workflows.inference.tgi_client_inference_workflow import ( TGIClientInferenceWorkflow, + TgiInferenceRequest, ) from quart import Quart, request @@ -16,7 +17,7 @@ def create_app() -> Quart: app = Quart(__name__) workflow = TGIClientInferenceWorkflow( - server_url=cast(str, os.environ.get("TGI_SERVICE_URL")) + server_url=os.environ["TGI_SERVICE_URL"], ) workflow.setup() @@ -38,42 +39,51 @@ def create_app() -> Quart: """ infernet_input: InfernetInput = InfernetInput(**req_data) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - prompt = cast(dict[str, Any], infernet_input.data).get("prompt") - else: - # On-chain requests are sent as a generalized hex-string which we will - # decode to the appropriate format. - (prompt,) = decode( - ["string"], bytes.fromhex(cast(str, infernet_input.data)) - ) + match infernet_input: + case InfernetInput(source=JobLocation.OFFCHAIN): + prompt = cast(dict[str, Any], infernet_input.data).get("prompt") + case InfernetInput(source=JobLocation.ONCHAIN): + # On-chain requests are sent as a generalized hex-string which we will + # decode to the appropriate format. + (prompt,) = decode( + ["string"], bytes.fromhex(cast(str, infernet_input.data)) + ) + case _: + raise ValueError("Invalid source") - result: dict[str, Any] = workflow.inference({"text": prompt}) + result: dict[str, Any] = workflow.inference( + TgiInferenceRequest(text=cast(str, prompt)) + ) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - """ - In case of an off-chain request, the result is returned as a dict. The - infernet node expects a dict format. - """ - return {"data": result} - else: - """ - In case of an on-chain request, the result is returned in the format: - { - "raw_input": str, - "processed_input": str, - "raw_output": str, - "processed_output": str, - "proof": str, - } - refer to: https://docs.ritual.net/infernet/node/containers for more info. - """ - return { - "raw_input": "", - "processed_input": "", - "raw_output": encode(["string"], [result]).hex(), - "processed_output": "", - "proof": "", - } + match infernet_input: + case InfernetInput(destination=JobLocation.OFFCHAIN): + """ + In case of an off-chain request, the result is returned as a dict. The + infernet node expects a dict format. + """ + return {"data": result} + case InfernetInput(destination=JobLocation.ONCHAIN): + """ + In case of an on-chain request, the result is returned in the format: + { + "raw_input": str, + "processed_input": str, + "raw_output": str, + "processed_output": str, + "proof": str, + } + refer to: https://docs.ritual.net/infernet/node/containers for more + info. + """ + return { + "raw_input": "", + "processed_input": "", + "raw_output": encode(["string"], [result]).hex(), + "processed_output": "", + "proof": "", + } + case _: + raise ValueError("Invalid destination") return app diff --git a/projects/tgi-llm/container/src/requirements.txt b/projects/tgi-llm/container/src/requirements.txt index 0ef237a..10c6215 100644 --- a/projects/tgi-llm/container/src/requirements.txt +++ b/projects/tgi-llm/container/src/requirements.txt @@ -1,6 +1,5 @@ quart==0.19.4 -infernet_ml==0.1.0 -PyArweave @ git+https://github.com/ritual-net/pyarweave.git +infernet-ml==1.0.0 +infernet-ml[tgi_inference]==1.0.0 web3==6.15.0 retry2==0.9.5 -text-generation==0.6.1 diff --git a/projects/tgi-llm/contracts/lib/forge-std b/projects/tgi-llm/contracts/lib/forge-std index e4aef94..52715a2 160000 --- a/projects/tgi-llm/contracts/lib/forge-std +++ b/projects/tgi-llm/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/projects/tgi-llm/contracts/lib/infernet-sdk b/projects/tgi-llm/contracts/lib/infernet-sdk index 2d04a7f..8e6cd6f 160000 --- a/projects/tgi-llm/contracts/lib/infernet-sdk +++ b/projects/tgi-llm/contracts/lib/infernet-sdk @@ -1 +1 @@ -Subproject commit 2d04a7f5ed64738218941e5d7a7270382f191a01 +Subproject commit 8e6cd6f5cbd66dc9baacb895a2ed8fe2c9ee3b6f diff --git a/projects/tgi-llm/contracts/script/CallContract.s.sol b/projects/tgi-llm/contracts/script/CallContract.s.sol index 750b58b..5c7a3d1 100644 --- a/projects/tgi-llm/contracts/script/CallContract.s.sol +++ b/projects/tgi-llm/contracts/script/CallContract.s.sol @@ -10,7 +10,7 @@ contract CallContract is Script { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); - Prompter prompter = Prompter(0x663F3ad617193148711d28f5334eE4Ed07016602); + Prompter prompter = Prompter(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e); prompter.promptLLM(vm.envString("prompt")); diff --git a/projects/tgi-llm/contracts/script/Deploy.s.sol b/projects/tgi-llm/contracts/script/Deploy.s.sol index 302d2c0..b467042 100644 --- a/projects/tgi-llm/contracts/script/Deploy.s.sol +++ b/projects/tgi-llm/contracts/script/Deploy.s.sol @@ -14,9 +14,9 @@ contract Deploy is Script { address deployerAddress = vm.addr(deployerPrivateKey); console2.log("Loaded deployer: ", deployerAddress); - address coordinator = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; // Create consumer - Prompter prompter = new Prompter(coordinator); + Prompter prompter = new Prompter(registry); console2.log("Deployed Prompter: ", address(prompter)); // Execute diff --git a/projects/tgi-llm/contracts/src/Prompter.sol b/projects/tgi-llm/contracts/src/Prompter.sol index 596afe1..cfac02a 100644 --- a/projects/tgi-llm/contracts/src/Prompter.sol +++ b/projects/tgi-llm/contracts/src/Prompter.sol @@ -13,15 +13,17 @@ contract Prompter is CallbackConsumer { "| | \\ \\ _| |_ | | | |__| / ____ \\| |____ \n" "|_| \\_\\_____| |_| \\____/_/ \\_\\______| \n\n"; - constructor(address coordinator) CallbackConsumer(coordinator) {} + constructor(address registry) CallbackConsumer(registry) {} function promptLLM(string calldata prompt) public { _requestCompute( "tgi-llm", abi.encode(prompt), - 20 gwei, - 1_000_000, - 1 + 1, // redundancy + address(0), // paymentToken + 0, // paymentAmount + address(0), // wallet + address(0) // prover ); } @@ -32,7 +34,9 @@ contract Prompter is CallbackConsumer { address node, bytes calldata input, bytes calldata output, - bytes calldata proof + bytes calldata proof, + bytes32 containerId, + uint256 index ) internal override { console2.log(EXTREMELY_COOL_BANNER); (bytes memory raw_output, bytes memory processed_output) = abi.decode(output, (bytes, bytes)); diff --git a/projects/tgi-llm/tgi-llm.md b/projects/tgi-llm/tgi-llm.md index bc9cd25..75a935e 100644 --- a/projects/tgi-llm/tgi-llm.md +++ b/projects/tgi-llm/tgi-llm.md @@ -334,7 +334,7 @@ Notice that in [the step above](#check-the-running-containers) we have an Anvil By default, the [`anvil-node`](https://hub.docker.com/r/ritualnetwork/infernet-anvil) image used deploys the [Infernet SDK](https://docs.ritual.net/infernet/sdk/introduction) and other relevant contracts for you: -- Coordinator: `0x5FbDB2315678afecb367f032d93F642f64180aa3` +- Coordinator: `0x663F3ad617193148711d28f5334eE4Ed07016602` - Primary node: `0x70997970C51812dc3A010C7d01b50e0d17dc79C8` ### Deploy our `Prompter` smart contract @@ -367,7 +367,7 @@ You should expect to see similar Anvil logs: eth_getTransactionReceipt Transaction: 0x17a9d17cc515d39eef26b6a9427e04ed6f7ce6572d9756c07305c2df78d93ffe -Contract created: 0x663f3ad617193148711d28f5334ee4ed07016602 +Contract created: 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e Gas used: 731312 Block Number: 1 @@ -378,7 +378,7 @@ eth_getTransactionByHash ``` From our logs, we can see that the `Prompter` contract has been deployed to address -`0x663f3ad617193148711d28f5334ee4ed07016602`. +`0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e`. ### Call the contract diff --git a/projects/tgi-llm/ui/Dockerfile b/projects/tgi-llm/ui/Dockerfile index 353c6b4..87f1d6b 100644 --- a/projects/tgi-llm/ui/Dockerfile +++ b/projects/tgi-llm/ui/Dockerfile @@ -5,6 +5,8 @@ WORKDIR /app ENV PYTHONUNBUFFERED 1 ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} WORKDIR /app diff --git a/projects/tgi-llm/ui/Makefile b/projects/tgi-llm/ui/Makefile index 9122989..80521c1 100644 --- a/projects/tgi-llm/ui/Makefile +++ b/projects/tgi-llm/ui/Makefile @@ -5,7 +5,7 @@ TAG := $(DOCKER_ORG)/example-$(EXAMPLE_NAME)-infernet:latest .phony: build run publish build: - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . run: build docker run --env-file ./gradio_ui.env -p 3001:7860 $(TAG) diff --git a/projects/tgi-llm/ui/src/requirements.txt b/projects/tgi-llm/ui/src/requirements.txt index 9bff6ea..470a5f3 100644 --- a/projects/tgi-llm/ui/src/requirements.txt +++ b/projects/tgi-llm/ui/src/requirements.txt @@ -1,4 +1,4 @@ python-dotenv==1.0.0 -gradio==3.47.1 +gradio==4.19.2 huggingface-hub==0.17.3 text-generation==0.6.1 diff --git a/projects/torch-iris/container/Dockerfile b/projects/torch-iris/container/Dockerfile index 57add3f..92d4f77 100644 --- a/projects/torch-iris/container/Dockerfile +++ b/projects/torch-iris/container/Dockerfile @@ -7,12 +7,15 @@ ENV PYTHONDONTWRITEBYTECODE 1 ENV PIP_NO_CACHE_DIR 1 ENV RUNTIME docker ENV PYTHONPATH src +ARG index_url +ENV UV_EXTRA_INDEX_URL ${index_url} RUN apt-get update RUN apt-get install -y git curl # install uv -ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh +ADD https://astral.sh/uv/install.sh /install.sh +RUN chmod 755 /install.sh RUN /install.sh && rm /install.sh COPY src/requirements.txt . diff --git a/projects/torch-iris/container/Makefile b/projects/torch-iris/container/Makefile index ea37b79..a6d83dc 100644 --- a/projects/torch-iris/container/Makefile +++ b/projects/torch-iris/container/Makefile @@ -5,7 +5,7 @@ TAG := $(DOCKER_ORG)/example-$(EXAMPLE_NAME)-infernet:latest .phony: build run build: - @docker build -t $(TAG) . + @docker build -t $(TAG) --build-arg index_url=$(index_url) . run: docker run -p 3000:3000 $(TAG) diff --git a/projects/torch-iris/container/README.md b/projects/torch-iris/container/README.md index 41e1246..82dbd16 100644 --- a/projects/torch-iris/container/README.md +++ b/projects/torch-iris/container/README.md @@ -8,9 +8,9 @@ repository. ## Overview We're making use of -the [TorchInferenceWorkflow](https://github.com/ritual-net/infernet-ml-internal/blob/main/src/ml/workflows/inference/torch_inference_workflow.py) +the [TorchInferenceWorkflow](https://github.com/ritual-net/infernet-ml/blob/main/src/ml/workflows/inference/torch_inference_workflow.py) class to run the model. This is one of many workflows that we currently support in our -[infernet-ml](https://github.com/ritual-net/infernet-ml-internal). Consult the library's +[infernet-ml](https://github.com/ritual-net/infernet-ml). Consult the library's documentation for more info on workflows that are supported. diff --git a/projects/torch-iris/container/config.json b/projects/torch-iris/container/config.json index 369625a..faeee85 100644 --- a/projects/torch-iris/container/config.json +++ b/projects/torch-iris/container/config.json @@ -7,7 +7,7 @@ "enabled": true, "trail_head_blocks": 0, "rpc_url": "http://host.docker.internal:8545", - "coordinator_address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "registry_address": "0x663F3ad617193148711d28f5334eE4Ed07016602", "wallet": { "max_gas_limit": 4000000, "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" @@ -23,6 +23,10 @@ "port": 6379 }, "forward_stats": true, + "snapshot_sync": { + "sleep": 3, + "batch_size": 100 + }, "containers": [ { "id": "torch-iris", @@ -33,18 +37,8 @@ "allowed_addresses": [], "allowed_ips": [], "command": "--bind=0.0.0.0:3000 --workers=2", - "env": {} - }, - { - "id": "anvil-node", - "image": "ritualnetwork/infernet-anvil:0.0.0", - "external": true, - "port": "8545", - "allowed_delegate_addresses": [], - "allowed_addresses": [], - "allowed_ips": [], - "command": "", - "env": {} + "env": {}, + "accepted_payments": {} } ] } diff --git a/projects/torch-iris/container/src/app.py b/projects/torch-iris/container/src/app.py index def8526..4386ea3 100644 --- a/projects/torch-iris/container/src/app.py +++ b/projects/torch-iris/container/src/app.py @@ -1,11 +1,16 @@ import logging from typing import Any, cast, List +from infernet_ml.utils.common_types import TensorInput from eth_abi import decode, encode # type: ignore +from infernet_ml.utils.model_loader import ( + HFLoadArgs, +) from infernet_ml.utils.model_loader import ModelSource -from infernet_ml.utils.service_models import InfernetInput, InfernetInputSource +from infernet_ml.utils.service_models import InfernetInput, JobLocation from infernet_ml.workflows.inference.torch_inference_workflow import ( TorchInferenceWorkflow, + TorchInferenceInput, ) from quart import Quart, request @@ -21,10 +26,10 @@ def create_app() -> Quart: app = Quart(__name__) # we are downloading the model from the hub. # model repo is located at: https://huggingface.co/Ritual-Net/iris-dataset - model_source = ModelSource.HUGGINGFACE_HUB - model_args = {"repo_id": "Ritual-Net/iris-dataset", "filename": "iris.torch"} - - workflow = TorchInferenceWorkflow(model_source=model_source, model_args=model_args) + workflow = TorchInferenceWorkflow( + model_source=ModelSource.HUGGINGFACE_HUB, + load_args=HFLoadArgs(repo_id="Ritual-Net/iris-dataset", filename="iris.torch"), + ) workflow.setup() @app.route("/") @@ -46,16 +51,17 @@ def create_app() -> Quart: """ infernet_input: InfernetInput = InfernetInput(**req_data) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - web2_input = cast(dict[str, Any], infernet_input.data) - values = cast(List[List[float]], web2_input["input"]) - else: - # On-chain requests are sent as a generalized hex-string which we will - # decode to the appropriate format. - web3_input: List[int] = decode( - ["uint256[]"], bytes.fromhex(cast(str, infernet_input.data)) - )[0] - values = [[float(v) / 1e6 for v in web3_input]] + match infernet_input: + case InfernetInput(source=JobLocation.OFFCHAIN): + web2_input = cast(dict[str, Any], infernet_input.data) + values = cast(List[List[float]], web2_input["input"]) + case InfernetInput(source=JobLocation.ONCHAIN): + web3_input: List[int] = decode( + ["uint256[]"], bytes.fromhex(cast(str, infernet_input.data)) + )[0] + values = [[float(v) / 1e6 for v in web3_input]] + case _: + raise ValueError("Invalid source") """ The input to the torch inference workflow needs to conform to this format: @@ -66,39 +72,52 @@ def create_app() -> Quart: } For more information refer to: - https://docs.ritual.net/ml-workflows/inference-workflows/torch_inference_workflow + https://infernet-ml.docs.ritual.net/reference/infernet_ml/workflows/inference/torch_inference_workflow/?h=torch - """ - inference_result = workflow.inference({"dtype": "float", "values": values}) + """ # noqa: E501 + log.info("Input values: %s", values) - result = [o.detach().numpy().reshape([-1]).tolist() for o in inference_result] + _input = TensorInput( + dtype="float", + shape=(1, 4), + values=values, + ) - if infernet_input.source == InfernetInputSource.OFFCHAIN: - """ - In case of an off-chain request, the result is returned as is. - """ - return {"result": result} - else: - """ - In case of an on-chain request, the result is returned in the format: - { - "raw_input": str, - "processed_input": str, - "raw_output": str, - "processed_output": str, - "proof": str, - } - refer to: https://docs.ritual.net/infernet/node/containers for more info. - """ - predictions = cast(List[List[float]], result) - predictions_normalized = [int(p * 1e6) for p in predictions[0]] - return { - "raw_input": "", - "processed_input": "", - "raw_output": encode(["uint256[]"], [predictions_normalized]).hex(), - "processed_output": "", - "proof": "", - } + iris_inference_input = TorchInferenceInput(input=_input) + + inference_result = workflow.inference(iris_inference_input) + + result = inference_result.outputs + + match infernet_input: + case InfernetInput(destination=JobLocation.OFFCHAIN): + """ + In case of an off-chain request, the result is returned as is. + """ + return {"result": result} + case InfernetInput(destination=JobLocation.ONCHAIN): + """ + In case of an on-chain request, the result is returned in the format: + { + "raw_input": str, + "processed_input": str, + "raw_output": str, + "processed_output": str, + "proof": str, + } + refer to: https://docs.ritual.net/infernet/node/containers for more + info. + """ + predictions_normalized = [int(p * 1e6) for p in result] + return { + "raw_input": "", + "processed_input": "", + "raw_output": encode(["uint256[]"], [predictions_normalized]).hex(), + "processed_output": "", + "proof": "", + } + case _: + raise ValueError("Invalid destination") return app diff --git a/projects/torch-iris/container/src/requirements.txt b/projects/torch-iris/container/src/requirements.txt index c8347e3..3998870 100644 --- a/projects/torch-iris/container/src/requirements.txt +++ b/projects/torch-iris/container/src/requirements.txt @@ -1,6 +1,6 @@ quart==0.19.4 -infernet_ml==0.1.0 -PyArweave @ git+https://github.com/ritual-net/pyarweave.git +infernet-ml==1.0.0 +infernet-ml[torch_inference]==1.0.0 huggingface-hub==0.17.3 sk2torch==1.2.0 torch==2.1.2 diff --git a/projects/torch-iris/contracts/lib/forge-std b/projects/torch-iris/contracts/lib/forge-std index e4aef94..52715a2 160000 --- a/projects/torch-iris/contracts/lib/forge-std +++ b/projects/torch-iris/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/projects/torch-iris/contracts/lib/infernet-sdk b/projects/torch-iris/contracts/lib/infernet-sdk index 2d04a7f..8e6cd6f 160000 --- a/projects/torch-iris/contracts/lib/infernet-sdk +++ b/projects/torch-iris/contracts/lib/infernet-sdk @@ -1 +1 @@ -Subproject commit 2d04a7f5ed64738218941e5d7a7270382f191a01 +Subproject commit 8e6cd6f5cbd66dc9baacb895a2ed8fe2c9ee3b6f diff --git a/projects/torch-iris/contracts/script/CallContract.s.sol b/projects/torch-iris/contracts/script/CallContract.s.sol index 3612da2..6660371 100644 --- a/projects/torch-iris/contracts/script/CallContract.s.sol +++ b/projects/torch-iris/contracts/script/CallContract.s.sol @@ -10,7 +10,7 @@ contract CallContract is Script { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); - IrisClassifier irisClassifier = IrisClassifier(0x663F3ad617193148711d28f5334eE4Ed07016602); + IrisClassifier irisClassifier = IrisClassifier(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e); irisClassifier.classifyIris(); diff --git a/projects/torch-iris/contracts/script/Deploy.s.sol b/projects/torch-iris/contracts/script/Deploy.s.sol index 94fb53e..9cf4cc6 100644 --- a/projects/torch-iris/contracts/script/Deploy.s.sol +++ b/projects/torch-iris/contracts/script/Deploy.s.sol @@ -14,9 +14,9 @@ contract Deploy is Script { address deployerAddress = vm.addr(deployerPrivateKey); console2.log("Loaded deployer: ", deployerAddress); - address coordinator = 0x5FbDB2315678afecb367f032d93F642f64180aa3; + address registry = 0x663F3ad617193148711d28f5334eE4Ed07016602; // Create consumer - IrisClassifier classifier = new IrisClassifier(coordinator); + IrisClassifier classifier = new IrisClassifier(registry); console2.log("Deployed IrisClassifier: ", address(classifier)); // Execute diff --git a/projects/torch-iris/contracts/src/IrisClassifier.sol b/projects/torch-iris/contracts/src/IrisClassifier.sol index 77ddade..d268012 100644 --- a/projects/torch-iris/contracts/src/IrisClassifier.sol +++ b/projects/torch-iris/contracts/src/IrisClassifier.sol @@ -14,7 +14,7 @@ contract IrisClassifier is CallbackConsumer { "| | \\ \\ _| |_ | | | |__| / ____ \\| |____\n" "|_| \\_\\_____| |_| \\____/_/ \\_\\______|\n\n"; - constructor(address coordinator) CallbackConsumer(coordinator) {} + constructor(address registry) CallbackConsumer(registry) {} function classifyIris() public { /// @dev Iris data is in the following format: @@ -38,9 +38,11 @@ contract IrisClassifier is CallbackConsumer { _requestCompute( "torch-iris", abi.encode(iris_data), - 20 gwei, - 1_000_000, - 1 + 1, // redundancy + address(0), // paymentToken + 0, // paymentAmount + address(0), // wallet + address(0) // prover ); } @@ -51,7 +53,9 @@ contract IrisClassifier is CallbackConsumer { address node, bytes calldata input, bytes calldata output, - bytes calldata proof + bytes calldata proof, + bytes32 containerId, + uint256 index ) internal override { console2.log(EXTREMELY_COOL_BANNER); (bytes memory raw_output, bytes memory processed_output) = abi.decode(output, (bytes, bytes)); diff --git a/projects/torch-iris/torch-iris.md b/projects/torch-iris/torch-iris.md index 1d5fbcd..40e490b 100644 --- a/projects/torch-iris/torch-iris.md +++ b/projects/torch-iris/torch-iris.md @@ -179,7 +179,7 @@ We already have a public [anvil node](https://hub.docker.com/r/ritualnetwork/inf corresponding infernet sdk contracts deployed, along with a node that has registered itself to listen to on-chain subscription events. -* Coordinator Address: `0x5FbDB2315678afecb367f032d93F642f64180aa3` +* Registry Address: `0x663F3ad617193148711d28f5334eE4Ed07016602` * Node Address: `0x70997970C51812dc3A010C7d01b50e0d17dc79C8` (This is the second account in the anvil's accounts.) ### Monitoring the EVM Logs @@ -210,7 +210,7 @@ eth_sendRawTransaction eth_getTransactionReceipt Transaction: 0x8e7e96d0a062285ee6fea864c43c29af65b962d260955e6284ab79dae145b32c - Contract created: 0x663f3ad617193148711d28f5334ee4ed07016602 + Contract created: 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e Gas used: 725947 Block Number: 1 @@ -224,7 +224,7 @@ eth_blockNumber ``` beautiful, we can see that a new contract has been created -at `0x663f3ad617193148711d28f5334ee4ed07016602`. That's the address of +at `0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e`. That's the address of the `IrisClassifier` contract. We are now going to call this contract. To do so, we are using the [CallContract.s.sol](contracts/script/CallContract.s.sol) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..acd5233 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[project] +name = "infernet-container-starter" +version = "1.0.0" +description = "A repository of infernet examples" +authors = [ + { name = "ritual", email = "hello@ritual.net" } +] + +readme = "README.md" +requires-python = ">= 3.11" + +[tool.mypy] +exclude = ['**/venv', '**/.venv'] + +[tool.ruff] +line-length = 89 + +[tool.isort] +profile = "black" +skip = [".venv", "venv", ".gitignore"] diff --git a/requirements.txt b/requirements.txt index 4034edb..b540461 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -gunicorn==21.2.0 +gunicorn==22.0.0 mypy==1.8.0 mypy-extensions==1.0.0 packaging==23.2 -requests==2.31.0 +requests==2.32.0 ruff==0.1.13 types-click==7.1.8 types-Flask==1.1.6 @@ -13,10 +13,12 @@ types-Werkzeug==1.0.9 typing_extensions==4.9.0 Flask==3.0.0 quart==0.19.4 -infernet_ml==0.1.0 -PyArweave @ git+https://github.com/ritual-net/pyarweave.git +infernet-ml==1.0.0 +infernet-ml[torch_inference]==1.0.0 +infernet-ml[onnx_inference]==1.0.0 +infernet-ml[css_inference]==1.0.0 torch==2.2.1 web3==6.15.0 -onnx==1.15.0 +onnx==1.16.0 onnxruntime==1.17.1 pre-commit==2.15.0