opengradient 0.5.7__py3-none-any.whl → 0.5.9__py3-none-any.whl

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.
opengradient/defaults.py CHANGED
@@ -9,4 +9,6 @@ DEFAULT_BLOCKCHAIN_EXPLORER = "https://explorer.opengradient.ai/tx/"
9
9
  DEFAULT_IMAGE_GEN_HOST = "18.217.25.69"
10
10
  DEFAULT_IMAGE_GEN_PORT = 5125
11
11
  DEFAULT_LLM_SERVER_URL = "http://35.225.197.84:8000"
12
- DEFAULT_OPENGRADIENT_LLM_SERVER_URL = "https://llm.opengradient.ai"
12
+ DEFAULT_OPENGRADIENT_LLM_SERVER_URL = "https://llmogevm.opengradient.ai"
13
+ DEFAULT_OPENGRADIENT_LLM_STREAMING_SERVER_URL = "https://llmogevm.opengradient.ai"
14
+ DEFAULT_NETWORK_FILTER = "og-evm"
@@ -36,7 +36,12 @@ class OpenGradientChatModel(BaseChatModel):
36
36
  super().__init__()
37
37
 
38
38
  self._client = Client(
39
- private_key=private_key, rpc_url=DEFAULT_RPC_URL, api_url=DEFAULT_API_URL, contract_address=DEFAULT_INFERENCE_CONTRACT_ADDRESS, email=None, password=None
39
+ private_key=private_key,
40
+ rpc_url=DEFAULT_RPC_URL,
41
+ api_url=DEFAULT_API_URL,
42
+ contract_address=DEFAULT_INFERENCE_CONTRACT_ADDRESS,
43
+ email=None,
44
+ password=None,
40
45
  )
41
46
  self._model_cid = model_cid
42
47
  self._max_tokens = max_tokens
opengradient/types.py CHANGED
@@ -1,15 +1,42 @@
1
1
  import time
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum, IntEnum, StrEnum
4
- from typing import Dict, List, Optional, Tuple, Union, DefaultDict
4
+ from typing import Dict, List, Optional, Tuple, Union, DefaultDict, Iterator, AsyncIterator
5
5
  import numpy as np
6
6
 
7
7
 
8
8
  class x402SettlementMode(StrEnum):
9
+ """
10
+ Settlement modes for x402 payment protocol transactions.
11
+
12
+ These modes control how inference data is recorded on-chain for payment settlement
13
+ and auditability. Each mode offers different trade-offs between data completeness,
14
+ privacy, and transaction costs.
15
+
16
+ Attributes:
17
+ SETTLE: Individual settlement with input/output hashes only.
18
+ Records cryptographic hashes of the inference input and output.
19
+ Most privacy-preserving option - actual data is not stored on-chain.
20
+ Suitable for applications where only proof of execution is needed.
21
+
22
+ SETTLE_METADATA: Individual settlement with full metadata.
23
+ Records complete model information, full input and output data,
24
+ and all inference metadata on-chain.
25
+ Provides maximum transparency and auditability.
26
+ Higher gas costs due to larger data storage.
27
+
28
+ SETTLE_BATCH: Batch settlement for multiple inferences.
29
+ Aggregates multiple inference requests into a single settlement transaction
30
+ using batch hashes.
31
+ Most cost-efficient for high-volume applications.
32
+ Reduced per-inference transaction overhead.
33
+ """
34
+
9
35
  SETTLE = "settle"
10
36
  SETTLE_METADATA = "settle-metadata"
11
37
  SETTLE_BATCH = "settle-batch"
12
38
 
39
+
13
40
  class CandleOrder(IntEnum):
14
41
  ASCENDING = 0
15
42
  DESCENDING = 1
@@ -107,7 +134,7 @@ class InferenceMode(Enum):
107
134
 
108
135
 
109
136
  class LlmInferenceMode(Enum):
110
- """Enum for differetn inference modes available for LLM inferences (VANILLA, TEE)"""
137
+ """Enum for different inference modes available for LLM inference (VANILLA, TEE)"""
111
138
 
112
139
  VANILLA = 0
