Skip to content

Commit

Permalink
Merge pull request #188 from runpod/1.3.1-changelog
Browse files Browse the repository at this point in the history
1.3.1 changelog
  • Loading branch information
justinmerrell authored Oct 29, 2023
2 parents 08ffdcf + d5739cf commit 8162556
Show file tree
Hide file tree
Showing 21 changed files with 567 additions and 22 deletions.
14 changes: 8 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# Change Log

## Release 1.3.1 (TBD)
## Release 1.3.1 (10/29/23)

### Added

- `test_output` can be passed in as an arg to compare the results of `test_input`
- Generator/Streaming handlers supported with local testing
- [BETA] CLI DevEx functionality to create development projects.

---

## Release 1.3.0 (10/12/23)

### Changes
### Changed

- Backwards compatibility with Python >= 3.8
- Consolidated install dependencies to `requirements.txt`
Expand All @@ -23,7 +25,7 @@

## Release 1.2.6 (10/6/23)

### Changes
### Changed

- Force `urllib3` logging to `WARNING` level to avoid spamming the console if global logging level is set to `DEBUG`.

Expand All @@ -40,7 +42,7 @@

## ~~Release (Patch) 1.2.3 (10/4/23)~~ Replaced by 1.2.5

### Bug Fix
### Fixed

- Job outputs that were not dictionaries, bool, or str were swallowed by the serverless worker. This has been fixed.

Expand All @@ -55,7 +57,7 @@
- `network_volume_id` can now be passed in when creating new pods, correct data center is automatically selected.
- `template_id` can now be passed in when creating new pods.

### Changes
### Changed

- Dependencies updated to latest versions.
- Reduced circular imports for version reference.
Expand Down Expand Up @@ -91,7 +93,7 @@
- Can generate a credentials file from the CLI to store your API key.
- `get_gpu` now supports `gpu_quantity` as a parameter.

### Changes
### Changed

- Minimized the use of pytests in favor of unittests.
- Re-named `api_wrapper` to `api` for consistency.
Expand Down
Binary file modified docs/cli/demos/config.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/cli/demos/help.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/cli/demos/ssh.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions examples/api/create_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
""" Example of creating an endpoint with the Runpod API. """

import runpod

# Set your global API key with `runpod config` or uncomment the line below:
# runpod.api_key = "YOUR_RUNPOD_API_KEY"

try:

new_template = runpod.create_template(
name="test",
image_name="runpod/base:0.1.0",
is_serverless=True
)

print(new_template)

new_endpoint = runpod.create_endpoint(
name="test",
template_id=new_template["id"],
gpu_ids="AMPERE_16",
workers_min=0,
workers_max=1
)

print(new_endpoint)

except runpod.error.QueryError as err:
print(err)
print(err.query)
19 changes: 19 additions & 0 deletions examples/api/create_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
""" Example of creating a template with the Runpod API. """

import runpod

# Set your global API key with `runpod config` or uncomment the line below:
# runpod.api_key = "YOUR_RUNPOD_API_KEY"

try:

new_template = runpod.create_template(
name="test",
image_name="runpod/base:0.1.0"
)

print(new_template)

except runpod.error.QueryError as err:
print(err)
print(err.query)
7 changes: 4 additions & 3 deletions runpod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
from .version import __version__
from .api.ctl_commands import(
get_user, update_user_settings,
get_gpus, get_gpu,
get_pods, get_pod,
create_pod, stop_pod, resume_pod, terminate_pod
get_gpu, get_gpus,
get_pod, get_pods, create_pod, stop_pod, resume_pod, terminate_pod,
create_template,
create_endpoint
)
from .cli.groups.config.functions import set_credentials, check_credentials, get_credentials

Expand Down
74 changes: 74 additions & 0 deletions runpod/api/ctl_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
from .queries import pods as pod_queries
from .graphql import run_graphql_query
from .mutations import pods as pod_mutations
from .mutations import endpoints as endpoint_mutations

