opengradient 0.4.14__tar.gz → 0.5.0a1__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.
- {opengradient-0.4.14/src/opengradient.egg-info → opengradient-0.5.0a1}/PKG-INFO +3 -25
- {opengradient-0.4.14 → opengradient-0.5.0a1}/pyproject.toml +5 -6
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/cli.py +158 -41
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/client.py +262 -67
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/defaults.py +2 -1
- {opengradient-0.4.14 → opengradient-0.5.0a1/src/opengradient.egg-info}/PKG-INFO +3 -25
- {opengradient-0.4.14 → opengradient-0.5.0a1}/LICENSE +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/README.md +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/setup.cfg +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/__init__.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/abi/InferencePrecompile.abi +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/abi/PriceHistoryInference.abi +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/abi/WorkflowScheduler.abi +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/abi/inference.abi +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/account.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/alphasense/__init__.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/alphasense/read_workflow_tool.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/alphasense/run_model_tool.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/alphasense/types.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/bin/PriceHistoryInference.bin +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/exceptions.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/llm/__init__.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/llm/og_langchain.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/llm/og_openai.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/proto/__init__.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/proto/infer.proto +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/proto/infer_pb2.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/proto/infer_pb2_grpc.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/types.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/utils.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/workflow_models/__init__.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/workflow_models/constants.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/workflow_models/types.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/workflow_models/utils.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/workflow_models/workflow_models.py +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient.egg-info/SOURCES.txt +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient.egg-info/dependency_links.txt +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient.egg-info/entry_points.txt +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient.egg-info/requires.txt +0 -0
- {opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient.egg-info/top_level.txt +0 -0
|
@@ -1,34 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opengradient
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0a1
|
|
4
4
|
Summary: Python SDK for OpenGradient decentralized model management & inference services
|
|
5
|
-
Author-email: OpenGradient <
|
|
6
|
-
License: MIT
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2024 OpenGradient
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
5
|
+
Author-email: OpenGradient <kyle@vannalabs.ai>
|
|
6
|
+
License-Expression: MIT
|
|
28
7
|
Project-URL: Homepage, https://opengradient.ai
|
|
29
8
|
Classifier: Development Status :: 3 - Alpha
|
|
30
9
|
Classifier: Intended Audience :: Developers
|
|
31
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
32
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
33
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
34
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=77.0.0"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opengradient"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0a1"
|
|
8
8
|
description = "Python SDK for OpenGradient decentralized model management & inference services"
|
|
9
|
-
authors = [{name = "OpenGradient", email = "
|
|
10
|
-
license = {file = "LICENSE"}
|
|
9
|
+
authors = [{name = "OpenGradient", email = "kyle@vannalabs.ai"}]
|
|
11
10
|
readme = "README.md"
|
|
12
11
|
requires-python = ">=3.10"
|
|
12
|
+
license = "MIT"
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Development Status :: 3 - Alpha",
|
|
15
15
|
"Intended Audience :: Developers",
|
|
16
|
-
"License :: OSI Approved :: MIT License",
|
|
17
16
|
"Programming Language :: Python :: 3.10",
|
|
18
17
|
"Programming Language :: Python :: 3.11",
|
|
19
18
|
"Programming Language :: Python :: 3.12",
|
|
@@ -47,7 +46,7 @@ include-package-data = true
|
|
|
47
46
|
|
|
48
47
|
[tool.setuptools.packages.find]
|
|
49
48
|
where = ["src"]
|
|
50
|
-
include = ["opengradient*"]
|
|
49
|
+
include = ["opengradient*"]
|
|
51
50
|
exclude = ["tests*", "stresstest*"]
|
|
52
51
|
|
|
53
52
|
[tool.setuptools.package-data]
|
|
@@ -18,6 +18,7 @@ from .defaults import (
|
|
|
18
18
|
DEFAULT_OG_FAUCET_URL,
|
|
19
19
|
DEFAULT_RPC_URL,
|
|
20
20
|
DEFAULT_API_URL,
|
|
21
|
+
DEFAULT_LLM_SERVER_URL,
|
|
21
22
|
)
|
|
22
23
|
from .types import InferenceMode, LlmInferenceMode, LLM, TEE_LLM
|
|
23
24
|
|
|
@@ -119,17 +120,21 @@ def cli(ctx):
|
|
|
119
120
|
|
|
120
121
|
Visit https://docs.opengradient.ai/developers/python_sdk/ for more documentation.
|
|
121
122
|
"""
|
|
122
|
-
# Load existing config
|
|
123
123
|
ctx.obj = load_og_config()
|
|
124
124
|
|
|
125
125
|
no_client_commands = ["config", "create-account", "version"]
|
|
126
126
|
|
|
127
|
-
# Only create client if this is not a config management command
|
|
128
127
|
if ctx.invoked_subcommand in no_client_commands:
|
|
129
128
|
return
|
|
130
129
|
|
|
131
130
|
if all(key in ctx.obj for key in ["private_key"]):
|
|
132
131
|
try:
|
|
132
|
+
# Extract API keys from config
|
|
133
|
+
llm_server_url = ctx.obj.get("llm_server_url", DEFAULT_LLM_SERVER_URL)
|
|
134
|
+
openai_api_key = ctx.obj.get("openai_api_key")
|
|
135
|
+
anthropic_api_key = ctx.obj.get("anthropic_api_key")
|
|
136
|
+
google_api_key = ctx.obj.get("google_api_key")
|
|
137
|
+
|
|
133
138
|
ctx.obj["client"] = Client(
|
|
134
139
|
private_key=ctx.obj["private_key"],
|
|
135
140
|
rpc_url=DEFAULT_RPC_URL,
|
|
@@ -137,6 +142,10 @@ def cli(ctx):
|
|
|
137
142
|
contract_address=DEFAULT_INFERENCE_CONTRACT_ADDRESS,
|
|
138
143
|
email=ctx.obj.get("email"),
|
|
139
144
|
password=ctx.obj.get("password"),
|
|
145
|
+
llm_server_url=llm_server_url,
|
|
146
|
+
openai_api_key=openai_api_key,
|
|
147
|
+
anthropic_api_key=anthropic_api_key,
|
|
148
|
+
google_api_key=google_api_key,
|
|
140
149
|
)
|
|
141
150
|
except Exception as e:
|
|
142
151
|
click.echo(f"Failed to create OpenGradient client: {str(e)}")
|
|
@@ -197,6 +206,50 @@ def clear(ctx):
|
|
|
197
206
|
click.echo("Config clear cancelled.")
|
|
198
207
|
|
|
199
208
|
|
|
209
|
+
@config.command()
|
|
210
|
+
@click.option("--provider", type=click.Choice(["openai", "anthropic", "google"]), required=True)
|
|
211
|
+
@click.option("--key", required=True, help="API key for the provider")
|
|
212
|
+
@click.pass_context
|
|
213
|
+
def set_api_key(ctx, provider: str, key: str):
|
|
214
|
+
"""
|
|
215
|
+
Set API key for external LLM providers.
|
|
216
|
+
|
|
217
|
+
Example usage:
|
|
218
|
+
|
|
219
|
+
\b
|
|
220
|
+
opengradient config set-api-key --provider openai --key ..
|
|
221
|
+
opengradient config set-api-key --provider anthropic --key ...
|
|
222
|
+
opengradient config set-api-key --provider google --key ...
|
|
223
|
+
"""
|
|
224
|
+
config_key = f"{provider}_api_key"
|
|
225
|
+
ctx.obj[config_key] = key
|
|
226
|
+
save_og_config(ctx)
|
|
227
|
+
|
|
228
|
+
click.secho(f"✅ API key for {provider} has been set", fg="green")
|
|
229
|
+
click.echo("You can now use models from this provider in completion and chat commands.")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@config.command()
|
|
233
|
+
@click.option("--provider", type=click.Choice(["openai", "anthropic", "google"]), required=True)
|
|
234
|
+
@click.pass_context
|
|
235
|
+
def remove_api_key(ctx, provider: str):
|
|
236
|
+
"""
|
|
237
|
+
Remove API key for an external LLM provider.
|
|
238
|
+
|
|
239
|
+
Example usage:
|
|
240
|
+
|
|
241
|
+
\b
|
|
242
|
+
opengradient config remove-api-key --provider openai
|
|
243
|
+
"""
|
|
244
|
+
config_key = f"{provider}_api_key"
|
|
245
|
+
if config_key in ctx.obj:
|
|
246
|
+
del ctx.obj[config_key]
|
|
247
|
+
save_og_config(ctx)
|
|
248
|
+
click.secho(f"✅ API key for {provider} has been removed", fg="green")
|
|
249
|
+
else:
|
|
250
|
+
click.echo(f"No API key found for {provider}")
|
|
251
|
+
|
|
252
|
+
|
|
200
253
|
@cli.command()
|
|
201
254
|
@click.option("--repo", "-r", "--name", "repo_name", required=True, help="Name of the new model repository")
|
|
202
255
|
@click.option("--description", "-d", required=True, help="Description of the model")
|
|
@@ -354,33 +407,55 @@ def infer(ctx, model_cid: str, inference_mode: str, input_data, input_file: Path
|
|
|
354
407
|
"--model",
|
|
355
408
|
"-m",
|
|
356
409
|
"model_cid",
|
|
357
|
-
type=click.Choice([e.value for e in LLM]),
|
|
358
410
|
required=True,
|
|
359
|
-
help="
|
|
411
|
+
help="Model identifier (local model from LLM enum or external model like 'gpt-4o', 'gemini-2.5-flash-lite', etc.)",
|
|
360
412
|
)
|
|
361
413
|
@click.option(
|
|
362
|
-
"--mode",
|
|
414
|
+
"--mode",
|
|
415
|
+
"inference_mode",
|
|
416
|
+
type=click.Choice(LlmInferenceModes.keys()),
|
|
417
|
+
default="VANILLA",
|
|
418
|
+
help="Inference mode (only applies to local models, default: VANILLA)"
|
|
363
419
|
)
|
|
364
420
|
@click.option("--prompt", "-p", required=True, help="Input prompt for the LLM completion")
|
|
365
421
|
@click.option("--max-tokens", type=int, default=100, help="Maximum number of tokens for LLM completion output")
|
|
366
422
|
@click.option("--stop-sequence", multiple=True, help="Stop sequences for LLM")
|
|
367
423
|
@click.option("--temperature", type=float, default=0.0, help="Temperature for LLM inference (0.0 to 1.0)")
|
|
424
|
+
@click.option("--local", is_flag=True, help="Force use of local model even if not in LLM enum")
|
|
368
425
|
@click.pass_context
|
|
369
|
-
def completion(ctx, model_cid: str, inference_mode: str, prompt: str, max_tokens: int, stop_sequence: List[str], temperature: float):
|
|
426
|
+
def completion(ctx, model_cid: str, inference_mode: str, prompt: str, max_tokens: int, stop_sequence: List[str], temperature: float, local: bool):
|
|
370
427
|
"""
|
|
371
|
-
Run completion inference on an LLM model.
|
|
428
|
+
Run completion inference on an LLM model (local or external).
|
|
372
429
|
|
|
373
|
-
This command
|
|
430
|
+
This command supports both local OpenGradient models and external providers
|
|
431
|
+
(OpenAI, Anthropic, Google, etc.). For external models, make sure to set
|
|
432
|
+
the appropriate API key using 'opengradient config set-api-key'.
|
|
374
433
|
|
|
375
434
|
Example usage:
|
|
376
435
|
|
|
377
436
|
\b
|
|
378
|
-
|
|
379
|
-
opengradient completion
|
|
437
|
+
# Local model
|
|
438
|
+
opengradient completion --model meta-llama/Meta-Llama-3-8B-Instruct --prompt "Hello, how are you?" --max-tokens 50
|
|
439
|
+
|
|
440
|
+
# External OpenAI model
|
|
441
|
+
opengradient completion --model gpt-4o --prompt "Translate to French: Hello world" --max-tokens 50
|
|
442
|
+
|
|
443
|
+
# External Anthropic model
|
|
444
|
+
opengradient completion --model claude-haiku-4-5-20251001--prompt "Write a haiku about coding" --max-tokens 100
|
|
445
|
+
|
|
446
|
+
# External Google model
|
|
447
|
+
opengradient completion --model gemini-2.5-flash-lite --prompt "Explain quantum computing" --max-tokens 200
|
|
380
448
|
"""
|
|
381
449
|
client: Client = ctx.obj["client"]
|
|
450
|
+
|
|
382
451
|
try:
|
|
383
|
-
|
|
452
|
+
is_local = local or model_cid in [llm.value for llm in LLM]
|
|
453
|
+
|
|
454
|
+
if is_local:
|
|
455
|
+
click.echo(f'Running LLM completion inference for local model "{model_cid}"\n')
|
|
456
|
+
else:
|
|
457
|
+
click.echo(f'Running LLM completion inference for external model "{model_cid}"\n')
|
|
458
|
+
|
|
384
459
|
completion_output = client.llm_completion(
|
|
385
460
|
model_cid=model_cid,
|
|
386
461
|
inference_mode=LlmInferenceModes[inference_mode],
|
|
@@ -388,23 +463,31 @@ def completion(ctx, model_cid: str, inference_mode: str, prompt: str, max_tokens
|
|
|
388
463
|
max_tokens=max_tokens,
|
|
389
464
|
stop_sequence=list(stop_sequence),
|
|
390
465
|
temperature=temperature,
|
|
466
|
+
local_model=local,
|
|
391
467
|
)
|
|
392
468
|
|
|
393
|
-
print_llm_completion_result(model_cid, completion_output.transaction_hash, completion_output.completion_output)
|
|
469
|
+
print_llm_completion_result(model_cid, completion_output.transaction_hash, completion_output.completion_output, is_local)
|
|
470
|
+
|
|
394
471
|
except Exception as e:
|
|
395
472
|
click.echo(f"Error running LLM completion: {str(e)}")
|
|
396
473
|
|
|
397
474
|
|
|
398
|
-
def print_llm_completion_result(model_cid, tx_hash, llm_output):
|
|
475
|
+
def print_llm_completion_result(model_cid, tx_hash, llm_output, is_local=True):
|
|
399
476
|
click.secho("✅ LLM completion Successful", fg="green", bold=True)
|
|
400
477
|
click.echo("──────────────────────────────────────")
|
|
401
|
-
click.echo("Model
|
|
478
|
+
click.echo("Model: ", nl=False)
|
|
402
479
|
click.secho(model_cid, fg="cyan", bold=True)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
480
|
+
|
|
481
|
+
if is_local and tx_hash != "external":
|
|
482
|
+
click.echo("Transaction hash: ", nl=False)
|
|
483
|
+
click.secho(tx_hash, fg="cyan", bold=True)
|
|
484
|
+
block_explorer_link = f"{DEFAULT_BLOCKCHAIN_EXPLORER}0x{tx_hash}"
|
|
485
|
+
click.echo("Block explorer link: ", nl=False)
|
|
486
|
+
click.secho(block_explorer_link, fg="blue", underline=True)
|
|
487
|
+
else:
|
|
488
|
+
click.echo("Source: ", nl=False)
|
|
489
|
+
click.secho("External Provider", fg="cyan", bold=True)
|
|
490
|
+
|
|
408
491
|
click.echo("──────────────────────────────────────")
|
|
409
492
|
click.secho("LLM Output:", fg="yellow", bold=True)
|
|
410
493
|
click.echo()
|
|
@@ -417,12 +500,15 @@ def print_llm_completion_result(model_cid, tx_hash, llm_output):
|
|
|
417
500
|
"--model",
|
|
418
501
|
"-m",
|
|
419
502
|
"model_cid",
|
|
420
|
-
type=click.Choice([e.value for e in LLM]),
|
|
421
503
|
required=True,
|
|
422
|
-
help="
|
|
504
|
+
help="Model identifier (local model from LLM enum or external model like 'gpt-4o', 'gemini-2.5-flash-lite', etc.)",
|
|
423
505
|
)
|
|
424
506
|
@click.option(
|
|
425
|
-
"--mode",
|
|
507
|
+
"--mode",
|
|
508
|
+
"inference_mode",
|
|
509
|
+
type=click.Choice(LlmInferenceModes.keys()),
|
|
510
|
+
default="VANILLA",
|
|
511
|
+
help="Inference mode (only applies to local models, default: VANILLA)"
|
|
426
512
|
)
|
|
427
513
|
@click.option("--messages", type=str, required=False, help="Input messages for the chat inference in JSON format")
|
|
428
514
|
@click.option(
|
|
@@ -436,9 +522,13 @@ def print_llm_completion_result(model_cid, tx_hash, llm_output):
|
|
|
436
522
|
@click.option("--temperature", type=float, default=0.0, help="Temperature for LLM inference (0.0 to 1.0)")
|
|
437
523
|
@click.option("--tools", type=str, default=None, help="Tool configurations in JSON format")
|
|
438
524
|
@click.option(
|
|
439
|
-
"--tools-file",
|
|
525
|
+
"--tools-file",
|
|
526
|
+
type=click.Path(exists=True, path_type=Path),
|
|
527
|
+
required=False,
|
|
528
|
+
help="Path to JSON file containing tool configurations"
|
|
440
529
|
)
|
|
441
530
|
@click.option("--tool-choice", type=str, default="", help="Specific tool choice for the LLM")
|
|
531
|
+
@click.option("--local", is_flag=True, help="Force use of local model even if not in LLM enum")
|
|
442
532
|
@click.pass_context
|
|
443
533
|
def chat(
|
|
444
534
|
ctx,
|
|
@@ -452,23 +542,37 @@ def chat(
|
|
|
452
542
|
tools: Optional[str],
|
|
453
543
|
tools_file: Optional[Path],
|
|
454
544
|
tool_choice: Optional[str],
|
|
545
|
+
local: bool,
|
|
455
546
|
):
|
|
456
547
|
"""
|
|
457
|
-
Run chat inference on an LLM model.
|
|
548
|
+
Run chat inference on an LLM model (local or external).
|
|
458
549
|
|
|
459
|
-
This command
|
|
460
|
-
|
|
461
|
-
Tool call formatting is based on OpenAI documentation tool calls (see here: https://platform.openai.com/docs/guides/function-calling).
|
|
550
|
+
This command supports both local OpenGradient models and external providers.
|
|
551
|
+
Tool calling is supported for compatible models.
|
|
462
552
|
|
|
463
553
|
Example usage:
|
|
464
554
|
|
|
465
555
|
\b
|
|
466
|
-
|
|
467
|
-
opengradient chat --model
|
|
556
|
+
# Local model
|
|
557
|
+
opengradient chat --model meta-llama/Meta-Llama-3-8B-Instruct --messages '[{"role":"user","content":"hello"}]' --max-tokens 50
|
|
558
|
+
|
|
559
|
+
# External OpenAI model with tools
|
|
560
|
+
opengradient chat --model gpt-4o --messages-file messages.json --tools-file tools.json --max-tokens 200
|
|
561
|
+
|
|
562
|
+
# External Anthropic model
|
|
563
|
+
opengradient chat --model claude-haiku-4-5-20251001 --messages '[{"role":"user","content":"Write a poem"}]' --max-tokens 100
|
|
468
564
|
"""
|
|
469
565
|
client: Client = ctx.obj["client"]
|
|
566
|
+
|
|
470
567
|
try:
|
|
471
|
-
|
|
568
|
+
is_local = local or model_cid in [llm.value for llm in LLM]
|
|
569
|
+
|
|
570
|
+
if is_local:
|
|
571
|
+
click.echo(f'Running LLM chat inference for local model "{model_cid}"\n')
|
|
572
|
+
else:
|
|
573
|
+
click.echo(f'Running LLM chat inference for external model "{model_cid}"\n')
|
|
574
|
+
|
|
575
|
+
# Parse messages
|
|
472
576
|
if not messages and not messages_file:
|
|
473
577
|
click.echo("Must specify either messages or messages-file")
|
|
474
578
|
ctx.exit(1)
|
|
@@ -488,10 +592,10 @@ def chat(
|
|
|
488
592
|
with messages_file.open("r") as file:
|
|
489
593
|
messages = json.load(file)
|
|
490
594
|
|
|
491
|
-
# Parse tools
|
|
595
|
+
# Parse tools
|
|
492
596
|
if (tools and tools != "[]") and tools_file:
|
|
493
597
|
click.echo("Cannot have both tools and tools-file")
|
|
494
|
-
|
|
598
|
+
ctx.exit(1)
|
|
495
599
|
return
|
|
496
600
|
|
|
497
601
|
parsed_tools = []
|
|
@@ -532,23 +636,37 @@ def chat(
|
|
|
532
636
|
temperature=temperature,
|
|
533
637
|
tools=parsed_tools,
|
|
534
638
|
tool_choice=tool_choice,
|
|
639
|
+
local_model=local,
|
|
535
640
|
)
|
|
536
641
|
|
|
537
|
-
print_llm_chat_result(
|
|
642
|
+
print_llm_chat_result(
|
|
643
|
+
model_cid,
|
|
644
|
+
completion_output.transaction_hash,
|
|
645
|
+
completion_output.finish_reason,
|
|
646
|
+
completion_output.chat_output,
|
|
647
|
+
is_local
|
|
648
|
+
)
|
|
649
|
+
|
|
538
650
|
except Exception as e:
|
|
539
651
|
click.echo(f"Error running LLM chat inference: {str(e)}")
|
|
540
652
|
|
|
541
653
|
|
|
542
|
-
def print_llm_chat_result(model_cid, tx_hash, finish_reason, chat_output):
|
|
654
|
+
def print_llm_chat_result(model_cid, tx_hash, finish_reason, chat_output, is_local=True):
|
|
543
655
|
click.secho("✅ LLM Chat Successful", fg="green", bold=True)
|
|
544
656
|
click.echo("──────────────────────────────────────")
|
|
545
|
-
click.echo("Model
|
|
657
|
+
click.echo("Model: ", nl=False)
|
|
546
658
|
click.secho(model_cid, fg="cyan", bold=True)
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
659
|
+
|
|
660
|
+
if is_local and tx_hash != "external":
|
|
661
|
+
click.echo("Transaction hash: ", nl=False)
|
|
662
|
+
click.secho(tx_hash, fg="cyan", bold=True)
|
|
663
|
+
block_explorer_link = f"{DEFAULT_BLOCKCHAIN_EXPLORER}0x{tx_hash}"
|
|
664
|
+
click.echo("Block explorer link: ", nl=False)
|
|
665
|
+
click.secho(block_explorer_link, fg="blue", underline=True)
|
|
666
|
+
else:
|
|
667
|
+
click.echo("Source: ", nl=False)
|
|
668
|
+
click.secho("External Provider", fg="cyan", bold=True)
|
|
669
|
+
|
|
552
670
|
click.echo("──────────────────────────────────────")
|
|
553
671
|
click.secho("Finish Reason: ", fg="yellow", bold=True)
|
|
554
672
|
click.echo()
|
|
@@ -557,7 +675,6 @@ def print_llm_chat_result(model_cid, tx_hash, finish_reason, chat_output):
|
|
|
557
675
|
click.secho("Chat Output:", fg="yellow", bold=True)
|
|
558
676
|
click.echo()
|
|
559
677
|
for key, value in chat_output.items():
|
|
560
|
-
# If the value doesn't give any information, don't print it
|
|
561
678
|
if value != None and value != "" and value != "[]" and value != []:
|
|
562
679
|
click.echo(f"{key}: {value}")
|
|
563
680
|
click.echo()
|
|
@@ -30,7 +30,7 @@ from .types import (
|
|
|
30
30
|
ModelRepository,
|
|
31
31
|
FileUploadResult,
|
|
32
32
|
)
|
|
33
|
-
from .defaults import DEFAULT_IMAGE_GEN_HOST, DEFAULT_IMAGE_GEN_PORT, DEFAULT_SCHEDULER_ADDRESS
|
|
33
|
+
from .defaults import DEFAULT_IMAGE_GEN_HOST, DEFAULT_IMAGE_GEN_PORT, DEFAULT_SCHEDULER_ADDRESS, DEFAULT_LLM_SERVER_URL
|
|
34
34
|
from .utils import convert_array_to_model_output, convert_to_model_input, convert_to_model_output
|
|
35
35
|
|
|
36
36
|
_FIREBASE_CONFIG = {
|
|
@@ -62,7 +62,21 @@ class Client:
|
|
|
62
62
|
_api_url: str
|
|
63
63
|
_inference_abi: Dict
|
|
64
64
|
_precompile_abi: Dict
|
|
65
|
-
|
|
65
|
+
_llm_server_url: str
|
|
66
|
+
_external_api_keys: Dict[str, str]
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
private_key: str,
|
|
70
|
+
rpc_url: str,
|
|
71
|
+
api_url: str,
|
|
72
|
+
contract_address: str,
|
|
73
|
+
email: Optional[str] = None,
|
|
74
|
+
password: Optional[str] = None,
|
|
75
|
+
llm_server_url: Optional[str] = DEFAULT_LLM_SERVER_URL,
|
|
76
|
+
openai_api_key: Optional[str] = None,
|
|
77
|
+
anthropic_api_key: Optional[str] = None,
|
|
78
|
+
google_api_key: Optional[str] = None,
|
|
79
|
+
):
|
|
66
80
|
"""
|
|
67
81
|
Initialize the Client with private key, RPC URL, and contract address.
|
|
68
82
|
|
|
@@ -91,6 +105,70 @@ class Client:
|
|
|
91
105
|
else:
|
|
92
106
|
self._hub_user = None
|
|
93
107
|
|
|
108
|
+
self._llm_server_url = llm_server_url
|
|
109
|
+
|
|
110
|
+
self._external_api_keys = {}
|
|
111
|
+
if openai_api_key or os.getenv("OPENAI_API_KEY"):
|
|
112
|
+
self._external_api_keys["openai"] = openai_api_key or os.getenv("OPENAI_API_KEY")
|
|
113
|
+
if anthropic_api_key or os.getenv("ANTHROPIC_API_KEY"):
|
|
114
|
+
self._external_api_keys["anthropic"] = anthropic_api_key or os.getenv("ANTHROPIC_API_KEY")
|
|
115
|
+
if google_api_key or os.getenv("GOOGLE_API_KEY"):
|
|
116
|
+
self._external_api_keys["google"] = google_api_key or os.getenv("GOOGLE_API_KEY")
|
|
117
|
+
|
|
118
|
+
def set_api_key(self, provider: str, api_key: str):
|
|
119
|
+
"""
|
|
120
|
+
Set or update API key for an external provider.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
provider: Provider name (e.g., 'openai', 'anthropic', 'google')
|
|
124
|
+
api_key: The API key for the provider
|
|
125
|
+
"""
|
|
126
|
+
self._external_api_keys[provider] = api_key
|
|
127
|
+
|
|
128
|
+
def _is_local_model(self, model_cid: str) -> bool:
|
|
129
|
+
"""
|
|
130
|
+
Check if a model is hosted locally on OpenGradient.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
model_cid: Model identifier
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
True if model is local, False if it should use external provider
|
|
137
|
+
"""
|
|
138
|
+
# Check if it's in our local LLM enum
|
|
139
|
+
try:
|
|
140
|
+
return model_cid in [llm.value for llm in LLM]
|
|
141
|
+
except:
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
def _get_provider_from_model(self, model: str) -> str:
|
|
145
|
+
"""Infer provider from model name."""
|
|
146
|
+
model_lower = model.lower()
|
|
147
|
+
|
|
148
|
+
if "gpt" in model_lower or model.startswith("openai/"):
|
|
149
|
+
return "openai"
|
|
150
|
+
elif "claude" in model_lower or model.startswith("anthropic/"):
|
|
151
|
+
return "anthropic"
|
|
152
|
+
elif "gemini" in model_lower or "palm" in model_lower or model.startswith("google/"):
|
|
153
|
+
return "google"
|
|
154
|
+
elif "command" in model_lower or model.startswith("cohere/"):
|
|
155
|
+
return "cohere"
|
|
156
|
+
else:
|
|
157
|
+
return "openai"
|
|
158
|
+
|
|
159
|
+
def _get_api_key_for_model(self, model: str) -> Optional[str]:
|
|
160
|
+
"""
|
|
161
|
+
Get the appropriate API key for a model.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
model: Model identifier
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
API key string or None
|
|
168
|
+
"""
|
|
169
|
+
provider = self._get_provider_from_model(model)
|
|
170
|
+
return self._external_api_keys.get(provider)
|
|
171
|
+
|
|
94
172
|
def _login_to_hub(self, email, password):
|
|
95
173
|
try:
|
|
96
174
|
firebase_app = firebase.initialize_app(_FIREBASE_CONFIG)
|
|
@@ -328,36 +406,48 @@ class Client:
|
|
|
328
406
|
|
|
329
407
|
def llm_completion(
|
|
330
408
|
self,
|
|
331
|
-
model_cid: LLM
|
|
332
|
-
inference_mode: LlmInferenceMode,
|
|
409
|
+
model_cid: str, # Changed from LLM to str to accept any model
|
|
333
410
|
prompt: str,
|
|
334
411
|
max_tokens: int = 100,
|
|
335
412
|
stop_sequence: Optional[List[str]] = None,
|
|
336
413
|
temperature: float = 0.0,
|
|
414
|
+
inference_mode: LlmInferenceMode = LlmInferenceMode.VANILLA,
|
|
337
415
|
max_retries: Optional[int] = None,
|
|
416
|
+
local_model: Optional[bool] = False,
|
|
338
417
|
) -> TextGenerationOutput:
|
|
339
418
|
"""
|
|
340
419
|
Perform inference on an LLM model using completions.
|
|
341
420
|
|
|
342
421
|
Args:
|
|
343
|
-
model_cid (
|
|
344
|
-
inference_mode (
|
|
422
|
+
model_cid (str): The unique content identifier for the model.
|
|
423
|
+
inference_mode (LlmInferenceMode): The inference mode (only used for local models).
|
|
345
424
|
prompt (str): The input prompt for the LLM.
|
|
346
425
|
max_tokens (int): Maximum number of tokens for LLM output. Default is 100.
|
|
347
426
|
stop_sequence (List[str], optional): List of stop sequences for LLM. Default is None.
|
|
348
427
|
temperature (float): Temperature for LLM inference, between 0 and 1. Default is 0.0.
|
|
428
|
+
max_retries (int, optional): Maximum number of retry attempts for blockchain transactions.
|
|
429
|
+
local_model (bool, optional): Force use of local model even if not in LLM enum.
|
|
349
430
|
|
|
350
431
|
Returns:
|
|
351
432
|
TextGenerationOutput: Generated text results including:
|
|
352
|
-
- Transaction hash
|
|
433
|
+
- Transaction hash (or "external" for external providers)
|
|
353
434
|
- String of completion output
|
|
354
435
|
|
|
355
436
|
Raises:
|
|
356
437
|
OpenGradientError: If the inference fails.
|
|
357
438
|
"""
|
|
358
|
-
|
|
439
|
+
# Check if this is a local model or external
|
|
440
|
+
if not local_model and not self._is_local_model(model_cid):
|
|
441
|
+
return self._external_llm_completion(
|
|
442
|
+
model=model_cid,
|
|
443
|
+
prompt=prompt,
|
|
444
|
+
max_tokens=max_tokens,
|
|
445
|
+
stop_sequence=stop_sequence,
|
|
446
|
+
temperature=temperature,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# Original local model logic
|
|
359
450
|
def execute_transaction():
|
|
360
|
-
# Check inference mode and supported model
|
|
361
451
|
if inference_mode != LlmInferenceMode.VANILLA and inference_mode != LlmInferenceMode.TEE:
|
|
362
452
|
raise OpenGradientError("Invalid inference mode %s: Inference mode must be VANILLA or TEE" % inference_mode)
|
|
363
453
|
|
|
@@ -366,14 +456,13 @@ class Client:
|
|
|
366
456
|
|
|
367
457
|
contract = self._blockchain.eth.contract(address=self._inference_hub_contract_address, abi=self._inference_abi)
|
|
368
458
|
|
|
369
|
-
# Prepare LLM input
|
|
370
459
|
llm_request = {
|
|
371
460
|
"mode": inference_mode.value,
|
|
372
461
|
"modelCID": model_cid,
|
|
373
462
|
"prompt": prompt,
|
|
374
463
|
"max_tokens": max_tokens,
|
|
375
464
|
"stop_sequence": stop_sequence or [],
|
|
376
|
-
"temperature": int(temperature * 100),
|
|
465
|
+
"temperature": int(temperature * 100),
|
|
377
466
|
}
|
|
378
467
|
logging.debug(f"Prepared LLM request: {llm_request}")
|
|
379
468
|
|
|
@@ -390,80 +479,117 @@ class Client:
|
|
|
390
479
|
|
|
391
480
|
return run_with_retry(execute_transaction, max_retries)
|
|
392
481
|
|
|
482
|
+
def _external_llm_completion(
|
|
483
|
+
self,
|
|
484
|
+
model: str,
|
|
485
|
+
prompt: str,
|
|
486
|
+
max_tokens: int = 100,
|
|
487
|
+
stop_sequence: Optional[List[str]] = None,
|
|
488
|
+
temperature: float = 0.0,
|
|
489
|
+
) -> TextGenerationOutput:
|
|
490
|
+
"""
|
|
491
|
+
Route completion request to external LLM server.
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
model: Model identifier
|
|
495
|
+
prompt: Input prompt
|
|
496
|
+
max_tokens: Maximum tokens to generate
|
|
497
|
+
stop_sequence: Stop sequences
|
|
498
|
+
temperature: Sampling temperature
|
|
499
|
+
|
|
500
|
+
Returns:
|
|
501
|
+
TextGenerationOutput with completion
|
|
502
|
+
|
|
503
|
+
Raises:
|
|
504
|
+
OpenGradientError: If request fails
|
|
505
|
+
"""
|
|
506
|
+
url = f"{self._llm_server_url}/v1/completions"
|
|
507
|
+
|
|
508
|
+
headers = {"Content-Type": "application/json"}
|
|
509
|
+
api_key = self._get_api_key_for_model(model)
|
|
510
|
+
if api_key:
|
|
511
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
512
|
+
|
|
513
|
+
payload = {
|
|
514
|
+
"model": model,
|
|
515
|
+
"prompt": prompt,
|
|
516
|
+
"max_tokens": max_tokens,
|
|
517
|
+
"temperature": temperature,
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if stop_sequence:
|
|
521
|
+
payload["stop"] = stop_sequence
|
|
522
|
+
|
|
523
|
+
try:
|
|
524
|
+
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
525
|
+
response.raise_for_status()
|
|
526
|
+
|
|
527
|
+
result = response.json()
|
|
528
|
+
|
|
529
|
+
return TextGenerationOutput(
|
|
530
|
+
transaction_hash="external", # No blockchain transaction for external
|
|
531
|
+
completion_output=result["completion"]
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
except requests.RequestException as e:
|
|
535
|
+
error_msg = f"External LLM completion failed: {str(e)}"
|
|
536
|
+
if hasattr(e, 'response') and e.response is not None:
|
|
537
|
+
try:
|
|
538
|
+
error_detail = e.response.json()
|
|
539
|
+
error_msg += f" - {error_detail}"
|
|
540
|
+
except:
|
|
541
|
+
error_msg += f" - {e.response.text}"
|
|
542
|
+
logging.error(error_msg)
|
|
543
|
+
raise OpenGradientError(error_msg)
|
|
544
|
+
|
|
393
545
|
def llm_chat(
|
|
394
546
|
self,
|
|
395
|
-
model_cid: LLM
|
|
396
|
-
inference_mode: LlmInferenceMode,
|
|
547
|
+
model_cid: str, # Changed from LLM to str
|
|
397
548
|
messages: List[Dict],
|
|
549
|
+
inference_mode: LlmInferenceMode = LlmInferenceMode.VANILLA,
|
|
398
550
|
max_tokens: int = 100,
|
|
399
551
|
stop_sequence: Optional[List[str]] = None,
|
|
400
552
|
temperature: float = 0.0,
|
|
401
553
|
tools: Optional[List[Dict]] = [],
|
|
402
554
|
tool_choice: Optional[str] = None,
|
|
403
555
|
max_retries: Optional[int] = None,
|
|
556
|
+
local_model: Optional[bool] = False,
|
|
404
557
|
) -> TextGenerationOutput:
|
|
405
558
|
"""
|
|
406
559
|
Perform inference on an LLM model using chat.
|
|
407
560
|
|
|
408
561
|
Args:
|
|
409
|
-
model_cid (
|
|
410
|
-
inference_mode (
|
|
411
|
-
messages (
|
|
412
|
-
This should be in OpenAI API format (https://platform.openai.com/docs/api-reference/chat/create)
|
|
413
|
-
Example:
|
|
414
|
-
[
|
|
415
|
-
{
|
|
416
|
-
"role": "system",
|
|
417
|
-
"content": "You are a helpful assistant."
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
"role": "user",
|
|
421
|
-
"content": "Hello!"
|
|
422
|
-
}
|
|
423
|
-
]
|
|
562
|
+
model_cid (str): The unique content identifier for the model.
|
|
563
|
+
inference_mode (LlmInferenceMode): The inference mode (only used for local models).
|
|
564
|
+
messages (List[Dict]): The messages that will be passed into the chat.
|
|
424
565
|
max_tokens (int): Maximum number of tokens for LLM output. Default is 100.
|
|
425
|
-
stop_sequence (List[str], optional): List of stop sequences for LLM.
|
|
426
|
-
temperature (float): Temperature for LLM inference, between 0 and 1.
|
|
427
|
-
tools (List[dict], optional): Set of tools
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
{
|
|
432
|
-
"type": "function",
|
|
433
|
-
"function": {
|
|
434
|
-
"name": "get_current_weather",
|
|
435
|
-
"description": "Get the current weather in a given location",
|
|
436
|
-
"parameters": {
|
|
437
|
-
"type": "object",
|
|
438
|
-
"properties": {
|
|
439
|
-
"location": {
|
|
440
|
-
"type": "string",
|
|
441
|
-
"description": "The city and state, e.g. San Francisco, CA"
|
|
442
|
-
},
|
|
443
|
-
"unit": {
|
|
444
|
-
"type": "string",
|
|
445
|
-
"enum": ["celsius", "fahrenheit"]
|
|
446
|
-
}
|
|
447
|
-
},
|
|
448
|
-
"required": ["location"]
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
]
|
|
453
|
-
tool_choice (str, optional): Sets a specific tool to choose. Default value is "auto".
|
|
566
|
+
stop_sequence (List[str], optional): List of stop sequences for LLM.
|
|
567
|
+
temperature (float): Temperature for LLM inference, between 0 and 1.
|
|
568
|
+
tools (List[dict], optional): Set of tools for function calling.
|
|
569
|
+
tool_choice (str, optional): Sets a specific tool to choose.
|
|
570
|
+
max_retries (int, optional): Maximum number of retry attempts.
|
|
571
|
+
local_model (bool, optional): Force use of local model.
|
|
454
572
|
|
|
455
573
|
Returns:
|
|
456
|
-
TextGenerationOutput: Generated text results
|
|
457
|
-
- Transaction hash
|
|
458
|
-
- Finish reason (tool_call, stop, etc.)
|
|
459
|
-
- Dictionary of chat message output (role, content, tool_call, etc.)
|
|
574
|
+
TextGenerationOutput: Generated text results.
|
|
460
575
|
|
|
461
576
|
Raises:
|
|
462
577
|
OpenGradientError: If the inference fails.
|
|
463
578
|
"""
|
|
464
|
-
|
|
579
|
+
# Check if this is a local model or external
|
|
580
|
+
if not local_model and not self._is_local_model(model_cid):
|
|
581
|
+
return self._external_llm_chat(
|
|
582
|
+
model=model_cid,
|
|
583
|
+
messages=messages,
|
|
584
|
+
max_tokens=max_tokens,
|
|
585
|
+
stop_sequence=stop_sequence,
|
|
586
|
+
temperature=temperature,
|
|
587
|
+
tools=tools,
|
|
588
|
+
tool_choice=tool_choice,
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
# Original local model logic
|
|
465
592
|
def execute_transaction():
|
|
466
|
-
# Check inference mode and supported model
|
|
467
593
|
if inference_mode != LlmInferenceMode.VANILLA and inference_mode != LlmInferenceMode.TEE:
|
|
468
594
|
raise OpenGradientError("Invalid inference mode %s: Inference mode must be VANILLA or TEE" % inference_mode)
|
|
469
595
|
|
|
@@ -472,7 +598,6 @@ class Client:
|
|
|
472
598
|
|
|
473
599
|
contract = self._blockchain.eth.contract(address=self._inference_hub_contract_address, abi=self._inference_abi)
|
|
474
600
|
|
|
475
|
-
# For incoming chat messages, tool_calls can be empty. Add an empty array so that it will fit the ABI.
|
|
476
601
|
for message in messages:
|
|
477
602
|
if "tool_calls" not in message:
|
|
478
603
|
message["tool_calls"] = []
|
|
@@ -481,7 +606,6 @@ class Client:
|
|
|
481
606
|
if "name" not in message:
|
|
482
607
|
message["name"] = ""
|
|
483
608
|
|
|
484
|
-
# Create simplified tool structure for smart contract
|
|
485
609
|
converted_tools = []
|
|
486
610
|
if tools is not None:
|
|
487
611
|
for tool in tools:
|
|
@@ -496,14 +620,13 @@ class Client:
|
|
|
496
620
|
raise OpenGradientError("Chat LLM failed to convert parameters into JSON: %s", e)
|
|
497
621
|
converted_tools.append(converted_tool)
|
|
498
622
|
|
|
499
|
-
# Prepare LLM input
|
|
500
623
|
llm_request = {
|
|
501
624
|
"mode": inference_mode.value,
|
|
502
625
|
"modelCID": model_cid,
|
|
503
626
|
"messages": messages,
|
|
504
627
|
"max_tokens": max_tokens,
|
|
505
628
|
"stop_sequence": stop_sequence or [],
|
|
506
|
-
"temperature": int(temperature * 100),
|
|
629
|
+
"temperature": int(temperature * 100),
|
|
507
630
|
"tools": converted_tools or [],
|
|
508
631
|
"tool_choice": tool_choice if tool_choice else ("" if tools is None else "auto"),
|
|
509
632
|
}
|
|
@@ -529,6 +652,78 @@ class Client:
|
|
|
529
652
|
|
|
530
653
|
return run_with_retry(execute_transaction, max_retries)
|
|
531
654
|
|
|
655
|
+
def _external_llm_chat(
|
|
656
|
+
self,
|
|
657
|
+
model: str,
|
|
658
|
+
messages: List[Dict],
|
|
659
|
+
max_tokens: int = 100,
|
|
660
|
+
stop_sequence: Optional[List[str]] = None,
|
|
661
|
+
temperature: float = 0.0,
|
|
662
|
+
tools: Optional[List[Dict]] = None,
|
|
663
|
+
tool_choice: Optional[str] = None,
|
|
664
|
+
) -> TextGenerationOutput:
|
|
665
|
+
"""
|
|
666
|
+
Route chat request to external LLM server.
|
|
667
|
+
|
|
668
|
+
Args:
|
|
669
|
+
model: Model identifier
|
|
670
|
+
messages: List of chat messages
|
|
671
|
+
max_tokens: Maximum tokens to generate
|
|
672
|
+
stop_sequence: Stop sequences
|
|
673
|
+
temperature: Sampling temperature
|
|
674
|
+
tools: Function calling tools
|
|
675
|
+
tool_choice: Tool selection strategy
|
|
676
|
+
|
|
677
|
+
Returns:
|
|
678
|
+
TextGenerationOutput with chat completion
|
|
679
|
+
|
|
680
|
+
Raises:
|
|
681
|
+
OpenGradientError: If request fails
|
|
682
|
+
"""
|
|
683
|
+
url = f"{self._llm_server_url}/v1/chat/completions"
|
|
684
|
+
|
|
685
|
+
headers = {"Content-Type": "application/json"}
|
|
686
|
+
api_key = self._get_api_key_for_model(model)
|
|
687
|
+
if api_key:
|
|
688
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
689
|
+
|
|
690
|
+
payload = {
|
|
691
|
+
"model": model,
|
|
692
|
+
"messages": messages,
|
|
693
|
+
"max_tokens": max_tokens,
|
|
694
|
+
"temperature": temperature,
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if stop_sequence:
|
|
698
|
+
payload["stop"] = stop_sequence
|
|
699
|
+
|
|
700
|
+
if tools:
|
|
701
|
+
payload["tools"] = tools
|
|
702
|
+
payload["tool_choice"] = tool_choice or "auto"
|
|
703
|
+
|
|
704
|
+
try:
|
|
705
|
+
response = requests.post(url, json=payload, headers=headers, timeout=60)
|
|
706
|
+
response.raise_for_status()
|
|
707
|
+
|
|
708
|
+
result = response.json()
|
|
709
|
+
|
|
710
|
+
return TextGenerationOutput(
|
|
711
|
+
transaction_hash="external", # No blockchain transaction for external
|
|
712
|
+
finish_reason=result["finish_reason"],
|
|
713
|
+
chat_output=result["message"]
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
except requests.RequestException as e:
|
|
717
|
+
error_msg = f"External LLM chat failed: {str(e)}"
|
|
718
|
+
if hasattr(e, 'response') and e.response is not None:
|
|
719
|
+
try:
|
|
720
|
+
error_detail = e.response.json()
|
|
721
|
+
error_msg += f" - {error_detail}"
|
|
722
|
+
except:
|
|
723
|
+
error_msg += f" - {e.response.text}"
|
|
724
|
+
logging.error(error_msg)
|
|
725
|
+
raise OpenGradientError(error_msg)
|
|
726
|
+
|
|
532
727
|
def list_files(self, model_name: str, version: str) -> List[Dict]:
|
|
533
728
|
"""
|
|
534
729
|
List files for a specific version of a model.
|
|
@@ -7,4 +7,5 @@ DEFAULT_INFERENCE_CONTRACT_ADDRESS = "0x8383C9bD7462F12Eb996DD02F78234C0421A6FaE
|
|
|
7
7
|
DEFAULT_SCHEDULER_ADDRESS = "0x7179724De4e7FF9271FA40C0337c7f90C0508eF6"
|
|
8
8
|
DEFAULT_BLOCKCHAIN_EXPLORER = "https://explorer.opengradient.ai/tx/"
|
|
9
9
|
DEFAULT_IMAGE_GEN_HOST = "18.217.25.69"
|
|
10
|
-
DEFAULT_IMAGE_GEN_PORT = 5125
|
|
10
|
+
DEFAULT_IMAGE_GEN_PORT = 5125
|
|
11
|
+
DEFAULT_LLM_SERVER_URL = "http://35.225.197.84:8000"
|
|
@@ -1,34 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opengradient
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0a1
|
|
4
4
|
Summary: Python SDK for OpenGradient decentralized model management & inference services
|
|
5
|
-
Author-email: OpenGradient <
|
|
6
|
-
License: MIT
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2024 OpenGradient
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
5
|
+
Author-email: OpenGradient <kyle@vannalabs.ai>
|
|
6
|
+
License-Expression: MIT
|
|
28
7
|
Project-URL: Homepage, https://opengradient.ai
|
|
29
8
|
Classifier: Development Status :: 3 - Alpha
|
|
30
9
|
Classifier: Intended Audience :: Developers
|
|
31
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
32
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
33
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
34
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/alphasense/read_workflow_tool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengradient-0.4.14 → opengradient-0.5.0a1}/src/opengradient/workflow_models/workflow_models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|