113
140
  TEE = 1
@@ -138,6 +165,196 @@ class InferenceResult:
138
165
  model_output: Dict[str, np.ndarray]
139
166
 
140
167
 
168
+ @dataclass
169
+ class StreamDelta:
170
+ """
171
+ Represents a delta (incremental change) in a streaming response.
172
+
173
+ Attributes:
174
+ content: Incremental text content (if any)
175
+ role: Message role (appears in first chunk)
176
+ tool_calls: Tool call information (if function calling is used)
177
+ """
178
+ content: Optional[str] = None
179
+ role: Optional[str] = None
180
+ tool_calls: Optional[List[Dict]] = None
181
+
182
+
183
+ @dataclass
184
+ class StreamChoice:
185
+ """
186
+ Represents a choice in a streaming response.
187
+
188
+ Attributes:
189
+ delta: The incremental changes in this chunk
190
+ index: Choice index (usually 0)
191
+ finish_reason: Reason for completion (appears in final chunk)
192
+ """
193
+ delta: StreamDelta
194
+ index: int = 0
195
+ finish_reason: Optional[str] = None
196
+
197
+
198
+ @dataclass
199
+ class StreamUsage:
200
+ """
201
+ Token usage information for a streaming response.
202
+
203
+ Attributes:
204
+ prompt_tokens: Number of tokens in the prompt
205
+ completion_tokens: Number of tokens in the completion
206
+ total_tokens: Total tokens used
207
+ """
208
+ prompt_tokens: int
209
+ completion_tokens: int
210
+ total_tokens: int
211
+
212
+
213
+ @dataclass
214
+ class StreamChunk:
215
+ """
216
+ Represents a single chunk in a streaming LLM response.
217
+
218
+ This follows the OpenAI streaming format but is provider-agnostic.
219
+ Each chunk contains incremental data, with the final chunk including
220
+ usage information.
221
+
222
+ Attributes:
223
+ choices: List of streaming choices (usually contains one choice)
224
+ model: Model identifier
225
+ usage: Token usage information (only in final chunk)
226
+ is_final: Whether this is the final chunk (before [DONE])
227
+ """
228
+ choices: List[StreamChoice]
229
+ model: str
230
+ usage: Optional[StreamUsage] = None
231
+ is_final: bool = False
232
+
233
+ @classmethod
234
+ def from_sse_data(cls, data: Dict) -> "StreamChunk":
235
+ """
236
+ Parse a StreamChunk from SSE data dictionary.
237
+
238
+ Args:
239
+ data: Dictionary parsed from SSE data line
240
+
241
+ Returns:
242
+ StreamChunk instance
243
+ """
244
+ choices = []
245
+ for choice_data in data.get("choices", []):
246
+ delta_data = choice_data.get("delta", {})
247
+ delta = StreamDelta(
248
+ content=delta_data.get("content"),
249
+ role=delta_data.get("role"),
250
+ tool_calls=delta_data.get("tool_calls")
251
+ )
252
+ choice = StreamChoice(
253
+ delta=delta,
254
+ index=choice_data.get("index", 0),
255
+ finish_reason=choice_data.get("finish_reason")
256
+ )
257
+ choices.append(choice)
258
+
259
+ usage = None
260
+ if "usage" in data:
261
+ usage_data = data["usage"]
262
+ usage = StreamUsage(
263
+ prompt_tokens=usage_data.get("prompt_tokens", 0),
264
+ completion_tokens=usage_data.get("completion_tokens", 0),
265
+ total_tokens=usage_data.get("total_tokens", 0)
266
+ )
267
+
268
+ is_final = any(c.finish_reason is not None for c in choices) or usage is not None
269
+
270
+ return cls(
271
+ choices=choices,
272
+ model=data.get("model", "unknown"),
273
+ usage=usage,
274
+ is_final=is_final
275
+ )
276
+
277
+
278
+ @dataclass
279
+ class TextGenerationStream:
280
+ """
281
+ Iterator wrapper for streaming text generation responses.
282
+
283
+ Provides a clean interface for iterating over stream chunks with
284
+ automatic parsing of SSE format.
285
+
286
+ Usage:
287
+ stream = client.llm_chat(..., stream=True)
288
+ for chunk in stream:
289
+ if chunk.choices[0].delta.content:
290
+ print(chunk.choices[0].delta.content, end="")
291
+ """
292
+ _iterator: Union[Iterator[str], AsyncIterator[str]]
293
+ _is_async: bool = False
294
+
295
+ def __iter__(self):
296
+ """Iterate over stream chunks."""
297
+ return self
298
+
299
+ def __next__(self) -> StreamChunk:
300
+ """Get next stream chunk."""
301
+ import json
302
+
303
+ while True:
304
+ try:
305
+ line = next(self._iterator)
306
+ except StopIteration:
307
+ raise
308
+
309
+ if not line or not line.strip():
310
+ continue
311
+
312
+ if not line.startswith("data: "):
313
+ continue
314
+
315
+ data_str = line[6:] # Remove "data: " prefix
316
+
317
+ if data_str.strip() == "[DONE]":
318
+ raise StopIteration
319
+
320
+ try:
321
+ data = json.loads(data_str)
322
+ return StreamChunk.from_sse_data(data)
323
+ except json.JSONDecodeError:
324
+ # Skip malformed chunks
325
+ continue
326
+
327
+ async def __anext__(self) -> StreamChunk:
328
+ """Get next stream chunk (async version)."""
329
+ import json
330
+
331
+ if not self._is_async:
332
+ raise TypeError("Use __next__ for sync iterators")
333
+
334
+ while True:
335
+ try:
336
+ line = await self._iterator.__anext__()
337
+ except StopAsyncIteration:
338
+ raise
339
+
340
+ if not line or not line.strip():
341
+ continue
342
+
343
+ if not line.startswith("data: "):
344
+ continue
345
+
346
+ data_str = line[6:]
347
+
348
+ if data_str.strip() == "[DONE]":
349
+ raise StopAsyncIteration
350
+
351
+ try:
352
+ data = json.loads(data_str)
353
+ return StreamChunk.from_sse_data(data)
354
+ except json.JSONDecodeError:
355
+ continue
356
+
357
+
141
358
  @dataclass