# Templates
from .mutations import templates as template_mutations

def get_user() -> dict:
'''
Expand Down Expand Up @@ -188,3 +192,73 @@ def terminate_pod(pod_id: str):
run_graphql_query(
pod_mutations.generate_pod_terminate_mutation(pod_id)
)


def create_template(
name:str, image_name:str, docker_start_cmd:str=None,
container_disk_in_gb:int=10, volume_in_gb:int=None, volume_mount_path:str=None,
ports:str=None, env:dict=None, is_serverless:bool=False
):
'''
Create a template
:param name: the name of the template
:param image_name: the name of the docker image to be used by the template
:param docker_start_cmd: the command to start the docker container with
:param container_disk_in_gb: how big should the container disk be
:param volume_in_gb: how big should the volume be
:param ports: the ports to open in the pod, example format - "8888/http,666/tcp"
:param volume_mount_path: where to mount the volume?
:param env: the environment variables to inject into the pod,
for example {EXAMPLE_VAR:"example_value", EXAMPLE_VAR2:"example_value 2"}, will
inject EXAMPLE_VAR and EXAMPLE_VAR2 into the pod with the mentioned values
:param is_serverless: is the template serverless?
:example:
>>> template_id = runpod.create_template("test", "runpod/stack", "python3 main.py")
'''
raw_response = run_graphql_query(
template_mutations.generate_pod_template(
name, image_name, docker_start_cmd,
container_disk_in_gb, volume_in_gb, volume_mount_path,
ports, env, is_serverless
)
)

return raw_response["data"]["saveTemplate"]

def create_endpoint(
name:str, template_id:str, gpu_ids:str="AMPERE_16",
network_volume_id:str=None, locations:str=None,
idle_timeout:int=5, scaler_type:str="QUEUE_DELAY", scaler_value:int=4,
workers_min:int=0, workers_max:int=3
):
'''
Create an endpoint
:param name: the name of the endpoint
:param template_id: the id of the template to use for the endpoint
:param gpu_ids: the ids of the GPUs to use for the endpoint
:param network_volume_id: the id of the network volume to use for the endpoint
:param locations: the locations to use for the endpoint
:param idle_timeout: the idle timeout for the endpoint
:param scaler_type: the scaler type for the endpoint
:param scaler_value: the scaler value for the endpoint
:param workers_min: the minimum number of workers for the endpoint
:param workers_max: the maximum number of workers for the endpoint
:example:
>>> endpoint_id = runpod.create_endpoint("test", "template_id")
'''
raw_response = run_graphql_query(
endpoint_mutations.generate_endpoint_mutation(
name, template_id, gpu_ids,
network_volume_id, locations,
idle_timeout, scaler_type, scaler_value,
workers_min, workers_max
)
)

return raw_response["data"]["saveEndpoint"]
5 changes: 4 additions & 1 deletion runpod/api/graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def run_graphql_query(query: str) -> Dict[str, Any]:
raise error.AuthenticationError("Unauthorized request, please check your API key.")

if "errors" in response.json():
raise error.QueryError(response.json()["errors"][0]["message"])
raise error.QueryError(
response.json()["errors"][0]["message"],
query
)

return response.json()
59 changes: 59 additions & 0 deletions runpod/api/mutations/endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
""" RunPod | API Wrapper | Mutations | Endpoints """

# pylint: disable=too-many-arguments

def generate_endpoint_mutation(
name:str, template_id:str, gpu_ids:str="AMPERE_16",
network_volume_id:str=None, locations:str=None,
idle_timeout:int=5, scaler_type:str="QUEUE_DELAY", scaler_value:int=4,
workers_min:int=0, workers_max:int=3
):
""" Generate a string for a GraphQL mutation to create a new endpoint. """
input_fields = []

# ------------------------------ Required Fields ----------------------------- #
input_fields.append(f'name: "{name}"')
input_fields.append(f'templateId: "{template_id}"')
input_fields.append(f'gpuIds: "{gpu_ids}"')

# ------------------------------ Optional Fields ----------------------------- #
if network_volume_id is not None:
input_fields.append(f'networkVolumeId: "{network_volume_id}"')
else:
input_fields.append('networkVolumeId: ""')

if locations is not None:
input_fields.append(f'locations: "{locations}"')
else:
input_fields.append('locations: ""')

input_fields.append(f'idleTimeout: {idle_timeout}')
input_fields.append(f'scalerType: "{scaler_type}"')
input_fields.append(f'scalerValue: {scaler_value}')
input_fields.append(f'workersMin: {workers_min}')
input_fields.append(f'workersMax: {workers_max}')

# Format the input fields into a string
input_fields_string = ", ".join(input_fields)

return f"""
mutation {{
saveEndpoint(
input: {{
{input_fields_string}
}}
) {{
id
name
templateId
gpuIds
networkVolumeId
locations
idleTimeout
scalerType
scalerValue
workersMin
workersMax
}}
}}
"""
8 changes: 1 addition & 7 deletions runpod/api/mutations/pods.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,7 @@ def generate_pod_deployment_mutation(
input_fields.append(f'templateId: "{template_id}"')

if network_volume_id is not None:
# network_volume_fragment = f'''
# networkVolume: {{
# id: "{network_volume_id}"
# }}
# '''
network_volume_fragment = f'networkVolumeId: "{network_volume_id}"'
input_fields.append(network_volume_fragment)
input_fields.append(f'networkVolumeId: "{network_volume_id}"')

