opengradient 0.5.7__py3-none-any.whl → 0.5.8__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/__init__.py +28 -4
- opengradient/cli.py +67 -52
- opengradient/client.py +120 -121
- opengradient/defaults.py +1 -1
- opengradient/llm/og_langchain.py +6 -1
- opengradient/types.py +38 -10
- {opengradient-0.5.7.dist-info → opengradient-0.5.8.dist-info}/METADATA +5 -2
- {opengradient-0.5.7.dist-info → opengradient-0.5.8.dist-info}/RECORD +12 -12
- {opengradient-0.5.7.dist-info → opengradient-0.5.8.dist-info}/WHEEL +0 -0
- {opengradient-0.5.7.dist-info → opengradient-0.5.8.dist-info}/entry_points.txt +0 -0
- {opengradient-0.5.7.dist-info → opengradient-0.5.8.dist-info}/licenses/LICENSE +0 -0
- {opengradient-0.5.7.dist-info → opengradient-0.5.8.dist-info}/top_level.txt +0 -0
opengradient/__init__.py
CHANGED
|
@@ -20,6 +20,7 @@ from .types import (
|
|
|
20
20
|
ModelOutput,
|
|
21
21
|
ModelRepository,
|
|
22
22
|
FileUploadResult,
|
|
23
|
+
x402SettlementMode,
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
from . import llm, alphasense
|
|
@@ -47,10 +48,25 @@ def new_client(
|
|
|
47
48
|
contract_address: Optional inference contract address
|
|
48
49
|
"""
|
|
49
50
|
|
|
50
|
-
return Client(
|
|
51
|
+
return Client(
|
|
52
|
+
email=email,
|
|
53
|
+
password=password,
|
|
54
|
+
private_key=private_key,
|
|
55
|
+
rpc_url=rpc_url,
|
|
56
|
+
api_url=api_url,
|
|
57
|
+
contract_address=contract_address,
|
|
58
|
+
**kwargs,
|
|
59
|
+
)
|
|
51
60
|
|
|
52
61
|
|
|
53
|
-
def init(
|
|
62
|
+
def init(
|
|
63
|
+
email: str,
|
|
64
|
+
password: str,
|
|
65
|
+
private_key: str,
|
|
66
|
+
rpc_url=DEFAULT_RPC_URL,
|
|
67
|
+
api_url=DEFAULT_API_URL,
|
|
68
|
+
contract_address=DEFAULT_INFERENCE_CONTRACT_ADDRESS,
|
|
69
|
+
):
|
|
54
70
|
"""Initialize the OpenGradient SDK with authentication and network settings.
|
|
55
71
|
|
|
56
72
|
Args:
|
|
@@ -62,8 +78,10 @@ def init(email: str, password: str, private_key: str, rpc_url=DEFAULT_RPC_URL, a
|
|
|
62
78
|
contract_address: Optional inference contract address
|
|
63
79
|
"""
|
|
64
80
|
global _client
|
|
65
|
-
|
|
66
|
-
_client = Client(
|
|
81
|
+
|
|
82
|
+
_client = Client(
|
|
83
|
+
private_key=private_key, rpc_url=rpc_url, api_url=api_url, email=email, password=password, contract_address=contract_address
|
|
84
|
+
)
|
|
67
85
|
return _client
|
|
68
86
|
|
|
69
87
|
|
|
@@ -162,6 +180,7 @@ def llm_completion(
|
|
|
162
180
|
stop_sequence: Optional[List[str]] = None,
|
|
163
181
|
temperature: float = 0.0,
|
|
164
182
|
max_retries: Optional[int] = None,
|
|
183
|
+
x402_settlement_mode: Optional[x402SettlementMode] = x402SettlementMode.SETTLE_BATCH,
|
|
165
184
|
) -> TextGenerationOutput:
|
|
166
185
|
"""Generate text completion using an LLM.
|
|
167
186
|
|
|
@@ -173,6 +192,7 @@ def llm_completion(
|
|
|
173
192
|
stop_sequence: Optional list of sequences where generation should stop
|
|
174
193
|
temperature: Sampling temperature (0.0 = deterministic, 1.0 = creative)
|
|
175
194
|
max_retries: Maximum number of retries for failed transactions
|
|
195
|
+
x402_settlement_mode: Settlement modes for x402 payment protocol transactions (enum x402SettlementMode)
|
|
176
196
|
|
|
177
197
|
Returns:
|
|
178
198
|
TextGenerationOutput: Transaction hash and generated text
|
|
@@ -190,6 +210,7 @@ def llm_completion(
|
|
|
190
210
|
stop_sequence=stop_sequence,
|
|
191
211
|
temperature=temperature,
|
|
192
212
|
max_retries=max_retries,
|
|
213
|
+
x402_settlement_mode=x402_settlement_mode
|
|
193
214
|
)
|
|
194
215
|
|
|
195
216
|
|
|
@@ -203,6 +224,7 @@ def llm_chat(
|
|
|
203
224
|
tools: Optional[List[Dict]] = None,
|
|
204
225
|
tool_choice: Optional[str] = None,
|
|
205
226
|
max_retries: Optional[int] = None,
|
|
227
|
+
x402_settlement_mode: Optional[x402SettlementMode] = x402SettlementMode.SETTLE_BATCH,
|
|
206
228
|
) -> TextGenerationOutput:
|
|
207
229
|
"""Have a chat conversation with an LLM.
|
|
208
230
|
|
|
@@ -216,6 +238,7 @@ def llm_chat(
|
|
|
216
238
|
tools: Optional list of tools the model can use
|
|
217
239
|
tool_choice: Optional specific tool to use
|
|
218
240
|
max_retries: Maximum number of retries for failed transactions
|
|
241
|
+
x402_settlement_mode: Settlement modes for x402 payment protocol transactions (enum x402SettlementMode)
|
|
219
242
|
|
|
220
243
|
Returns:
|
|
221
244
|
TextGenerationOutput
|
|
@@ -235,6 +258,7 @@ def llm_chat(
|
|
|
235
258
|
tools=tools,
|
|
236
259
|
tool_choice=tool_choice,
|
|
237
260
|
max_retries=max_retries,
|
|
261
|
+
x402_settlement_mode=x402_settlement_mode
|
|
238
262
|
)
|
|
239
263
|
|
|
240
264
|
|
opengradient/cli.py
CHANGED
|
@@ -80,6 +80,7 @@ x402SettlementModes = {
|
|
|
80
80
|
"settle-metadata": x402SettlementMode.SETTLE_METADATA,
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
|
|
83
84
|
def initialize_config(ctx):
|
|
84
85
|
"""Interactively initialize OpenGradient config"""
|
|
85
86
|
if ctx.obj: # Check if config data already exists
|
|
@@ -140,7 +141,7 @@ def cli(ctx):
|
|
|
140
141
|
openai_api_key = ctx.obj.get("openai_api_key")
|
|
141
142
|
anthropic_api_key = ctx.obj.get("anthropic_api_key")
|
|
142
143
|
google_api_key = ctx.obj.get("google_api_key")
|
|
143
|
-
|
|
144
|
+
|
|
144
145
|
ctx.obj["client"] = Client(
|
|
145
146
|
private_key=ctx.obj["private_key"],
|
|
146
147
|
rpc_url=DEFAULT_RPC_URL,
|
|
@@ -219,9 +220,9 @@ def clear(ctx):
|
|
|
219
220
|
def set_api_key(ctx, provider: str, key: str):
|
|
220
221
|
"""
|
|
221
222
|
Set API key for external LLM providers.
|
|
222
|
-
|
|
223
|
+
|
|
223
224
|
Example usage:
|
|
224
|
-
|
|
225
|
+
|
|
225
226
|
\b
|
|
226
227
|
opengradient config set-api-key --provider openai --key ..
|
|
227
228
|
opengradient config set-api-key --provider anthropic --key ...
|
|
@@ -230,7 +231,7 @@ def set_api_key(ctx, provider: str, key: str):
|
|
|
230
231
|
config_key = f"{provider}_api_key"
|
|
231
232
|
ctx.obj[config_key] = key
|
|
232
233
|
save_og_config(ctx)
|
|
233
|
-
|
|
234
|
+
|
|
234
235
|
click.secho(f"✅ API key for {provider} has been set", fg="green")
|
|
235
236
|
click.echo("You can now use models from this provider in completion and chat commands.")
|
|
236
237
|
|
|
@@ -241,9 +242,9 @@ def set_api_key(ctx, provider: str, key: str):
|
|
|
241
242
|
def remove_api_key(ctx, provider: str):
|
|
242
243
|
"""
|
|
243
244
|
Remove API key for an external LLM provider.
|
|
244
|
-
|
|
245
|
+
|
|
245
246
|
Example usage:
|
|
246
|
-
|
|
247
|
+
|
|
247
248
|
\b
|
|
248
249
|
opengradient config remove-api-key --provider openai
|
|
249
250
|
"""
|
|
@@ -417,52 +418,68 @@ def infer(ctx, model_cid: str, inference_mode: str, input_data, input_file: Path
|
|
|
417
418
|
help="Model identifier (local model from LLM enum or external model like 'gpt-4o', 'gemini-2.5-flash-lite', etc.)",
|
|
418
419
|
)
|
|
419
420
|
@click.option(
|
|
420
|
-
"--mode",
|
|
421
|
-
"inference_mode",
|
|
422
|
-
type=click.Choice(LlmInferenceModes.keys()),
|
|
423
|
-
default="VANILLA",
|
|
424
|
-
help="Inference mode (only applies to local models, default: VANILLA)"
|
|
421
|
+
"--mode",
|
|
422
|
+
"inference_mode",
|
|
423
|
+
type=click.Choice(LlmInferenceModes.keys()),
|
|
424
|
+
default="VANILLA",
|
|
425
|
+
help="Inference mode (only applies to local models, default: VANILLA)",
|
|
425
426
|
)
|
|
426
427
|
@click.option("--prompt", "-p", required=True, help="Input prompt for the LLM completion")
|
|
427
428
|
@click.option("--max-tokens", type=int, default=100, help="Maximum number of tokens for LLM completion output")
|
|
428
429
|
@click.option("--stop-sequence", multiple=True, help="Stop sequences for LLM")
|
|
429
430
|
@click.option("--temperature", type=float, default=0.0, help="Temperature for LLM inference (0.0 to 1.0)")
|
|
430
431
|
@click.option("--local", is_flag=True, help="Force use of local model even if not in LLM enum")
|
|
431
|
-
@click.option(
|
|
432
|
+
@click.option(
|
|
433
|
+
"--x402-settlement-mode",
|
|
434
|
+
"x402_settlement_mode",
|
|
435
|
+
type=click.Choice(x402SettlementModes.keys()),
|
|
436
|
+
default="settle-batch",
|
|
437
|
+
help="Settlement mode for x402 payments: settle (hashes only), settle-batch (batched, default), settle-metadata (full data)",
|
|
438
|
+
)
|
|
432
439
|
@click.pass_context
|
|
433
|
-
def completion(
|
|
440
|
+
def completion(
|
|
441
|
+
ctx,
|
|
442
|
+
model_cid: str,
|
|
443
|
+
inference_mode: str,
|
|
444
|
+
x402_settlement_mode: str,
|
|
445
|
+
prompt: str,
|
|
446
|
+
max_tokens: int,
|
|
447
|
+
stop_sequence: List[str],
|
|
448
|
+
temperature: float,
|
|
449
|
+
local: bool,
|
|
450
|
+
):
|
|
434
451
|
"""
|
|
435
452
|
Run completion inference on an LLM model (local or external).
|
|
436
453
|
|
|
437
|
-
This command supports both local OpenGradient models and external providers
|
|
438
|
-
(OpenAI, Anthropic, Google, etc.). For external models, make sure to set
|
|
454
|
+
This command supports both local OpenGradient models and external providers
|
|
455
|
+
(OpenAI, Anthropic, Google, etc.). For external models, make sure to set
|
|
439
456
|
the appropriate API key using 'opengradient config set-api-key'.
|
|
440
457
|
|
|
441
458
|
Example usage:
|
|
442
459
|
|
|
443
460
|
\b
|
|
444
|
-
#
|
|
445
|
-
opengradient completion --model
|
|
446
|
-
|
|
461
|
+
# TEE model
|
|
462
|
+
opengradient completion --model anthropic/claude-3.5-haiku --prompt "Hello, how are you?" --max-tokens 50
|
|
463
|
+
|
|
447
464
|
# External OpenAI model
|
|
448
465
|
opengradient completion --model gpt-4o --prompt "Translate to French: Hello world" --max-tokens 50
|
|
449
|
-
|
|
466
|
+
|
|
450
467
|
# External Anthropic model
|
|
451
|
-
opengradient completion --model claude-haiku-4-5-20251001--prompt "Write a haiku about coding" --max-tokens 100
|
|
452
|
-
|
|
468
|
+
opengradient completion --model claude-haiku-4-5-20251001 --prompt "Write a haiku about coding" --max-tokens 100
|
|
469
|
+
|
|
453
470
|
# External Google model
|
|
454
471
|
opengradient completion --model gemini-2.5-flash-lite --prompt "Explain quantum computing" --max-tokens 200
|
|
455
472
|
"""
|
|
456
473
|
client: Client = ctx.obj["client"]
|
|
457
|
-
|
|
474
|
+
|
|
458
475
|
try:
|
|
459
476
|
is_local = local or model_cid in [llm.value for llm in LLM]
|
|
460
|
-
|
|
477
|
+
|
|
461
478
|
if is_local:
|
|
462
479
|
click.echo(f'Running LLM completion inference for local model "{model_cid}"\n')
|
|
463
480
|
else:
|
|
464
481
|
click.echo(f'Running LLM completion inference for external model "{model_cid}"\n')
|
|
465
|
-
|
|
482
|
+
|
|
466
483
|
completion_output = client.llm_completion(
|
|
467
484
|
model_cid=model_cid,
|
|
468
485
|
inference_mode=LlmInferenceModes[inference_mode],
|
|
@@ -475,7 +492,7 @@ def completion(ctx, model_cid: str, inference_mode: str, x402_settlement_mode: s
|
|
|
475
492
|
)
|
|
476
493
|
|
|
477
494
|
print_llm_completion_result(model_cid, completion_output.transaction_hash, completion_output.completion_output, is_local)
|
|
478
|
-
|
|
495
|
+
|
|
479
496
|
except Exception as e:
|
|
480
497
|
click.echo(f"Error running LLM completion: {str(e)}")
|
|
481
498
|
|
|
@@ -485,7 +502,7 @@ def print_llm_completion_result(model_cid, tx_hash, llm_output, is_local=True):
|
|
|
485
502
|
click.echo("──────────────────────────────────────")
|
|
486
503
|
click.echo("Model: ", nl=False)
|
|
487
504
|
click.secho(model_cid, fg="cyan", bold=True)
|
|
488
|
-
|
|
505
|
+
|
|
489
506
|
if is_local and tx_hash != "external":
|
|
490
507
|
click.echo("Transaction hash: ", nl=False)
|
|
491
508
|
click.secho(tx_hash, fg="cyan", bold=True)
|
|
@@ -495,7 +512,7 @@ def print_llm_completion_result(model_cid, tx_hash, llm_output, is_local=True):
|
|
|
495
512
|
else:
|
|
496
513
|
click.echo("Source: ", nl=False)
|
|
497
514
|
click.secho("External Provider", fg="cyan", bold=True)
|
|
498
|
-
|
|
515
|
+
|
|
499
516
|
click.echo("──────────────────────────────────────")
|
|
500
517
|
click.secho("LLM Output:", fg="yellow", bold=True)
|
|
501
518
|
click.echo()
|
|
@@ -512,11 +529,11 @@ def print_llm_completion_result(model_cid, tx_hash, llm_output, is_local=True):
|
|
|
512
529
|
help="Model identifier (local model from LLM enum or external model like 'gpt-4o', 'gemini-2.5-flash-lite', etc.)",
|
|
513
530
|
)
|
|
514
531
|
@click.option(
|
|
515
|
-
"--mode",
|
|
516
|
-
"inference_mode",
|
|
517
|
-
type=click.Choice(LlmInferenceModes.keys()),
|
|
518
|
-
default="VANILLA",
|
|
519
|
-
help="Inference mode (only applies to local models, default: VANILLA)"
|
|
532
|
+
"--mode",
|
|
533
|
+
"inference_mode",
|
|
534
|
+
type=click.Choice(LlmInferenceModes.keys()),
|
|
535
|
+
default="VANILLA",
|
|
536
|
+
help="Inference mode (only applies to local models, default: VANILLA)",
|
|
520
537
|
)
|
|
521
538
|
@click.option("--messages", type=str, required=False, help="Input messages for the chat inference in JSON format")
|
|
522
539
|
@click.option(
|
|
@@ -530,14 +547,16 @@ def print_llm_completion_result(model_cid, tx_hash, llm_output, is_local=True):
|
|
|
530
547
|
@click.option("--temperature", type=float, default=0.0, help="Temperature for LLM inference (0.0 to 1.0)")
|
|
531
548
|
@click.option("--tools", type=str, default=None, help="Tool configurations in JSON format")
|
|
532
549
|
@click.option(
|
|
533
|
-
"--tools-file",
|
|
534
|
-
type=click.Path(exists=True, path_type=Path),
|
|
535
|
-
required=False,
|
|
536
|
-
help="Path to JSON file containing tool configurations"
|
|
550
|
+
"--tools-file", type=click.Path(exists=True, path_type=Path), required=False, help="Path to JSON file containing tool configurations"
|
|
537
551
|
)
|
|
538
552
|
@click.option("--tool-choice", type=str, default="", help="Specific tool choice for the LLM")
|
|
539
553
|
@click.option("--local", is_flag=True, help="Force use of local model even if not in LLM enum")
|
|
540
|
-
@click.option(
|
|
554
|
+
@click.option(
|
|
555
|
+
"--x402-settlement-mode",
|
|
556
|
+
type=click.Choice(x402SettlementModes.keys()),
|
|
557
|
+
default="settle-batch",
|
|
558
|
+
help="Settlement mode for x402 payments: settle (hashes only), settle-batch (batched, default), settle-metadata (full data)",
|
|
559
|
+
)
|
|
541
560
|
@click.pass_context
|
|
542
561
|
def chat(
|
|
543
562
|
ctx,
|
|
@@ -563,25 +582,25 @@ def chat(
|
|
|
563
582
|
Example usage:
|
|
564
583
|
|
|
565
584
|
\b
|
|
566
|
-
#
|
|
567
|
-
opengradient chat --model
|
|
568
|
-
|
|
585
|
+
# TEE model
|
|
586
|
+
opengradient chat --model anthropic/claude-3.5-haiku --messages '[{"role":"user","content":"hello"}]' --max-tokens 50
|
|
587
|
+
|
|
569
588
|
# External OpenAI model with tools
|
|
570
589
|
opengradient chat --model gpt-4o --messages-file messages.json --tools-file tools.json --max-tokens 200
|
|
571
|
-
|
|
590
|
+
|
|
572
591
|
# External Anthropic model
|
|
573
592
|
opengradient chat --model claude-haiku-4-5-20251001 --messages '[{"role":"user","content":"Write a poem"}]' --max-tokens 100
|
|
574
593
|
"""
|
|
575
594
|
client: Client = ctx.obj["client"]
|
|
576
|
-
|
|
595
|
+
|
|
577
596
|
try:
|
|
578
597
|
is_local = local or model_cid in [llm.value for llm in LLM]
|
|
579
|
-
|
|
598
|
+
|
|
580
599
|
if is_local:
|
|
581
600
|
click.echo(f'Running LLM chat inference for local model "{model_cid}"\n')
|
|
582
601
|
else:
|
|
583
602
|
click.echo(f'Running LLM chat inference for external model "{model_cid}"\n')
|
|
584
|
-
|
|
603
|
+
|
|
585
604
|
# Parse messages
|
|
586
605
|
if not messages and not messages_file:
|
|
587
606
|
click.echo("Must specify either messages or messages-file")
|
|
@@ -651,13 +670,9 @@ def chat(
|
|
|
651
670
|
)
|
|
652
671
|
|
|
653
672
|
print_llm_chat_result(
|
|
654
|
-
model_cid,
|
|
655
|
-
completion_output.transaction_hash,
|
|
656
|
-
completion_output.finish_reason,
|
|
657
|
-
completion_output.chat_output,
|
|
658
|
-
is_local
|
|
673
|
+
model_cid, completion_output.transaction_hash, completion_output.finish_reason, completion_output.chat_output, is_local
|
|
659
674
|
)
|
|
660
|
-
|
|
675
|
+
|
|
661
676
|
except Exception as e:
|
|
662
677
|
click.echo(f"Error running LLM chat inference: {str(e)}")
|
|
663
678
|
|
|
@@ -667,7 +682,7 @@ def print_llm_chat_result(model_cid, tx_hash, finish_reason, chat_output, is_loc
|
|
|
667
682
|
click.echo("──────────────────────────────────────")
|
|
668
683
|
click.echo("Model: ", nl=False)
|
|
669
684
|
click.secho(model_cid, fg="cyan", bold=True)
|
|
670
|
-
|
|
685
|
+
|
|
671
686
|
if is_local and tx_hash != "external":
|
|
672
687
|
click.echo("Transaction hash: ", nl=False)
|
|
673
688
|
click.secho(tx_hash, fg="cyan", bold=True)
|
|
@@ -677,7 +692,7 @@ def print_llm_chat_result(model_cid, tx_hash, finish_reason, chat_output, is_loc
|
|
|
677
692
|
else:
|
|
678
693
|
click.echo("Source: ", nl=False)
|
|
679
694
|
click.secho("External Provider", fg="cyan", bold=True)
|
|
680
|
-
|
|
695
|
+
|
|
681
696
|
click.echo("──────────────────────────────────────")
|
|
682
697
|
click.secho("Finish Reason: ", fg="yellow", bold=True)
|
|
683
698
|
click.echo()
|
opengradient/client.py
CHANGED
|
@@ -35,11 +35,12 @@ from .types import (
|
|
|
35
35
|
FileUploadResult,
|
|
36
36
|
)
|
|
37
37
|
from .defaults import (
|
|
38
|
-
DEFAULT_IMAGE_GEN_HOST,
|
|
39
|
-
DEFAULT_IMAGE_GEN_PORT,
|
|
38
|
+
DEFAULT_IMAGE_GEN_HOST,
|
|
39
|
+
DEFAULT_IMAGE_GEN_PORT,
|
|
40
40
|
DEFAULT_SCHEDULER_ADDRESS,
|
|
41
|
-
DEFAULT_LLM_SERVER_URL,
|
|
42
|
-
DEFAULT_OPENGRADIENT_LLM_SERVER_URL
|
|
41
|
+
DEFAULT_LLM_SERVER_URL,
|
|
42
|
+
DEFAULT_OPENGRADIENT_LLM_SERVER_URL,
|
|
43
|
+
)
|
|
43
44
|
from .utils import convert_array_to_model_output, convert_to_model_input, convert_to_model_output
|
|
44
45
|
|
|
45
46
|
_FIREBASE_CONFIG = {
|
|
@@ -65,6 +66,7 @@ PRECOMPILE_CONTRACT_ADDRESS = "0x00000000000000000000000000000000000000F4"
|
|
|
65
66
|
X402_PROCESSING_HASH_HEADER = "x-processing-hash"
|
|
66
67
|
X402_PLACEHOLDER_API_KEY = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
|
|
67
68
|
|
|
69
|
+
|
|
68
70
|
class Client:
|
|
69
71
|
_inference_hub_contract_address: str
|
|
70
72
|
_blockchain: Web3
|
|
@@ -76,20 +78,21 @@ class Client:
|
|
|
76
78
|
_precompile_abi: Dict
|
|
77
79
|
_llm_server_url: str
|
|
78
80
|
_external_api_keys: Dict[str, str]
|
|
81
|
+
|
|
79
82
|
def __init__(
|
|
80
|
-
self,
|
|
81
|
-
private_key: str,
|
|
82
|
-
rpc_url: str,
|
|
83
|
-
api_url: str,
|
|
84
|
-
contract_address: str,
|
|
85
|
-
email: Optional[str] = None,
|
|
86
|
-
password: Optional[str] = None,
|
|
83
|
+
self,
|
|
84
|
+
private_key: str,
|
|
85
|
+
rpc_url: str,
|
|
86
|
+
api_url: str,
|
|
87
|
+
contract_address: str,
|
|
88
|
+
email: Optional[str] = None,
|
|
89
|
+
password: Optional[str] = None,
|
|
87
90
|
llm_server_url: Optional[str] = DEFAULT_LLM_SERVER_URL,
|
|
88
91
|
og_llm_server_url: Optional[str] = DEFAULT_OPENGRADIENT_LLM_SERVER_URL,
|
|
89
92
|
openai_api_key: Optional[str] = None,
|
|
90
93
|
anthropic_api_key: Optional[str] = None,
|
|
91
94
|
google_api_key: Optional[str] = None,
|
|
92
|
-
|
|
95
|
+
):
|
|
93
96
|
"""
|
|
94
97
|
Initialize the Client with private key, RPC URL, and contract address.
|
|
95
98
|
|
|
@@ -120,7 +123,7 @@ class Client:
|
|
|
120
123
|
|
|
121
124
|
self._llm_server_url = llm_server_url
|
|
122
125
|
self._og_llm_server_url = og_llm_server_url
|
|
123
|
-
|
|
126
|
+
|
|
124
127
|
self._external_api_keys = {}
|
|
125
128
|
if openai_api_key or os.getenv("OPENAI_API_KEY"):
|
|
126
129
|
self._external_api_keys["openai"] = openai_api_key or os.getenv("OPENAI_API_KEY")
|
|
@@ -132,7 +135,7 @@ class Client:
|
|
|
132
135
|
def set_api_key(self, provider: str, api_key: str):
|
|
133
136
|
"""
|
|
134
137
|
Set or update API key for an external provider.
|
|
135
|
-
|
|
138
|
+
|
|
136
139
|
Args:
|
|
137
140
|
provider: Provider name (e.g., 'openai', 'anthropic', 'google')
|
|
138
141
|
api_key: The API key for the provider
|
|
@@ -142,10 +145,10 @@ class Client:
|
|
|
142
145
|
def _is_local_model(self, model_cid: str) -> bool:
|
|
143
146
|
"""
|
|
144
147
|
Check if a model is hosted locally on OpenGradient.
|
|
145
|
-
|
|
148
|
+
|
|
146
149
|
Args:
|
|
147
150
|
model_cid: Model identifier
|
|
148
|
-
|
|
151
|
+
|
|
149
152
|
Returns:
|
|
150
153
|
True if model is local, False if it should use external provider
|
|
151
154
|
"""
|
|
@@ -158,7 +161,7 @@ class Client:
|
|
|
158
161
|
def _get_provider_from_model(self, model: str) -> str:
|
|
159
162
|
"""Infer provider from model name."""
|
|
160
163
|
model_lower = model.lower()
|
|
161
|
-
|
|
164
|
+
|
|
162
165
|
if "gpt" in model_lower or model.startswith("openai/"):
|
|
163
166
|
return "openai"
|
|
164
167
|
elif "claude" in model_lower or model.startswith("anthropic/"):
|
|
@@ -173,10 +176,10 @@ class Client:
|
|
|
173
176
|
def _get_api_key_for_model(self, model: str) -> Optional[str]:
|
|
174
177
|
"""
|
|
175
178
|
Get the appropriate API key for a model.
|
|
176
|
-
|
|
179
|
+
|
|
177
180
|
Args:
|
|
178
181
|
model: Model identifier
|
|
179
|
-
|
|
182
|
+
|
|
180
183
|
Returns:
|
|
181
184
|
API key string or None
|
|
182
185
|
"""
|
|
@@ -451,11 +454,17 @@ class Client:
|
|
|
451
454
|
temperature (float): Temperature for LLM inference, between 0 and 1. Default is 0.0.
|
|
452
455
|
max_retries (int, optional): Maximum number of retry attempts for blockchain transactions.
|
|
453
456
|
local_model (bool, optional): Force use of local model even if not in LLM enum.
|
|
457
|
+
x402_settlement_mode (x402SettlementMode, optional): Settlement mode for x402 payments.
|
|
458
|
+
- SETTLE: Records input/output hashes only (most privacy-preserving).
|
|
459
|
+
- SETTLE_BATCH: Aggregates multiple inferences into batch hashes (most cost-efficient).
|
|
460
|
+
- SETTLE_METADATA: Records full model info, complete input/output data, and all metadata.
|
|
461
|
+
Defaults to SETTLE_BATCH.
|
|
454
462
|
|
|
455
463
|
Returns:
|
|
456
464
|
TextGenerationOutput: Generated text results including:
|
|
457
465
|
- Transaction hash (or "external" for external providers)
|
|
458
466
|
- String of completion output
|
|
467
|
+
- Payment hash for x402 transactions (when using x402 settlement)
|
|
459
468
|
|
|
460
469
|
Raises:
|
|
461
470
|
OpenGradientError: If the inference fails.
|
|
@@ -467,14 +476,14 @@ class Client:
|
|
|
467
476
|
return OpenGradientError("That model CID is not supported yet for TEE inference")
|
|
468
477
|
|
|
469
478
|
return self._external_llm_completion(
|
|
470
|
-
model=model_cid.split(
|
|
479
|
+
model=model_cid.split("/")[1],
|
|
471
480
|
prompt=prompt,
|
|
472
481
|
max_tokens=max_tokens,
|
|
473
482
|
stop_sequence=stop_sequence,
|
|
474
483
|
temperature=temperature,
|
|
475
484
|
x402_settlement_mode=x402_settlement_mode,
|
|
476
485
|
)
|
|
477
|
-
|
|
486
|
+
|
|
478
487
|
# Original local model logic
|
|
479
488
|
def execute_transaction():
|
|
480
489
|
if inference_mode != LlmInferenceMode.VANILLA:
|
|
@@ -482,10 +491,10 @@ class Client:
|
|
|
482
491
|
|
|
483
492
|
if model_cid not in [llm.value for llm in LLM]:
|
|
484
493
|
raise OpenGradientError("That model CID is not yet supported for inference")
|
|
485
|
-
|
|
494
|
+
|
|
486
495
|
model_name = model_cid
|
|
487
496
|
if model_cid in [llm.value for llm in TEE_LLM]:
|
|
488
|
-
model_name = model_cid.split(
|
|
497
|
+
model_name = model_cid.split("/")[1]
|
|
489
498
|
|
|
490
499
|
contract = self._blockchain.eth.contract(address=self._inference_hub_contract_address, abi=self._inference_abi)
|
|
491
500
|
|
|
@@ -523,55 +532,49 @@ class Client:
|
|
|
523
532
|
) -> TextGenerationOutput:
|
|
524
533
|
"""
|
|
525
534
|
Route completion request to external LLM server with x402 payments.
|
|
526
|
-
|
|
535
|
+
|
|
527
536
|
Args:
|
|
528
537
|
model: Model identifier
|
|
529
538
|
prompt: Input prompt
|
|
530
539
|
max_tokens: Maximum tokens to generate
|
|
531
540
|
stop_sequence: Stop sequences
|
|
532
541
|
temperature: Sampling temperature
|
|
533
|
-
|
|
542
|
+
|
|
534
543
|
Returns:
|
|
535
544
|
TextGenerationOutput with completion
|
|
536
|
-
|
|
545
|
+
|
|
537
546
|
Raises:
|
|
538
547
|
OpenGradientError: If request fails
|
|
539
548
|
"""
|
|
540
549
|
api_key = self._get_api_key_for_model(model)
|
|
541
|
-
|
|
550
|
+
|
|
542
551
|
if api_key:
|
|
543
552
|
logging.debug("External LLM completions using API key")
|
|
544
553
|
url = f"{self._llm_server_url}/v1/completions"
|
|
545
|
-
|
|
546
|
-
headers = {
|
|
547
|
-
|
|
548
|
-
"Authorization": f"Bearer {api_key}"
|
|
549
|
-
}
|
|
550
|
-
|
|
554
|
+
|
|
555
|
+
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
|
|
556
|
+
|
|
551
557
|
payload = {
|
|
552
558
|
"model": model,
|
|
553
559
|
"prompt": prompt,
|
|
554
560
|
"max_tokens": max_tokens,
|
|
555
561
|
"temperature": temperature,
|
|
556
562
|
}
|
|
557
|
-
|
|
563
|
+
|
|
558
564
|
if stop_sequence:
|
|
559
565
|
payload["stop"] = stop_sequence
|
|
560
|
-
|
|
566
|
+
|
|
561
567
|
try:
|
|
562
568
|
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
563
569
|
response.raise_for_status()
|
|
564
|
-
|
|
570
|
+
|
|
565
571
|
result = response.json()
|
|
566
|
-
|
|
567
|
-
return TextGenerationOutput(
|
|
568
|
-
|
|
569
|
-
completion_output=result.get("completion")
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
+
|
|
573
|
+
return TextGenerationOutput(transaction_hash="external", completion_output=result.get("completion"))
|
|
574
|
+
|
|
572
575
|
except requests.RequestException as e:
|
|
573
576
|
error_msg = f"External LLM completion failed: {str(e)}"
|
|
574
|
-
if hasattr(e,
|
|
577
|
+
if hasattr(e, "response") and e.response is not None:
|
|
575
578
|
try:
|
|
576
579
|
error_detail = e.response.json()
|
|
577
580
|
error_msg += f" - {error_detail}"
|
|
@@ -591,20 +594,20 @@ class Client:
|
|
|
591
594
|
"Authorization": f"Bearer {X402_PLACEHOLDER_API_KEY}",
|
|
592
595
|
"X-SETTLEMENT-TYPE": x402_settlement_mode,
|
|
593
596
|
}
|
|
594
|
-
|
|
597
|
+
|
|
595
598
|
payload = {
|
|
596
599
|
"model": model,
|
|
597
600
|
"prompt": prompt,
|
|
598
601
|
"max_tokens": max_tokens,
|
|
599
602
|
"temperature": temperature,
|
|
600
603
|
}
|
|
601
|
-
|
|
604
|
+
|
|
602
605
|
if stop_sequence:
|
|
603
606
|
payload["stop"] = stop_sequence
|
|
604
|
-
|
|
607
|
+
|
|
605
608
|
try:
|
|
606
609
|
response = await client.post("/v1/completions", json=payload, headers=headers, timeout=60)
|
|
607
|
-
|
|
610
|
+
|
|
608
611
|
# Read the response content
|
|
609
612
|
content = await response.aread()
|
|
610
613
|
result = json.loads(content.decode())
|
|
@@ -612,24 +615,22 @@ class Client:
|
|
|
612
615
|
|
|
613
616
|
if X402_PROCESSING_HASH_HEADER in response.headers:
|
|
614
617
|
payment_hash = response.headers[X402_PROCESSING_HASH_HEADER]
|
|
615
|
-
|
|
618
|
+
|
|
616
619
|
return TextGenerationOutput(
|
|
617
|
-
transaction_hash="external",
|
|
618
|
-
completion_output=result.get("completion"),
|
|
619
|
-
payment_hash=payment_hash
|
|
620
|
+
transaction_hash="external", completion_output=result.get("completion"), payment_hash=payment_hash
|
|
620
621
|
)
|
|
621
|
-
|
|
622
|
+
|
|
622
623
|
except Exception as e:
|
|
623
624
|
error_msg = f"External LLM completion request failed: {str(e)}"
|
|
624
625
|
logging.error(error_msg)
|
|
625
626
|
raise OpenGradientError(error_msg)
|
|
626
|
-
|
|
627
|
+
|
|
627
628
|
try:
|
|
628
629
|
# Run the async function in a sync context
|
|
629
630
|
return asyncio.run(make_request())
|
|
630
631
|
except Exception as e:
|
|
631
632
|
error_msg = f"External LLM completion failed: {str(e)}"
|
|
632
|
-
if hasattr(e,
|
|
633
|
+
if hasattr(e, "response") and e.response is not None:
|
|
633
634
|
try:
|
|
634
635
|
error_detail = e.response.json()
|
|
635
636
|
error_msg += f" - {error_detail}"
|
|
@@ -666,9 +667,18 @@ class Client:
|
|
|
666
667
|
tool_choice (str, optional): Sets a specific tool to choose.
|
|
667
668
|
max_retries (int, optional): Maximum number of retry attempts.
|
|
668
669
|
local_model (bool, optional): Force use of local model.
|
|
670
|
+
x402_settlement_mode (x402SettlementMode, optional): Settlement mode for x402 payments.
|
|
671
|
+
- SETTLE: Records input/output hashes only (most privacy-preserving).
|
|
672
|
+
- SETTLE_BATCH: Aggregates multiple inferences into batch hashes (most cost-efficient).
|
|
673
|
+
- SETTLE_METADATA: Records full model info, complete input/output data, and all metadata.
|
|
674
|
+
Defaults to SETTLE_BATCH.
|
|
669
675
|
|
|
670
676
|
Returns:
|
|
671
|
-
TextGenerationOutput: Generated text results
|
|
677
|
+
TextGenerationOutput: Generated text results including:
|
|
678
|
+
- chat_output: Dict with role, content, and tool_calls
|
|
679
|
+
- transaction_hash: Blockchain hash (or "external" for external providers)
|
|
680
|
+
- finish_reason: Reason for completion (e.g., "stop", "tool_call")
|
|
681
|
+
- payment_hash: Payment hash for x402 transactions (when using x402 settlement)
|
|
672
682
|
|
|
673
683
|
Raises:
|
|
674
684
|
OpenGradientError: If the inference fails.
|
|
@@ -680,7 +690,7 @@ class Client:
|
|
|
680
690
|
return OpenGradientError("That model CID is not supported yet for TEE inference")
|
|
681
691
|
|
|
682
692
|
return self._external_llm_chat(
|
|
683
|
-
model=model_cid.split(
|
|
693
|
+
model=model_cid.split("/")[1],
|
|
684
694
|
messages=messages,
|
|
685
695
|
max_tokens=max_tokens,
|
|
686
696
|
stop_sequence=stop_sequence,
|
|
@@ -689,18 +699,18 @@ class Client:
|
|
|
689
699
|
tool_choice=tool_choice,
|
|
690
700
|
x402_settlement_mode=x402_settlement_mode,
|
|
691
701
|
)
|
|
692
|
-
|
|
702
|
+
|
|
693
703
|
# Original local model logic
|
|
694
704
|
def execute_transaction():
|
|
695
705
|
if inference_mode != LlmInferenceMode.VANILLA:
|
|
696
706
|
raise OpenGradientError("Invalid inference mode %s: Inference mode must be VANILLA or TEE" % inference_mode)
|
|
697
|
-
|
|
707
|
+
|
|
698
708
|
if model_cid not in [llm.value for llm in LLM]:
|
|
699
709
|
raise OpenGradientError("That model CID is not yet supported for inference")
|
|
700
|
-
|
|
710
|
+
|
|
701
711
|
model_name = model_cid
|
|
702
712
|
if model_cid in [llm.value for llm in TEE_LLM]:
|
|
703
|
-
model_name = model_cid.split(
|
|
713
|
+
model_name = model_cid.split("/")[1]
|
|
704
714
|
|
|
705
715
|
contract = self._blockchain.eth.contract(address=self._inference_hub_contract_address, abi=self._inference_abi)
|
|
706
716
|
|
|
@@ -771,7 +781,7 @@ class Client:
|
|
|
771
781
|
) -> TextGenerationOutput:
|
|
772
782
|
"""
|
|
773
783
|
Route chat request to external LLM server with x402 payments.
|
|
774
|
-
|
|
784
|
+
|
|
775
785
|
Args:
|
|
776
786
|
model: Model identifier
|
|
777
787
|
messages: List of chat messages
|
|
@@ -780,53 +790,48 @@ class Client:
|
|
|
780
790
|
temperature: Sampling temperature
|
|
781
791
|
tools: Function calling tools
|
|
782
792
|
tool_choice: Tool selection strategy
|
|
783
|
-
|
|
793
|
+
|
|
784
794
|
Returns:
|
|
785
795
|
TextGenerationOutput with chat completion
|
|
786
|
-
|
|
796
|
+
|
|
787
797
|
Raises:
|
|
788
798
|
OpenGradientError: If request fails
|
|
789
799
|
"""
|
|
790
800
|
api_key = self._get_api_key_for_model(model)
|
|
791
|
-
|
|
801
|
+
|
|
792
802
|
if api_key:
|
|
793
803
|
logging.debug("External LLM completion using API key")
|
|
794
804
|
url = f"{self._llm_server_url}/v1/chat/completions"
|
|
795
|
-
|
|
796
|
-
headers = {
|
|
797
|
-
|
|
798
|
-
"Authorization": f"Bearer {api_key}"
|
|
799
|
-
}
|
|
800
|
-
|
|
805
|
+
|
|
806
|
+
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
|
|
807
|
+
|
|
801
808
|
payload = {
|
|
802
809
|
"model": model,
|
|
803
810
|
"messages": messages,
|
|
804
811
|
"max_tokens": max_tokens,
|
|
805
812
|
"temperature": temperature,
|
|
806
813
|
}
|
|
807
|
-
|
|
814
|
+
|
|
808
815
|
if stop_sequence:
|
|
809
816
|
payload["stop"] = stop_sequence
|
|
810
|
-
|
|
817
|
+
|
|
811
818
|
if tools:
|
|
812
819
|
payload["tools"] = tools
|
|
813
820
|
payload["tool_choice"] = tool_choice or "auto"
|
|
814
|
-
|
|
821
|
+
|
|
815
822
|
try:
|
|
816
823
|
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
817
824
|
response.raise_for_status()
|
|
818
|
-
|
|
825
|
+
|
|
819
826
|
result = response.json()
|
|
820
|
-
|
|
827
|
+
|
|
821
828
|
return TextGenerationOutput(
|
|
822
|
-
transaction_hash="external",
|
|
823
|
-
finish_reason=result.get("finish_reason"),
|
|
824
|
-
chat_output=result.get("message")
|
|
829
|
+
transaction_hash="external", finish_reason=result.get("finish_reason"), chat_output=result.get("message")
|
|
825
830
|
)
|
|
826
|
-
|
|
831
|
+
|
|
827
832
|
except requests.RequestException as e:
|
|
828
833
|
error_msg = f"External LLM chat failed: {str(e)}"
|
|
829
|
-
if hasattr(e,
|
|
834
|
+
if hasattr(e, "response") and e.response is not None:
|
|
830
835
|
try:
|
|
831
836
|
error_detail = e.response.json()
|
|
832
837
|
error_msg += f" - {error_detail}"
|
|
@@ -844,26 +849,26 @@ class Client:
|
|
|
844
849
|
headers = {
|
|
845
850
|
"Content-Type": "application/json",
|
|
846
851
|
"Authorization": f"Bearer {X402_PLACEHOLDER_API_KEY}",
|
|
847
|
-
"X-SETTLEMENT-TYPE": x402_settlement_mode
|
|
852
|
+
"X-SETTLEMENT-TYPE": x402_settlement_mode,
|
|
848
853
|
}
|
|
849
|
-
|
|
854
|
+
|
|
850
855
|
payload = {
|
|
851
856
|
"model": model,
|
|
852
857
|
"messages": messages,
|
|
853
858
|
"max_tokens": max_tokens,
|
|
854
859
|
"temperature": temperature,
|
|
855
860
|
}
|
|
856
|
-
|
|
861
|
+
|
|
857
862
|
if stop_sequence:
|
|
858
863
|
payload["stop"] = stop_sequence
|
|
859
|
-
|
|
864
|
+
|
|
860
865
|
if tools:
|
|
861
866
|
payload["tools"] = tools
|
|
862
867
|
payload["tool_choice"] = tool_choice or "auto"
|
|
863
|
-
|
|
868
|
+
|
|
864
869
|
try:
|
|
865
870
|
response = await client.post("/v1/chat/completions", json=payload, headers=headers, timeout=60)
|
|
866
|
-
|
|
871
|
+
|
|
867
872
|
# Read the response content
|
|
868
873
|
content = await response.aread()
|
|
869
874
|
result = json.loads(content.decode())
|
|
@@ -873,29 +878,29 @@ class Client:
|
|
|
873
878
|
payment_hash = ""
|
|
874
879
|
if X402_PROCESSING_HASH_HEADER in response.headers:
|
|
875
880
|
payment_hash = response.headers[X402_PROCESSING_HASH_HEADER]
|
|
876
|
-
|
|
881
|
+
|
|
877
882
|
choices = result.get("choices")
|
|
878
883
|
if not choices:
|
|
879
884
|
raise OpenGradientError(f"Invalid response: 'choices' missing or empty in {result}")
|
|
880
|
-
|
|
885
|
+
|
|
881
886
|
return TextGenerationOutput(
|
|
882
887
|
transaction_hash="external",
|
|
883
888
|
finish_reason=choices[0].get("finish_reason"),
|
|
884
889
|
chat_output=choices[0].get("message"),
|
|
885
|
-
payment_hash=payment_hash
|
|
890
|
+
payment_hash=payment_hash,
|
|
886
891
|
)
|
|
887
|
-
|
|
892
|
+
|
|
888
893
|
except Exception as e:
|
|
889
894
|
error_msg = f"External LLM chat request failed: {str(e)}"
|
|
890
895
|
logging.error(error_msg)
|
|
891
896
|
raise OpenGradientError(error_msg)
|
|
892
|
-
|
|
897
|
+
|
|
893
898
|
try:
|
|
894
899
|
# Run the async function in a sync context
|
|
895
900
|
return asyncio.run(make_request())
|
|
896
901
|
except Exception as e:
|
|
897
902
|
error_msg = f"External LLM chat failed: {str(e)}"
|
|
898
|
-
if hasattr(e,
|
|
903
|
+
if hasattr(e, "response") and e.response is not None:
|
|
899
904
|
try:
|
|
900
905
|
error_detail = e.response.json()
|
|
901
906
|
error_msg += f" - {error_detail}"
|
|
@@ -1104,12 +1109,12 @@ class Client:
|
|
|
1104
1109
|
except ContractLogicError as e:
|
|
1105
1110
|
try:
|
|
1106
1111
|
run_function.call({"from": self._wallet_account.address})
|
|
1107
|
-
|
|
1112
|
+
|
|
1108
1113
|
except ContractLogicError as call_err:
|
|
1109
1114
|
raise ContractLogicError(f"simulation failed with revert reason: {call_err.args[0]}")
|
|
1110
|
-
|
|
1115
|
+
|
|
1111
1116
|
raise ContractLogicError(f"simulation failed with no revert reason. Reason: {e}")
|
|
1112
|
-
|
|
1117
|
+
|
|
1113
1118
|
gas_limit = int(estimated_gas * 3)
|
|
1114
1119
|
|
|
1115
1120
|
transaction = run_function.build_transaction(
|
|
@@ -1128,10 +1133,10 @@ class Client:
|
|
|
1128
1133
|
if tx_receipt["status"] == 0:
|
|
1129
1134
|
try:
|
|
1130
1135
|
run_function.call({"from": self._wallet_account.address})
|
|
1131
|
-
|
|
1136
|
+
|
|
1132
1137
|
except ContractLogicError as call_err:
|
|
1133
1138
|
raise ContractLogicError(f"Transaction failed with revert reason: {call_err.args[0]}")
|
|
1134
|
-
|
|
1139
|
+
|
|
1135
1140
|
raise ContractLogicError(f"Transaction failed with no revert reason. Receipt: {tx_receipt}")
|
|
1136
1141
|
|
|
1137
1142
|
return tx_hash, tx_receipt
|
|
@@ -1346,45 +1351,42 @@ class Client:
|
|
|
1346
1351
|
results = contract.functions.getLastInferenceResults(num_results).call()
|
|
1347
1352
|
return [convert_array_to_model_output(result) for result in results]
|
|
1348
1353
|
|
|
1349
|
-
|
|
1350
1354
|
def _get_inference_result_from_node(self, inference_id: str, inference_mode: InferenceMode) -> Dict:
|
|
1351
1355
|
"""
|
|
1352
1356
|
Get the inference result from node.
|
|
1353
|
-
|
|
1357
|
+
|
|
1354
1358
|
Args:
|
|
1355
1359
|
inference_id (str): Inference id for a inference request
|
|
1356
|
-
|
|
1360
|
+
|
|
1357
1361
|
Returns:
|
|
1358
1362
|
Dict: The inference result as returned by the node
|
|
1359
|
-
|
|
1363
|
+
|
|
1360
1364
|
Raises:
|
|
1361
1365
|
OpenGradientError: If the request fails or returns an error
|
|
1362
1366
|
"""
|
|
1363
1367
|
try:
|
|
1364
|
-
encoded_id = urllib.parse.quote(inference_id, safe=
|
|
1368
|
+
encoded_id = urllib.parse.quote(inference_id, safe="")
|
|
1365
1369
|
url = f"{self._api_url}/artela-network/artela-rollkit/inference/tx/{encoded_id}"
|
|
1366
|
-
|
|
1370
|
+
|
|
1367
1371
|
response = requests.get(url)
|
|
1368
1372
|
if response.status_code == 200:
|
|
1369
1373
|
resp = response.json()
|
|
1370
1374
|
inference_result = resp.get("inference_results", {})
|
|
1371
1375
|
if inference_result:
|
|
1372
1376
|
decoded_bytes = base64.b64decode(inference_result[0])
|
|
1373
|
-
decoded_string = decoded_bytes.decode(
|
|
1374
|
-
output = json.loads(decoded_string).get("InferenceResult",{})
|
|
1377
|
+
decoded_string = decoded_bytes.decode("utf-8")
|
|
1378
|
+
output = json.loads(decoded_string).get("InferenceResult", {})
|
|
1375
1379
|
if output is None:
|
|
1376
1380
|
raise OpenGradientError("Missing InferenceResult in inference output")
|
|
1377
|
-
|
|
1381
|
+
|
|
1378
1382
|
match inference_mode:
|
|
1379
1383
|
case InferenceMode.VANILLA:
|
|
1380
1384
|
if "VanillaResult" not in output:
|
|
1381
1385
|
raise OpenGradientError("Missing VanillaResult in inference output")
|
|
1382
1386
|
if "model_output" not in output["VanillaResult"]:
|
|
1383
1387
|
raise OpenGradientError("Missing model_output in VanillaResult")
|
|
1384
|
-
return {
|
|
1385
|
-
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
+
return {"output": output["VanillaResult"]["model_output"]}
|
|
1389
|
+
|
|
1388
1390
|
case InferenceMode.TEE:
|
|
1389
1391
|
if "TeeNodeResult" not in output:
|
|
1390
1392
|
raise OpenGradientError("Missing TeeNodeResult in inference output")
|
|
@@ -1393,34 +1395,30 @@ class Client:
|
|
|
1393
1395
|
if "VanillaResponse" in output["TeeNodeResult"]["Response"]:
|
|
1394
1396
|
if "model_output" not in output["TeeNodeResult"]["Response"]["VanillaResponse"]:
|
|
1395
1397
|
raise OpenGradientError("Missing model_output in VanillaResponse")
|
|
1396
|
-
return {
|
|
1397
|
-
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1398
|
+
return {"output": output["TeeNodeResult"]["Response"]["VanillaResponse"]["model_output"]}
|
|
1399
|
+
|
|
1400
1400
|
else:
|
|
1401
1401
|
raise OpenGradientError("Missing VanillaResponse in TeeNodeResult Response")
|
|
1402
|
-
|
|
1402
|
+
|
|
1403
1403
|
case InferenceMode.ZKML:
|
|
1404
1404
|
if "ZkmlResult" not in output:
|
|
1405
1405
|
raise OpenGradientError("Missing ZkmlResult in inference output")
|
|
1406
1406
|
if "model_output" not in output["ZkmlResult"]:
|
|
1407
1407
|
raise OpenGradientError("Missing model_output in ZkmlResult")
|
|
1408
|
-
return {
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1408
|
+
return {"output": output["ZkmlResult"]["model_output"]}
|
|
1409
|
+
|
|
1412
1410
|
case _:
|
|
1413
1411
|
raise OpenGradientError(f"Invalid inference mode: {inference_mode}")
|
|
1414
1412
|
else:
|
|
1415
1413
|
return None
|
|
1416
|
-
|
|
1414
|
+
|
|
1417
1415
|
else:
|
|
1418
1416
|
error_message = f"Failed to get inference result: HTTP {response.status_code}"
|
|
1419
1417
|
if response.text:
|
|
1420
1418
|
error_message += f" - {response.text}"
|
|
1421
1419
|
logging.error(error_message)
|
|
1422
1420
|
raise OpenGradientError(error_message)
|
|
1423
|
-
|
|
1421
|
+
|
|
1424
1422
|
except requests.RequestException as e:
|
|
1425
1423
|
logging.error(f"Request exception when getting inference result: {str(e)}")
|
|
1426
1424
|
raise OpenGradientError(f"Failed to get inference result: {str(e)}")
|
|
@@ -1428,6 +1426,7 @@ class Client:
|
|
|
1428
1426
|
logging.error(f"Unexpected error when getting inference result: {str(e)}", exc_info=True)
|
|
1429
1427
|
raise OpenGradientError(f"Failed to get inference result: {str(e)}")
|
|
1430
1428
|
|
|
1429
|
+
|
|
1431
1430
|
def run_with_retry(txn_function: Callable, max_retries=DEFAULT_MAX_RETRY, retry_delay=DEFAULT_RETRY_DELAY_SEC):
|
|
1432
1431
|
"""
|
|
1433
1432
|
Execute a blockchain transaction with retry logic.
|
opengradient/defaults.py
CHANGED
|
@@ -9,4 +9,4 @@ 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://llm.opengradient.ai"
|
opengradient/llm/og_langchain.py
CHANGED
|
@@ -36,7 +36,12 @@ class OpenGradientChatModel(BaseChatModel):
|
|
|
36
36
|
super().__init__()
|
|
37
37
|
|
|
38
38
|
self._client = Client(
|
|
39
|
-
private_key=private_key,
|
|
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
|
@@ -6,10 +6,37 @@ 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
|
|
137
|
+
"""Enum for different inference modes available for LLM inference (VANILLA, TEE)"""
|
|
111
138
|
|
|
112
139
|
VANILLA = 0
|
|
113
140
|
TEE = 1
|
|
@@ -205,23 +232,23 @@ class LLM(str, Enum):
|
|
|
205
232
|
# META_LLAMA_3_1_70B_INSTRUCT = "meta-llama/Llama-3.1-70B-Instruct"
|
|
206
233
|
# DOBBY_UNHINGED_3_1_8B = "SentientAGI/Dobby-Mini-Unhinged-Llama-3.1-8B"
|
|
207
234
|
# DOBBY_LEASHED_3_1_8B = "SentientAGI/Dobby-Mini-Leashed-Llama-3.1-8B"
|
|
208
|
-
|
|
235
|
+
|
|
209
236
|
# OpenAI models via TEE
|
|
210
237
|
GPT_4_1_2025_04_14 = "openai/gpt-4.1-2025-04-14"
|
|
211
238
|
GPT_4O = "openai/gpt-4o"
|
|
212
239
|
O4_MINI = "openai/o4-mini"
|
|
213
|
-
|
|
240
|
+
|
|
214
241
|
# Anthropic models via TEE
|
|
215
242
|
CLAUDE_3_7_SONNET = "anthropic/claude-3.7-sonnet"
|
|
216
243
|
CLAUDE_3_5_HAIKU = "anthropic/claude-3.5-haiku"
|
|
217
244
|
CLAUDE_4_0_SONNET = "anthropic/claude-4.0-sonnet"
|
|
218
|
-
|
|
245
|
+
|
|
219
246
|
# Google models via TEE
|
|
220
247
|
GEMINI_2_5_FLASH = "google/gemini-2.5-flash"
|
|
221
248
|
GEMINI_2_5_PRO = "google/gemini-2.5-pro"
|
|
222
249
|
GEMINI_2_0_FLASH = "google/gemini-2.0-flash"
|
|
223
250
|
GEMINI_2_5_FLASH_LITE = "google/gemini-2.5-flash-lite"
|
|
224
|
-
|
|
251
|
+
|
|
225
252
|
# xAI Grok models via TEE
|
|
226
253
|
GROK_3_MINI_BETA = "x-ai/grok-3-mini-beta"
|
|
227
254
|
GROK_3_BETA = "x-ai/grok-3-beta"
|
|
@@ -230,28 +257,29 @@ class LLM(str, Enum):
|
|
|
230
257
|
GROK_4_1_FAST = "x-ai/grok-4.1-fast"
|
|
231
258
|
GROK_4_1_FAST_NON_REASONING = "x-ai/grok-4-1-fast-non-reasoning"
|
|
232
259
|
|
|
260
|
+
|
|
233
261
|
class TEE_LLM(str, Enum):
|
|
234
262
|
"""Enum for LLM models available for TEE execution"""
|
|
235
|
-
|
|
263
|
+
|
|
236
264
|
# Existing (Currently turned off)
|
|
237
265
|
# META_LLAMA_3_1_70B_INSTRUCT = "meta-llama/Llama-3.1-70B-Instruct"
|
|
238
|
-
|
|
266
|
+
|
|
239
267
|
# OpenAI models via TEE
|
|
240
268
|
GPT_4_1_2025_04_14 = "openai/gpt-4.1-2025-04-14"
|
|
241
269
|
GPT_4O = "openai/gpt-4o"
|
|
242
270
|
O4_MINI = "openai/o4-mini"
|
|
243
|
-
|
|
271
|
+
|
|
244
272
|
# Anthropic models via TEE
|
|
245
273
|
CLAUDE_3_7_SONNET = "anthropic/claude-3.7-sonnet"
|
|
246
274
|
CLAUDE_3_5_HAIKU = "anthropic/claude-3.5-haiku"
|
|
247
275
|
CLAUDE_4_0_SONNET = "anthropic/claude-4.0-sonnet"
|
|
248
|
-
|
|
276
|
+
|
|
249
277
|
# Google models via TEE
|
|
250
278
|
GEMINI_2_5_FLASH = "google/gemini-2.5-flash"
|
|
251
279
|
GEMINI_2_5_PRO = "google/gemini-2.5-pro"
|
|
252
280
|
GEMINI_2_0_FLASH = "google/gemini-2.0-flash"
|
|
253
281
|
GEMINI_2_5_FLASH_LITE = "google/gemini-2.5-flash-lite"
|
|
254
|
-
|
|
282
|
+
|
|
255
283
|
# xAI Grok models via TEE
|
|
256
284
|
GROK_3_MINI_BETA = "x-ai/grok-3-mini-beta"
|
|
257
285
|
GROK_3_BETA = "x-ai/grok-3-beta"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opengradient
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.8
|
|
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
|
|
@@ -24,7 +24,6 @@ Requires-Dist: langchain>=0.3.7
|
|
|
24
24
|
Requires-Dist: openai>=1.58.1
|
|
25
25
|
Requires-Dist: pydantic>=2.9.2
|
|
26
26
|
Requires-Dist: og-test-x402==0.0.1
|
|
27
|
-
Requires-Dist: x402==0.2.1
|
|
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,10 +1,10 @@
|
|
|
1
|
-
opengradient/__init__.py,sha256=
|
|
1
|
+
opengradient/__init__.py,sha256=7UkGoQRDtSb0lh3vobxmyJct_uFfm1Re_oz5s0s9dOs,13263
|
|
2
2
|
opengradient/account.py,sha256=5wrYpws_1lozjOFjLCTHtxgoxK-LmObDAaVy9eDcJY4,1145
|
|
3
|
-
opengradient/cli.py,sha256=
|
|
4
|
-
opengradient/client.py,sha256=
|
|
5
|
-
opengradient/defaults.py,sha256=
|
|
3
|
+
opengradient/cli.py,sha256=4IUKxecZV9la-_nEVxObOIjm6qQ9aEHhq5-m5clzzHc,29901
|
|
4
|
+
opengradient/client.py,sha256=nozp80z8KSYQewKQmSVXZQIdVtsSjv53reS3TBRwlXc,63071
|
|
5
|
+
opengradient/defaults.py,sha256=yiZnpIOLyHEmZhCEQXgWpT2eJin10UVsivJY6r61xmo,674
|
|
6
6
|
opengradient/exceptions.py,sha256=88tfegboGtlehQcwhxsl6ZzhLJWZWlkf_bkHTiCtXpo,3391
|
|
7
|
-
opengradient/types.py,sha256=
|
|
7
|
+
opengradient/types.py,sha256=DSkJAcD4fRQ78bG3Ny5-_OqcfptFSIpliS4qKKYE2jU,9026
|
|
8
8
|
opengradient/utils.py,sha256=ZUq4OBIml2vsC0tRqus4Zwb_e3g4woo00apByrafuVw,8058
|
|
9
9
|
opengradient/abi/InferencePrecompile.abi,sha256=reepTHg6Q01UrFP0Gexc-JayplsvOLPfG7jrEZ-cV28,10197
|
|
10
10
|
opengradient/abi/PriceHistoryInference.abi,sha256=ZB3fZdx1kaFlp2wt1vTbTZZG1k8HPvmNtkG5Q8Bnajw,5098
|
|
@@ -16,7 +16,7 @@ opengradient/alphasense/run_model_tool.py,sha256=wlDqXVHa1xpqQy_hmht_wWegxtqdYgY
|
|
|
16
16
|
opengradient/alphasense/types.py,sha256=uxk4JQKbaS2cM3ZiKpdHQb234OJ5ylprNR5vi01QFzA,220
|
|
17
17
|
opengradient/bin/PriceHistoryInference.bin,sha256=nU2FZpGHIKBZ7NSK9Sr-p9lr-nXja_40ISPN9yckDq8,41276
|
|
18
18
|
opengradient/llm/__init__.py,sha256=eYFBrOf1GZr0VGbIw-gSFr8hM3Rbw74ye8l-pnBPNuA,1104
|
|
19
|
-
opengradient/llm/og_langchain.py,sha256=
|
|
19
|
+
opengradient/llm/og_langchain.py,sha256=fVHEq_hJbWrLLVZXKSH5wwSG5kQEt_PGnmAOLUnEgmw,4965
|
|
20
20
|
opengradient/llm/og_openai.py,sha256=26W_NDnLaICIaWbi9aou40v5ZJXLlmLdztDrdFoDGAU,3789
|
|
21
21
|
opengradient/proto/__init__.py,sha256=AhaSmrqV0TXGzCKaoPV8-XUvqs2fGAJBM2aOmDpkNbE,55
|
|
22
22
|
opengradient/proto/infer.proto,sha256=13eaEMcppxkBF8yChptsX9HooWFwJKze7oLZNl-LEb8,1217
|
|
@@ -27,9 +27,9 @@ opengradient/workflow_models/constants.py,sha256=viIkb_LGcfVprqQNaA80gBTj6cfYam0
|
|
|
27
27
|
opengradient/workflow_models/types.py,sha256=Z22hF6c8Y4D2GlzVEIBODGwsqSjSrQvUcpZ7R-mIJdI,409
|
|
28
28
|
opengradient/workflow_models/utils.py,sha256=ySfpuiOBqLTlfto6ZxZf2vc7K6RGIja0l4eaVm5AOzY,1503
|
|
29
29
|
opengradient/workflow_models/workflow_models.py,sha256=d4C_gs39DAfy4cdY9Ee6GMXpPfzwvKFpmxzK1A7LNgU,3900
|
|
30
|
-
opengradient-0.5.
|
|
31
|
-
opengradient-0.5.
|
|
32
|
-
opengradient-0.5.
|
|
33
|
-
opengradient-0.5.
|
|
34
|
-
opengradient-0.5.
|
|
35
|
-
opengradient-0.5.
|
|
30
|
+
opengradient-0.5.8.dist-info/licenses/LICENSE,sha256=xEcvQ3AxZOtDkrqkys2Mm6Y9diEnaSeQRKvxi-JGnNA,1069
|
|
31
|
+
opengradient-0.5.8.dist-info/METADATA,sha256=DyqayJvXV39OUn5H9jUqVsKqX9ilHMAaQ2-u4GnqIwM,4215
|
|
32
|
+
opengradient-0.5.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
33
|
+
opengradient-0.5.8.dist-info/entry_points.txt,sha256=yUKTaJx8RXnybkob0J62wVBiCp_1agVbgw9uzsmaeJc,54
|
|
34
|
+
opengradient-0.5.8.dist-info/top_level.txt,sha256=oC1zimVLa2Yi1LQz8c7x-0IQm92milb5ax8gHBHwDqU,13
|
|
35
|
+
opengradient-0.5.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|