opengradient 0.3.11__tar.gz → 0.3.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. opengradient-0.3.13/.gitignore +162 -0
  2. {opengradient-0.3.11/src/opengradient.egg-info → opengradient-0.3.13}/PKG-INFO +17 -17
  3. {opengradient-0.3.11 → opengradient-0.3.13}/pyproject.toml +17 -5
  4. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/__init__.py +23 -2
  5. opengradient-0.3.13/src/opengradient/abi/inference.abi +1 -0
  6. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/cli.py +38 -0
  7. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/client.py +135 -1
  8. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/defaults.py +3 -2
  9. opengradient-0.3.13/src/opengradient/proto/__init__.py +2 -0
  10. opengradient-0.3.13/src/opengradient/proto/infer.proto +50 -0
  11. opengradient-0.3.13/src/opengradient/proto/infer_pb2.py +50 -0
  12. opengradient-0.3.13/src/opengradient/proto/infer_pb2_grpc.py +186 -0
  13. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/types.py +3 -2
  14. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/utils.py +14 -1
  15. opengradient-0.3.11/PKG-INFO +0 -230
  16. opengradient-0.3.11/setup.cfg +0 -4
  17. opengradient-0.3.11/src/opengradient/abi/inference.abi +0 -1
  18. opengradient-0.3.11/src/opengradient.egg-info/SOURCES.txt +0 -20
  19. opengradient-0.3.11/src/opengradient.egg-info/dependency_links.txt +0 -1
  20. opengradient-0.3.11/src/opengradient.egg-info/entry_points.txt +0 -2
  21. opengradient-0.3.11/src/opengradient.egg-info/requires.txt +0 -94
  22. opengradient-0.3.11/src/opengradient.egg-info/top_level.txt +0 -1
  23. {opengradient-0.3.11 → opengradient-0.3.13}/LICENSE +0 -0
  24. {opengradient-0.3.11 → opengradient-0.3.13}/README.md +0 -0
  25. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/account.py +0 -0
  26. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/exceptions.py +0 -0
  27. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/llm/__init__.py +0 -0
  28. {opengradient-0.3.11 → opengradient-0.3.13}/src/opengradient/llm/chat.py +0 -0
@@ -0,0 +1,162 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mkdocs documentation
141
+ /site
142
+
143
+ # mypy
144
+ .mypy_cache/
145
+ .dmypy.json
146
+ dmypy.json
147
+
148
+ # Pyre type checker
149
+ .pyre/
150
+
151
+ # pytype static type analyzer
152
+ .pytype/
153
+
154
+ # Cython debug symbols
155
+ cython_debug/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
@@ -1,7 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: opengradient
3
- Version: 0.3.11
3
+ Version: 0.3.13
4
4
  Summary: Python SDK for OpenGradient decentralized model management & inference services
5
+ Project-URL: Homepage, https://opengradient.ai
5
6
  Author-email: OpenGradient <oliver@opengradient.ai>
6
7
  License: MIT License
7
8
 
@@ -24,8 +25,6 @@ License: MIT License
24
25
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
26
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
27
  SOFTWARE.
27
-
28
- Project-URL: Homepage, https://opengradient.ai
29
28
  Classifier: Development Status :: 3 - Alpha
30
29
  Classifier: Intended Audience :: Developers
31
30
  Classifier: License :: OSI Approved :: MIT License
@@ -33,15 +32,13 @@ Classifier: Programming Language :: Python :: 3.10
33
32
  Classifier: Programming Language :: Python :: 3.11
34
33
  Classifier: Programming Language :: Python :: 3.12
35
34
  Requires-Python: >=3.10
36
- Description-Content-Type: text/markdown
37
- License-File: LICENSE
38
35
  Requires-Dist: aiohappyeyeballs==2.4.3
39
36
  Requires-Dist: aiohttp==3.10.8
40
37
  Requires-Dist: aiosignal==1.3.1
41
38
  Requires-Dist: annotated-types==0.7.0
42
39
  Requires-Dist: attrs==24.2.0
43
40
  Requires-Dist: bitarray==2.9.2
44
- Requires-Dist: CacheControl==0.14.0
41
+ Requires-Dist: cachecontrol==0.14.0
45
42
  Requires-Dist: cachetools==5.5.0
46
43
  Requires-Dist: certifi==2024.8.30