# Format input fields
input_string = ", ".join(input_fields)
Expand Down
83 changes: 83 additions & 0 deletions runpod/api/mutations/templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
""" RunPod | API Wrapper | Mutations | Templates """

# pylint: disable=too-many-arguments

def generate_pod_template(
name:str, image_name:str, docker_start_cmd:str=None,
container_disk_in_gb:int=10, volume_in_gb:int=None, volume_mount_path:str=None,
ports:str=None, env:dict=None, is_serverless:bool=False
):
""" Generate a string for a GraphQL mutation to create a new pod template. """
input_fields = []

# ------------------------------ Required Fields ----------------------------- #
input_fields.append(f'name: "{name}"')
input_fields.append(f'imageName: "{image_name}"')

# ------------------------------ Optional Fields ----------------------------- #
if docker_start_cmd is not None:
docker_start_cmd = docker_start_cmd.replace('"', '\\"')
input_fields.append(f'dockerArgs: "{docker_start_cmd}"')
else:
input_fields.append('dockerArgs: ""')

input_fields.append(f'containerDiskInGb: {container_disk_in_gb}')

if volume_in_gb is not None:
input_fields.append(f'volumeInGb: {volume_in_gb}')
else:
input_fields.append('volumeInGb: 0')

if volume_mount_path is not None:
input_fields.append(f'volumeMountPath: "{volume_mount_path}"')

if ports is not None:
ports = ports.replace(" ", "")
input_fields.append(f'ports: "{ports}"')
else:
input_fields.append('ports: ""')

if env is not None:
env_string = ", ".join(
[f'{{ key: "{key}", value: "{value}" }}' for key, value in env.items()])
input_fields.append(f"env: [{env_string}]")
else:
input_fields.append('env: []')


if is_serverless:
input_fields.append('isServerless: true')
else:
input_fields.append('isServerless: false')

# ------------------------------ Enforced Fields ----------------------------- #
input_fields.append('startSsh: true')
input_fields.append('isPublic: false')
input_fields.append('readme: ""')

# Format the input fields into a string
input_fields_string = ", ".join(input_fields)

return f"""
mutation {{
saveTemplate(
input: {{
{input_fields_string}
}}
) {{
id
name
imageName
dockerArgs
containerDiskInGb
volumeInGb
volumeMountPath
ports
env {{
key
value
}}
isServerless
}}
}}
"""
Loading

0 comments on commit 8162556

Please sign in to comment.