142
359
  class TextGenerationOutput:
143
360
  """
@@ -205,23 +422,23 @@ class LLM(str, Enum):
205
422
  # META_LLAMA_3_1_70B_INSTRUCT = "meta-llama/Llama-3.1-70B-Instruct"
206
423
  # DOBBY_UNHINGED_3_1_8B = "SentientAGI/Dobby-Mini-Unhinged-Llama-3.1-8B"
207
424
  # DOBBY_LEASHED_3_1_8B = "SentientAGI/Dobby-Mini-Leashed-Llama-3.1-8B"
208
-
425
+
209
426
  # OpenAI models via TEE
210
427
  GPT_4_1_2025_04_14 = "openai/gpt-4.1-2025-04-14"
211
428
  GPT_4O = "openai/gpt-4o"
212
429
  O4_MINI = "openai/o4-mini"
213
-
430
+
214
431
  # Anthropic models via TEE
215
432
  CLAUDE_3_7_SONNET = "anthropic/claude-3.7-sonnet"
216
433
  CLAUDE_3_5_HAIKU = "anthropic/claude-3.5-haiku"
217
434
  CLAUDE_4_0_SONNET = "anthropic/claude-4.0-sonnet"
218
-
435
+
219
436
  # Google models via TEE
220
437
  GEMINI_2_5_FLASH = "google/gemini-2.5-flash"
221
438
  GEMINI_2_5_PRO = "google/gemini-2.5-pro"
222
439
  GEMINI_2_0_FLASH = "google/gemini-2.0-flash"
223
440
  GEMINI_2_5_FLASH_LITE = "google/gemini-2.5-flash-lite"
224
-
441
+
225
442
  # xAI Grok models via TEE
226
443
  GROK_3_MINI_BETA = "x-ai/grok-3-mini-beta"
227
444
  GROK_3_BETA = "x-ai/grok-3-beta"
@@ -230,28 +447,29 @@ class LLM(str, Enum):
230
447
  GROK_4_1_FAST = "x-ai/grok-4.1-fast"
231
448
  GROK_4_1_FAST_NON_REASONING = "x-ai/grok-4-1-fast-non-reasoning"
232
449
 
450
+
233
451
  class TEE_LLM(str, Enum):
234
452
  """Enum for LLM models available for TEE execution"""
235
-
453
+
236
454
  # Existing (Currently turned off)
237
455
  # META_LLAMA_3_1_70B_INSTRUCT = "meta-llama/Llama-3.1-70B-Instruct"
238
-
456
+
239
457
  # OpenAI models via TEE
240
458
  GPT_4_1_2025_04_14 = "openai/gpt-4.1-2025-04-14"
241
459
  GPT_4O = "openai/gpt-4o"
242
460
  O4_MINI = "openai/o4-mini"
243
-
461
+
244
462
  # Anthropic models via TEE
245
463
  CLAUDE_3_7_SONNET = "anthropic/claude-3.7-sonnet"
246
464
  CLAUDE_3_5_HAIKU = "anthropic/claude-3.5-haiku"
247
465
  CLAUDE_4_0_SONNET = "anthropic/claude-4.0-sonnet"
248
-
466
+
249
467
  # Google models via TEE
250
468
  GEMINI_2_5_FLASH = "google/gemini-2.5-flash"
251
469
  GEMINI_2_5_PRO = "google/gemini-2.5-pro"
252
470
  GEMINI_2_0_FLASH = "google/gemini-2.0-flash"
253
471
  GEMINI_2_5_FLASH_LITE = "google/gemini-2.5-flash-lite"
254
-
472
+
255
473
  # xAI Grok models via TEE
256
474
  GROK_3_MINI_BETA = "x-ai/grok-3-mini-beta"
257
475
  GROK_3_BETA = "x-ai/grok-3-beta"
@@ -0,0 +1,60 @@
1
+ import httpx
2
+ import typing
3
+ import logging
4
+
5
+ from x402.clients.base import x402Client
6
+ from x402.types import x402PaymentRequiredResponse, PaymentRequirements
7
+
8
+
9
+ class X402Auth(httpx.Auth):
10
+ """Auth class for handling x402 payment requirements."""
11
+
12
+ def __init__(
13
+ self,
14
+ account: typing.Any,
15
+ max_value: typing.Optional[int] = None,
16
+ payment_requirements_selector: typing.Optional[
17
+ typing.Callable[
18
+ [
19
+ list[PaymentRequirements],
20
+ typing.Optional[str],
21
+ typing.Optional[str],
22
+ typing.Optional[int],
23
+ ],
24
+ PaymentRequirements,
25
+ ]
26
+ ] = None,
27
+ ):
28
+ self.x402_client = x402Client(
29
+ account,
30
+ max_value=max_value,
31
+ payment_requirements_selector=payment_requirements_selector, # type: ignore
32
+ )
33
+
34
+ async def async_auth_flow(
35
+ self, request: httpx.Request
36
+ ) -> typing.AsyncGenerator[httpx.Request, httpx.Response]:
37
+ response = yield request
38
+
39
+ if response.status_code == 402:
40
+ try:
41
+ await response.aread()
42
+ data = response.json()
43
+
44
+ payment_response = x402PaymentRequiredResponse(**data)
45
+
46
+ selected_requirements = self.x402_client.select_payment_requirements(
47
+ payment_response.accepts
48
+ )
49
+
50
+ payment_header = self.x402_client.create_payment_header(
51
+ selected_requirements, payment_response.x402_version
52
+ )
53
+
54
+ request.headers["X-Payment"] = payment_header
55
+ request.headers["Access-Control-Expose-Headers"] = "X-Payment-Response"
56
+ yield request
57
+
58
+ except Exception as e:
59
+ logging.error(f"X402Auth: Error handling payment: {e}")
60
+ return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opengradient
3
- Version: 0.5.7
3
+ Version: 0.5.9
4
4
  Summary: Python SDK for OpenGradient decentralized model management & inference services
5
5
  Author-email: OpenGradient <kyle@vannalabs.ai>
6
6
  License-Expression: MIT
@@ -23,8 +23,7 @@ Requires-Dist: requests>=2.32.3
23
23
  Requires-Dist: langchain>=0.3.7
24
24
  Requires-Dist: openai>=1.58.1
25
25
  Requires-Dist: pydantic>=2.9.2
26
- Requires-Dist: og-test-x402==0.0.1
27
- Requires-Dist: x402==0.2.1
26
+ Requires-Dist: og-test-x402==0.0.9
28
27
  Dynamic: license-file
29
28
 
30
29
  # OpenGradient Python SDK
@@ -133,6 +132,10 @@ For comprehensive documentation, API reference, and examples, visit:
133
132
  - [OpenGradient Documentation](https://docs.opengradient.ai/)
134
133
  - [API Reference](https://docs.opengradient.ai/api_reference/python_sdk/)
135
134
 
135
+ ### Claude Code Users
136
+
137
+ If you use [Claude Code](https://claude.ai/code), copy [docs/CLAUDE_SDK_USERS.md](docs/CLAUDE_SDK_USERS.md) to your project's `CLAUDE.md` to help Claude assist you with OpenGradient SDK development.
138
+
136
139
  ## Support
137
140
 
138
141
  - Run `opengradient --help` for CLI command reference
@@ -1,11 +1,12 @@
1
- opengradient/__init__.py,sha256=wVg0KTFNBl7RnZF9huR5-m_q1E7tO-YyQwY7AD9JFoc,12635
1
+ opengradient/__init__.py,sha256=1PSbDRGe4ft_0FYoPS3XpeajnRPOTkmx8aZZxcOeztQ,13455
2
2
  opengradient/account.py,sha256=5wrYpws_1lozjOFjLCTHtxgoxK-LmObDAaVy9eDcJY4,1145
3
- opengradient/cli.py,sha256=RksBEGVcZgUg6ng53Fgz-Ncv1erBwdADgblB2HmKkwk,29868
4
- opengradient/client.py,sha256=lzuC4rEwyf1EgHw49PMXjIyjyTsBIAP2XmCNp0cCNzg,63113
5
- opengradient/defaults.py,sha256=w8-dr5ciF2TGnqbm_ib0Yz4U0YL5ikpNqkcPVpmXzP8,673
3
+ opengradient/cli.py,sha256=pfgyLfD1MIDifKmGLFsJqBlgvqIcnsIh3zzg7PaIeH4,33670
4
+ opengradient/client.py,sha256=KDkFxcZ-vGyriFW-ydWTnitgV6rYfxtnNzchWca-8u8,74009
5
+ opengradient/defaults.py,sha256=YOtFDq8HiwEkgMXlV4Zf3YgkopfKUkkx0CpgNuY_Mxk,796
6
6
  opengradient/exceptions.py,sha256=88tfegboGtlehQcwhxsl6ZzhLJWZWlkf_bkHTiCtXpo,3391
7
- opengradient/types.py,sha256=L60W1O3xmlYxbJ9ouN6nKNKJSdTW3yzCDsU0lozWF8U,7837
7
+ opengradient/types.py,sha256=bADakUM6WwdMORGC5HvQvWCezNwIlVc7l0zodPapbhQ,14622
8
8
  opengradient/utils.py,sha256=ZUq4OBIml2vsC0tRqus4Zwb_e3g4woo00apByrafuVw,8058
9
+ opengradient/x402_auth.py,sha256=Jmj-40OybugOXIt_qHzN1qy4x7U3QuM1MKNmPzoEKwc,1920
9
10
  opengradient/abi/InferencePrecompile.abi,sha256=reepTHg6Q01UrFP0Gexc-JayplsvOLPfG7jrEZ-cV28,10197
10
11
  opengradient/abi/PriceHistoryInference.abi,sha256=ZB3fZdx1kaFlp2wt1vTbTZZG1k8HPvmNtkG5Q8Bnajw,5098
11
12
  opengradient/abi/WorkflowScheduler.abi,sha256=yEGs76qO4S1z980KL5hBdfyXiJ6k-kERcB1O_o73AEU,416
@@ -16,7 +17,7 @@ opengradient/alphasense/run_model_tool.py,sha256=wlDqXVHa1xpqQy_hmht_wWegxtqdYgY
16
17
  opengradient/alphasense/types.py,sha256=uxk4JQKbaS2cM3ZiKpdHQb234OJ5ylprNR5vi01QFzA,220
17
18
  opengradient/bin/PriceHistoryInference.bin,sha256=nU2FZpGHIKBZ7NSK9Sr-p9lr-nXja_40ISPN9yckDq8,41276
18
19
  opengradient/llm/__init__.py,sha256=eYFBrOf1GZr0VGbIw-gSFr8hM3Rbw74ye8l-pnBPNuA,1104
19
- opengradient/llm/og_langchain.py,sha256=inJmu28aWA1RAWJx8G0h8w4FGXSKMCzJVOBGUwiKqnI,4904
20
+ opengradient/llm/og_langchain.py,sha256=fVHEq_hJbWrLLVZXKSH5wwSG5kQEt_PGnmAOLUnEgmw,4965
20
21
  opengradient/llm/og_openai.py,sha256=26W_NDnLaICIaWbi9aou40v5ZJXLlmLdztDrdFoDGAU,3789
21
22
  opengradient/proto/__init__.py,sha256=AhaSmrqV0TXGzCKaoPV8-XUvqs2fGAJBM2aOmDpkNbE,55
22
23
  opengradient/proto/infer.proto,sha256=13eaEMcppxkBF8yChptsX9HooWFwJKze7oLZNl-LEb8,1217
@@ -27,9 +28,9 @@ opengradient/workflow_models/constants.py,sha256=viIkb_LGcfVprqQNaA80gBTj6cfYam0
27
28
  opengradient/workflow_models/types.py,sha256=Z22hF6c8Y4D2GlzVEIBODGwsqSjSrQvUcpZ7R-mIJdI,409
28
29
  opengradient/workflow_models/utils.py,sha256=ySfpuiOBqLTlfto6ZxZf2vc7K6RGIja0l4eaVm5AOzY,1503
29
30
  opengradient/workflow_models/workflow_models.py,sha256=d4C_gs39DAfy4cdY9Ee6GMXpPfzwvKFpmxzK1A7LNgU,3900
30
- opengradient-0.5.7.dist-info/licenses/LICENSE,sha256=xEcvQ3AxZOtDkrqkys2Mm6Y9diEnaSeQRKvxi-JGnNA,1069
31
- opengradient-0.5.7.dist-info/METADATA,sha256=WmJWW2IPbsKD_fuhafHf_cZeLiCz9_2T8qZUcvw0saU,4019
32
- opengradient-0.5.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- opengradient-0.5.7.dist-info/entry_points.txt,sha256=yUKTaJx8RXnybkob0J62wVBiCp_1agVbgw9uzsmaeJc,54
34
- opengradient-0.5.7.dist-info/top_level.txt,sha256=oC1zimVLa2Yi1LQz8c7x-0IQm92milb5ax8gHBHwDqU,13
35
- opengradient-0.5.7.dist-info/RECORD,,
31
+ opengradient-0.5.9.dist-info/licenses/LICENSE,sha256=xEcvQ3AxZOtDkrqkys2Mm6Y9diEnaSeQRKvxi-JGnNA,1069
32
+ opengradient-0.5.9.dist-info/METADATA,sha256=kSTyBctZ-r4h3ilq7DRgxvxQhYO4ejUl3KbwcDX1Ygs,4215
33
+ opengradient-0.5.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
34
+ opengradient-0.5.9.dist-info/entry_points.txt,sha256=yUKTaJx8RXnybkob0J62wVBiCp_1agVbgw9uzsmaeJc,54
35
+ opengradient-0.5.9.dist-info/top_level.txt,sha256=oC1zimVLa2Yi1LQz8c7x-0IQm92milb5ax8gHBHwDqU,13
36
+ opengradient-0.5.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5