47
44
  Requires-Dist: cffi==1.17.1
@@ -55,6 +52,7 @@ Requires-Dist: cryptography==43.0.1
55
52
  Requires-Dist: cytoolz==0.12.3
56
53
  Requires-Dist: distlib==0.3.8
57
54
  Requires-Dist: dulwich==0.21.7
55
+ Requires-Dist: eth-abi==5.1.0
58
56
  Requires-Dist: eth-account==0.13.4
59
57
  Requires-Dist: eth-hash==0.7.0
60
58
  Requires-Dist: eth-keyfile==0.8.1
@@ -62,7 +60,6 @@ Requires-Dist: eth-keys==0.5.1
62
60
  Requires-Dist: eth-rlp==2.1.0
63
61
  Requires-Dist: eth-typing==5.0.0
64
62
  Requires-Dist: eth-utils==5.0.0
65
- Requires-Dist: eth_abi==5.1.0
66
63
  Requires-Dist: fastjsonschema==2.20.0
67
64
  Requires-Dist: fastparquet==2024.5.0
68
65
  Requires-Dist: filelock==3.16.1
@@ -77,13 +74,14 @@ Requires-Dist: google-cloud-storage==2.18.2
77
74
  Requires-Dist: google-crc32c==1.6.0
78
75
  Requires-Dist: google-resumable-media==2.7.2
79
76
  Requires-Dist: googleapis-common-protos==1.65.0
77
+ Requires-Dist: grpcio-tools==1.66.2
80
78
  Requires-Dist: grpcio==1.66.2
81
- Requires-Dist: grpcio-status==1.66.2
82
79
  Requires-Dist: hexbytes==1.2.1
83
80
  Requires-Dist: idna==3.10
84
- Requires-Dist: jaraco.classes==3.4.0
81
+ Requires-Dist: jaraco-classes==3.4.0
85
82
  Requires-Dist: jwcrypto==1.5.6
86
83
  Requires-Dist: keyring==24.3.1
84
+ Requires-Dist: langchain==0.3.7
87
85
  Requires-Dist: more-itertools==10.5.0
88
86
  Requires-Dist: msgpack==1.1.0
89
87
  Requires-Dist: multidict==6.1.0
@@ -97,23 +95,24 @@ Requires-Dist: pkginfo==1.11.1
97
95
  Requires-Dist: platformdirs==4.3.6
98
96
  Requires-Dist: proto-plus==1.24.0
99
97
  Requires-Dist: protobuf==5.28.2
98
+ Requires-Dist: protobuf>=4.24.0
100
99
  Requires-Dist: ptyprocess==0.7.0
101
100
  Requires-Dist: pyarrow==17.0.0
101
+ Requires-Dist: pyasn1-modules==0.4.1
102
102
  Requires-Dist: pyasn1==0.6.1
103
- Requires-Dist: pyasn1_modules==0.4.1
104
103
  Requires-Dist: pycparser==2.22
105
104
  Requires-Dist: pycryptodome==3.21.0
105
+ Requires-Dist: pydantic-core==2.23.4
106
106
  Requires-Dist: pydantic==2.9.2
107
- Requires-Dist: pydantic_core==2.23.4
108
- Requires-Dist: pyproject_hooks==1.2.0
107
+ Requires-Dist: pyproject-hooks==1.2.0
109
108
  Requires-Dist: python-dateutil==2.9.0.post0
110
109
  Requires-Dist: python-jwt==4.1.0
111
110
  Requires-Dist: pytz==2024.2
112
111
  Requires-Dist: pyunormalize==16.0.0
113
- Requires-Dist: RapidFuzz==3.10.0
112
+ Requires-Dist: rapidfuzz==3.10.0
114
113
  Requires-Dist: regex==2024.9.11
115
- Requires-Dist: requests==2.32.3
116
114
  Requires-Dist: requests-toolbelt==1.0.0
115
+ Requires-Dist: requests==2.32.3
117
116
  Requires-Dist: rlp==4.0.1
118
117
  Requires-Dist: rsa==4.9
119
118
  Requires-Dist: shellingham==1.5.4
@@ -122,13 +121,14 @@ Requires-Dist: tomlkit==0.13.2
122
121
  Requires-Dist: toolz==0.12.1
123
122
  Requires-Dist: trove-classifiers==2024.9.12
124
123
  Requires-Dist: types-requests==2.32.0.20240914
125
- Requires-Dist: typing_extensions==4.12.2
124
+ Requires-Dist: typing-extensions==4.12.2
126
125
  Requires-Dist: tzdata==2024.2
127
126
  Requires-Dist: urllib3==2.2.3
128
127
  Requires-Dist: web3==7.3.0
129
128
  Requires-Dist: websockets==13.1
130
129
  Requires-Dist: xattr==1.1.0
131
130
  Requires-Dist: yarl==1.13.1
131
+ Description-Content-Type: text/markdown
132
132
 
133
133
  # OpenGradient Python SDK
134
134
  Python SDK for the OpenGradient platform provides decentralized model management & inference services. Python SDK allows programmatic access to our model repository and decentralized AI infrastructure.
@@ -227,4 +227,4 @@ opengradient infer QmbUqS93oc4JTLMHwpVxsE39mhNxy6hpf6Py3r9oANr8aZ VANILLA --inpu
227
227
  opengradient llm --model "meta-llama/Meta-Llama-3-8B-Instruct" --prompt "Translate to French: Hello, how are you?" --max-tokens 50 --temperature 0.7
228
228
  ```
229
229
 
230
- For more information read the OpenGradient [documentation](https://docs.opengradient.ai/).
230
+ For more information read the OpenGradient [documentation](https://docs.opengradient.ai/).
@@ -1,10 +1,10 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "opengradient"
7
- version = "0.3.11"
7
+ version = "0.3.13"
8
8
  description = "Python SDK for OpenGradient decentralized model management & inference services"
9
9
  authors = [{name = "OpenGradient", email = "oliver@opengradient.ai"}]
10
10
  license = {file = "LICENSE"}
@@ -62,12 +62,14 @@ dependencies = [
62
62
  "google-resumable-media==2.7.2",
63
63
  "googleapis-common-protos==1.65.0",
64
64
  "grpcio==1.66.2",
65
- "grpcio-status==1.66.2",
65
+ "grpcio-tools==1.66.2",
66
+ "protobuf>=4.24.0",
66
67
  "hexbytes==1.2.1",
67
68
  "idna==3.10",
68
69
  "jaraco.classes==3.4.0",
69
70
  "jwcrypto==1.5.6",
70
71
  "keyring==24.3.1",
72
+ "langchain==0.3.7",
71
73
  "more-itertools==10.5.0",
72
74
  "msgpack==1.1.0",
73
75
  "multidict==6.1.0",
@@ -143,4 +145,14 @@ select = ["E", "F", "I", "N"]
143
145
  ignore = []
144
146
 
145
147
  [tool.ruff.mccabe]
146
- max-complexity = 10
148
+ max-complexity = 10
149
+
150
+ [tool.hatch.build]
151
+ include = [
152
+ "src/opengradient/**/*.py",
153
+ "src/opengradient/proto/*.proto",
154
+ "src/opengradient/abi/*.abi"
155
+ ]
156
+
157
+ [tool.hatch.build.targets.wheel]
158
+ packages = ["src/opengradient"]
@@ -5,7 +5,7 @@ from .defaults import DEFAULT_INFERENCE_CONTRACT_ADDRESS, DEFAULT_RPC_URL
5
5
  from .types import InferenceMode, LLM
6
6
  from . import llm
7
7
 
8
- __version__ = "0.3.11"
8
+ __version__ = "0.3.13"
9
9
 
10
10
  _client = None
11
11
 
@@ -73,4 +73,25 @@ def login(email: str, password: str):
73
73
  def list_files(model_name: str, version: str) -> List[Dict]:
74
74
  if _client is None:
75
75
  raise RuntimeError("OpenGradient client not initialized. Call og.init() first.")
76
- return _client.list_files(model_name, version)
76
+ return _client.list_files(model_name, version)
77
+
78
+ def generate_image(model: str, prompt: str, height: Optional[int] = None, width: Optional[int] = None) -> bytes:
79
+ """
80
+ Generate an image using the specified model and prompt.
81
+
82
+ Args:
83
+ model (str): The model identifier (e.g. "stabilityai/stable-diffusion-xl-base-1.0")
84
+ prompt (str): The text prompt to generate the image from
85
+ height (Optional[int]): Height of the generated image. Default is None.
86
+ width (Optional[int]): Width of the generated image. Default is None.
87
+
88
+ Returns:
89
+ bytes: The raw image data bytes
90
+
91
+ Raises:
92
+ RuntimeError: If the client is not initialized
93
+ OpenGradientError: If the image generation fails
94
+ """
95
+ if _client is None:
96
+ raise RuntimeError("OpenGradient client not initialized. Call og.init() first.")
97
+ return _client.generate_image(model, prompt, height=height, width=width)
@@ -0,0 +1 @@
1
+ [{"anonymous":false,"inputs":[{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"components":[{"internalType":"int128","name":"value","type":"int128"},{"internalType":"int128","name":"decimals","type":"int128"}],"internalType":"struct TensorLib.Number[]","name":"values","type":"tuple[]"},{"internalType":"uint32[]","name":"shape","type":"uint32[]"}],"internalType":"struct TensorLib.MultiDimensionalNumberTensor[]","name":"numbers","type":"tuple[]"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string[]","name":"values","type":"string[]"}],"internalType":"struct TensorLib.StringTensor[]","name":"strings","type":"tuple[]"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct TensorLib.JsonScalar[]","name":"jsons","type":"tuple[]"},{"internalType":"bool","name":"is_simulation_result","type":"bool"}],"indexed":false,"internalType":"struct ModelOutput","name":"output","type":"tuple"}],"name":"InferenceResult","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"string","name":"finish_reason","type":"string"},{"components":[{"internalType":"string","name":"role","type":"string"},{"internalType":"string","name":"content","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"tool_call_id","type":"string"},{"components":[{"internalType":"string","name":"id","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"arguments","type":"string"}],"internalType":"struct ToolCall[]","name":"tool_calls","type":"tuple[]"}],"internalType":"struct ChatMessage","name":"message","type":"tuple"}],"indexed":false,"internalType":"struct LLMChatResponse","name":"response","type":"tuple"}],"name":"LLMChatResult","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"string","name":"answer","type":"string"}],"indexed":false,"internalType":"struct LLMCompletionResponse","name":"response","type":"tuple"}],"name":"LLMCompletionResult","type":"event"},{"inputs":[{"internalType":"string","name":"modelId","type":"string"},{"internalType":"enum ModelInferenceMode","name":"inferenceMode","type":"uint8"},{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"components":[{"internalType":"int128","name":"value","type":"int128"},{"internalType":"int128","name":"decimals","type":"int128"}],"internalType":"struct TensorLib.Number[]","name":"values","type":"tuple[]"},{"internalType":"uint32[]","name":"shape","type":"uint32[]"}],"internalType":"struct TensorLib.MultiDimensionalNumberTensor[]","name":"numbers","type":"tuple[]"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string[]","name":"values","type":"string[]"}],"internalType":"struct TensorLib.StringTensor[]","name":"strings","type":"tuple[]"}],"internalType":"struct ModelInput","name":"modelInput","type":"tuple"}],"name":"run","outputs":[{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"components":[{"internalType":"int128","name":"value","type":"int128"},{"internalType":"int128","name":"decimals","type":"int128"}],"internalType":"struct TensorLib.Number[]","name":"values","type":"tuple[]"},{"internalType":"uint32[]","name":"shape","type":"uint32[]"}],"internalType":"struct TensorLib.MultiDimensionalNumberTensor[]","name":"numbers","type":"tuple[]"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string[]","name":"values","type":"string[]"}],"internalType":"struct TensorLib.StringTensor[]","name":"strings","type":"tuple[]"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"value","type":"string"}],"internalType":"struct TensorLib.JsonScalar[]","name":"jsons","type":"tuple[]"},{"internalType":"bool","name":"is_simulation_result","type":"bool"}],"internalType":"struct ModelOutput","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum LLMInferenceMode","name":"mode","type":"uint8"},{"internalType":"string","name":"modelCID","type":"string"},{"components":[{"internalType":"string","name":"role","type":"string"},{"internalType":"string","name":"content","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"tool_call_id","type":"string"},{"components":[{"internalType":"string","name":"id","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"arguments","type":"string"}],"internalType":"struct ToolCall[]","name":"tool_calls","type":"tuple[]"}],"internalType":"struct ChatMessage[]","name":"messages","type":"tuple[]"},{"components":[{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"parameters","type":"string"}],"internalType":"struct ToolDefinition[]","name":"tools","type":"tuple[]"},{"internalType":"string","name":"tool_choice","type":"string"},{"internalType":"uint32","name":"max_tokens","type":"uint32"},{"internalType":"string[]","name":"stop_sequence","type":"string[]"},{"internalType":"uint32","name":"temperature","type":"uint32"}],"internalType":"struct LLMChatRequest","name":"request","type":"tuple"}],"name":"runLLMChat","outputs":[{"components":[{"internalType":"string","name":"finish_reason","type":"string"},{"components":[{"internalType":"string","name":"role","type":"string"},{"internalType":"string","name":"content","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"tool_call_id","type":"string"},{"components":[{"internalType":"string","name":"id","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"arguments","type":"string"}],"internalType":"struct ToolCall[]","name":"tool_calls","type":"tuple[]"}],"internalType":"struct ChatMessage","name":"message","type":"tuple"}],"internalType":"struct LLMChatResponse","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum LLMInferenceMode","name":"mode","type":"uint8"},{"internalType":"string","name":"modelCID","type":"string"},{"internalType":"string","name":"prompt","type":"string"},{"internalType":"uint32","name":"max_tokens","type":"uint32"},{"internalType":"string[]","name":"stop_sequence","type":"string[]"},{"internalType":"uint32","name":"temperature","type":"uint32"}],"internalType":"struct LLMCompletionRequest","name":"request","type":"tuple"}],"name":"runLLMCompletion","outputs":[{"components":[{"internalType":"string","name":"answer","type":"string"}],"internalType":"struct LLMCompletionResponse","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"}]
@@ -604,6 +604,44 @@ def list_files(client: Client, repo_name: str, version: str):
604
604
  click.echo(f"Error listing files: {str(e)}")
605
605
 
606
606
 
607
+ @cli.command()
608
+ @click.option('--model', '-m', required=True, help='Model identifier for image generation')
609
+ @click.option('--prompt', '-p', required=True, help='Text prompt for generating the image')
610
+ @click.option('--output-path', '-o', required=True, type=click.Path(path_type=Path),
611
+ help='Output file path for the generated image')
612
+ @click.option('--width', type=int, default=1024, help='Output image width')
613
+ @click.option('--height', type=int, default=1024, help='Output image height')
614
+ @click.pass_context
615
+ def generate_image(ctx, model: str, prompt: str, output_path: Path, width: int, height: int):
616
+ """
617
+ Generate an image using a diffusion model.
618
+
619
+ Example usage:
620
+ opengradient generate-image --model stabilityai/stable-diffusion-xl-base-1.0
621
+ --prompt "A beautiful sunset over mountains" --output-path sunset.png
622
+ """
623
+ client: Client = ctx.obj['client']
624
+ try:
625
+ click.echo(f"Generating image with model \"{model}\"")
626
+ image_data = client.generate_image(
627
+ model_cid=model,
628
+ prompt=prompt,
629
+ width=width,
630
+ height=height
631
+ )
632
+
633
+ # Save the image
634
+ with open(output_path, 'wb') as f:
635
+ f.write(image_data)
636
+
637
+ click.echo() # Add a newline for better spacing
638
+ click.secho("✅ Image generation successful", fg="green", bold=True)
639
+ click.echo(f"Image saved to: {output_path}")
640
+
641
+ except Exception as e:
642
+ click.echo(f"Error generating image: {str(e)}")
643
+
644
+
607
645
  if __name__ == '__main__':
608
646
  logging.getLogger().setLevel(logging.WARN)
609
647
  cli()
@@ -14,6 +14,14 @@ from opengradient import utils
14
14
  from opengradient.exceptions import OpenGradientError
15
15
  from opengradient.types import InferenceMode, LLM
16
16
 
17
+ import grpc
18
+ import time
19
+ import uuid
20
+ from google.protobuf import timestamp_pb2
21
+
22
+ from opengradient.proto import infer_pb2
23
+ from opengradient.proto import infer_pb2_grpc
24
+ from .defaults import DEFAULT_IMAGE_GEN_HOST, DEFAULT_IMAGE_GEN_PORT
17
25
 
18
26
  class Client:
19
27
  FIREBASE_CONFIG = {
@@ -319,6 +327,7 @@ class Client:
319
327
  Raises:
320
328
  OpenGradientError: If the inference fails.
321
329
  """
330
+ # TODO (Kyle): Add input support for JSON tensors
322
331
  try:
323
332
  logging.debug("Entering infer method")
324
333
  self._initialize_web3()
@@ -705,4 +714,129 @@ class Client:
705
714
  status_code=e.response.status_code if hasattr(e, 'response') else None)
706
715
  except Exception as e:
707
716
  logging.error(f"Unexpected error during file listing: {str(e)}", exc_info=True)
708
- raise OpenGradientError(f"Unexpected error during file listing: {str(e)}")
717
+ raise OpenGradientError(f"Unexpected error during file listing: {str(e)}")
718
+
719
+ def generate_image(
720
+ self,
721
+ model_cid: str,
722
+ prompt: str,
723
+ host: str = DEFAULT_IMAGE_GEN_HOST,
724
+ port: int = DEFAULT_IMAGE_GEN_PORT,
725
+ width: int = 1024,
726
+ height: int = 1024,
727
+ timeout: int = 300, # 5 minute timeout
728
+ max_retries: int = 3
729
+ ) -> bytes:
730
+ """
731
+ Generate an image using a diffusion model through gRPC.
732
+
733
+ Args:
734
+ model_cid (str): The model identifier (e.g. "stabilityai/stable-diffusion-xl-base-1.0")
735
+ prompt (str): The text prompt to generate the image from
736
+ host (str, optional): gRPC host address. Defaults to DEFAULT_IMAGE_GEN_HOST.
737
+ port (int, optional): gRPC port number. Defaults to DEFAULT_IMAGE_GEN_PORT.
738
+ width (int, optional): Output image width. Defaults to 1024.
739
+ height (int, optional): Output image height. Defaults to 1024.
740
+ timeout (int, optional): Maximum time to wait for generation in seconds. Defaults to 300.
741
+ max_retries (int, optional): Maximum number of retry attempts. Defaults to 3.
742
+
743
+ Returns:
744
+ bytes: The raw image data bytes
745
+
746
+ Raises:
747
+ OpenGradientError: If the image generation fails
748
+ TimeoutError: If the generation exceeds the timeout period
749
+ """
750
+ def exponential_backoff(attempt: int, max_delay: float = 30.0) -> None:
751
+ """Calculate and sleep for exponential backoff duration"""
752
+ delay = min(0.1 * (2 ** attempt), max_delay)
753
+ time.sleep(delay)
754
+
755
+ channel = None
756
+ start_time = time.time()
757
+ retry_count = 0
758
+
759
+ try:
760
+ while retry_count < max_retries:
761
+ try:
762
+ # Initialize gRPC channel and stub
763
+ channel = grpc.insecure_channel(f'{host}:{port}')
764
+ stub = infer_pb2_grpc.InferenceServiceStub(channel)
765
+
766
+ # Create image generation request
767
+ image_request = infer_pb2.ImageGenerationRequest(
768
+ model=model_cid,
769
+ prompt=prompt,
770
+ height=height,
771
+ width=width
772
+ )
773
+
774
+ # Create inference request with random transaction ID
775
+ tx_id = str(uuid.uuid4())
776
+ request = infer_pb2.InferenceRequest(
777
+ tx=tx_id,
778
+ image_generation=image_request
779
+ )
780
+
781
+ # Send request with timeout
782
+ response_id = stub.RunInferenceAsync(
783
+ request,
784
+ timeout=min(30, timeout) # Initial request timeout
785
+ )
786
+
787
+ # Poll for completion
788
+ attempt = 0
789
+ while True:
790
+ # Check timeout
791
+ if time.time() - start_time > timeout:
792
+ raise TimeoutError(f"Image generation timed out after {timeout} seconds")
793
+
794
+ status_request = infer_pb2.InferenceTxId(id=response_id.id)
795
+ try:
796
+ status = stub.GetInferenceStatus(
797
+ status_request,
798
+ timeout=min(5, timeout) # Status check timeout
799
+ ).status
800
+ except grpc.RpcError as e:
801
+ logging.warning(f"Status check failed (attempt {attempt}): {str(e)}")
802
+ exponential_backoff(attempt)
803
+ attempt += 1
804
+ continue
805
+
806
+ if status == infer_pb2.InferenceStatus.STATUS_COMPLETED:
807
+ break
808
+ elif status == infer_pb2.InferenceStatus.STATUS_ERROR:
809
+ raise OpenGradientError("Image generation failed on server")
810
+ elif status != infer_pb2.InferenceStatus.STATUS_IN_PROGRESS:
811
+ raise OpenGradientError(f"Unexpected status: {status}")
812
+
813
+ exponential_backoff(attempt)
814
+ attempt += 1
815
+
816
+ # Get result
817
+ result = stub.GetInferenceResult(
818
+ response_id,
819
+ timeout=min(30, timeout) # Result fetch timeout
820
+ )
821
+ return result.image_generation_result.image_data
822
+
823
+ except (grpc.RpcError, TimeoutError) as e:
824
+ retry_count += 1
825
+ if retry_count >= max_retries:
826
+ raise OpenGradientError(f"Image generation failed after {max_retries} retries: {str(e)}")
827
+
828
+ logging.warning(f"Attempt {retry_count} failed: {str(e)}. Retrying...")
829
+ exponential_backoff(retry_count)
830
+
831
+ except grpc.RpcError as e:
832
+ logging.error(f"gRPC error: {str(e)}")
833
+ raise OpenGradientError(f"Image generation failed: {str(e)}")
834
+ except TimeoutError as e:
835
+ logging.error(f"Timeout error: {str(e)}")
836
+ raise
837
+ except Exception as e:
838
+ logging.error(f"Error in generate image method: {str(e)}", exc_info=True)
839
+ raise OpenGradientError(f"Image generation failed: {str(e)}")
840
+ finally:
841
+ if channel:
842
+ channel.close()
@@ -1,7 +1,8 @@
1
-
2
1
  # Default variables
3
2
  DEFAULT_RPC_URL="http://18.218.115.248:8545"
4
3
  DEFAULT_OG_FAUCET_URL="http://18.218.115.248:8080/?address="
5
4
  DEFAULT_HUB_SIGNUP_URL="https://hub.opengradient.ai/signup"
6
- DEFAULT_INFERENCE_CONTRACT_ADDRESS="0xF78F7d5a7e9f484f0924Cc21347029715bD3B8f4"
5
+ DEFAULT_INFERENCE_CONTRACT_ADDRESS="0x3fDCb0394CF4919ff4361f4EbA0750cEc2e3bBc7"
7
6
  DEFAULT_BLOCKCHAIN_EXPLORER="http://3.145.62.2/tx/"
7
+ DEFAULT_IMAGE_GEN_HOST="18.217.25.69"
8
+ DEFAULT_IMAGE_GEN_PORT=5125
@@ -0,0 +1,2 @@
1
+ from .infer_pb2 import *
2
+ from .infer_pb2_grpc import *
@@ -0,0 +1,50 @@
1
+ syntax = "proto3";
2
+
3
+ package inference;
4
+
5
+ // The inference service definition
6
+ service InferenceService {
7
+ rpc RunInferenceAsync(InferenceRequest) returns (InferenceTxId);
8
+ rpc GetInferenceStatus(InferenceTxId) returns (InferenceStatus);
9
+ rpc GetInferenceResult(InferenceTxId) returns (InferenceResult);
10
+ }
11
+
12
+ // Request messages
13
+ message InferenceRequest {
14
+ string tx = 1;
15
+ ImageGenerationRequest image_generation = 6; // Field number 6 matches original proto
16
+ }
17
+
18
+ message ImageGenerationRequest {
19
+ string model = 1;
20
+ string prompt = 2;
21
+ optional int32 height = 3;
22
+ optional int32 width = 4;
23
+ }
24
+
25
+ message InferenceTxId {
26
+ string id = 1;
27
+ }
28
+
29
+ // Status messages
30
+ message InferenceStatus {
31
+ enum Status {
32
+ STATUS_UNSPECIFIED = 0;
33
+ STATUS_IN_PROGRESS = 1;
34
+ STATUS_COMPLETED = 2;
35
+ STATUS_ERROR = 3;
36
+ }
37
+ Status status = 1;
38
+ optional string error_message = 2; // For error handling
39
+ }
40
+
41
+ // Result messages
42
+ message InferenceResult {
43
+ ImageGenerationResponse image_generation_result = 5; // Field number 5 matches original proto
44
+ optional string public_key = 7;
45
+ optional string signature = 8;
46
+ }
47
+
48
+ message ImageGenerationResponse {
49
+ bytes image_data = 1;
50
+ }