From 162e6b11a74048f3892e65e9705d162f1b1d3927 Mon Sep 17 00:00:00 2001 From: Charlie Holtz Date: Sat, 16 Sep 2023 11:38:48 -0400 Subject: [PATCH] Add deployments (#18) * deployment behavior * fix deployments * version not necessary * fix behavior for deployments * remove unused import * update tests * add readme explanation * update version to 1.1.1 --- README.md | 13 +++++- lib/deployments.ex | 85 +++++++++++++++++++++++++++++++++++ lib/deployments/behaviour.ex | 17 +++++++ lib/deployments/deployment.ex | 9 ++++ lib/mock_client.ex | 4 +- mix.exs | 2 +- test/replicate_test.exs | 12 +++++ 7 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 lib/deployments.ex create mode 100644 lib/deployments/behaviour.ex create mode 100644 lib/deployments/deployment.ex diff --git a/README.md b/README.md index 26147e6..b132913 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Install by adding `replicate` to your list of dependencies in `mix.exs`: ```elixir def deps do [ - {:replicate, "~> 1.1.0"} + {:replicate, "~> 1.1.1"} ] end ``` @@ -219,4 +219,15 @@ iex> {{_, 200, 'OK'}, _headers, body} = resp iex> File.write!("babadook_watercolor.jpg", body) ``` +## Create prediction from deployment + +Deployments allow you to control the configuration of a model with a private, fixed API endpoint. You can control the version of the model, the hardware it runs on, and how it scales. + +Once you create a deployment on Replicate, you can make predictions like this: + +```elixir +iex> {:ok, deployment} = Replicate.Deployments.get("test/model") +iex> {:ok, prediction} = Replicate.Deployments.create_prediction(deployment, %{prompt: "a 19th century portrait of a wombat gentleman"}) +``` + # replicate-elixir diff --git a/lib/deployments.ex b/lib/deployments.ex new file mode 100644 index 0000000..f41be5c --- /dev/null +++ b/lib/deployments.ex @@ -0,0 +1,85 @@ +defmodule Replicate.Deployments do + @moduledoc """ + Documentation for `Predictions`. + """ + @behaviour Replicate.Deployments.Behaviour + @replicate_client Application.compile_env(:replicate, :replicate_client, Replicate.Client) + + alias Replicate.Deployments.Deployment + alias Replicate.Predictions.Prediction + + @doc """ + Gets a deployment by name, in the format `owner/model-name`. + + ## Examples + + ``` + iex> {:ok, deployment} = Replicate.Deployments.get("test/model") + iex> deployment.username + "test" + + iex> Replicate.Predictions.get("not_a_real_id") + {:error, "Not found"} + ``` + """ + def get(name) do + [owner, model_name] = String.split(name, "/") + {:ok, %Deployment{username: owner, name: model_name}} + end + + @doc """ + Create a new prediction with the deployment. The input parameter should be a map of the model inputs. + + ## Examples + + ``` + iex> {:ok, deployment} = Replicate.Deployments.get("test/model") + iex> {:ok, prediction} = Replicate.Deployments.create_prediction(deployment, %{prompt: "a 19th century portrait of a wombat gentleman"}) + iex> prediction.status + "starting" + ``` + """ + def create_prediction( + %Deployment{username: username, name: name}, + input, + webhook \\ nil, + webhook_completed \\ nil, + webhook_event_filter \\ nil, + stream \\ nil + ) do + webhook_parameters = + %{ + "webhook" => webhook, + "webhook_completed" => webhook_completed, + "webhook_event_filter" => webhook_event_filter, + "stream" => stream + } + |> Enum.filter(fn {_key, value} -> !is_nil(value) end) + |> Enum.into(%{}) + + body = + %{ + "input" => input |> Enum.into(%{}) + } + |> Map.merge(webhook_parameters) + |> Jason.encode!() + + @replicate_client.request(:post, "/v1/deployments/#{username}/#{name}/predictions", body) + |> parse_response() + end + + defp parse_response({:ok, json_body}) do + body = + json_body + |> Jason.decode!() + |> string_to_atom() + + {:ok, struct(Prediction, body)} + end + + defp parse_response({:error, message}), do: {:error, message} + + defp string_to_atom(body) do + for {k, v} <- body, into: %{}, do: {String.to_atom(k), v} + end +end diff --git a/lib/deployments/behaviour.ex b/lib/deployments/behaviour.ex new file mode 100644 index 0000000..39ede05 --- /dev/null +++ b/lib/deployments/behaviour.ex @@ -0,0 +1,17 @@ +defmodule Replicate.Deployments.Behaviour do + @moduledoc """ + Documentation for the Deployment Behaviour. + """ + alias Replicate.Deployments.Deployment + + @callback get(String.t()) :: {:ok, Deployment.t()} | {:error, String.t()} + @callback create_prediction( + Deployment.t(), + input :: %{string: any}, + webhook :: list(String.t()), + webhook_completed :: list(String.t()), + webook_event_filter :: list(String.t()), + stream :: boolean() + ) :: + {:ok, Replicate.Predictions.Prediction.t()} | {:error, String.t()} +end diff --git a/lib/deployments/deployment.ex b/lib/deployments/deployment.ex new file mode 100644 index 0000000..83b4181 --- /dev/null +++ b/lib/deployments/deployment.ex @@ -0,0 +1,9 @@ +defmodule Replicate.Deployments.Deployment do + @moduledoc """ + `Deployment` struct. + """ + defstruct [ + :username, + :name + ] +end diff --git a/lib/mock_client.ex b/lib/mock_client.ex index d546b25..3c3a1f6 100644 --- a/lib/mock_client.ex +++ b/lib/mock_client.ex @@ -14,7 +14,7 @@ defmodule Replicate.MockClient do ], urls: %{ "get" => "https://api.replicate.com/v1/predictions/1234", - "cancel" => "https://api.replicate.com/v1/predictions/1234/cancel", + "cancel" => "https://api.replicate.com/v1/predictions/1234/cancel" } } @stub_prediction2 %{ @@ -27,7 +27,7 @@ defmodule Replicate.MockClient do ], urls: %{ "get" => "https://api.replicate.com/v1/predictions/1235", - "cancel" => "https://api.replicate.com/v1/predictions/1235/cancel", + "cancel" => "https://api.replicate.com/v1/predictions/1235/cancel" } } diff --git a/mix.exs b/mix.exs index 1b29e56..f08be2f 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Replicate.MixProject do def project do [ app: :replicate, - version: "1.1.0", + version: "1.1.1", elixir: "~> 1.14", start_permanent: Mix.env() == :prod, start_permanent: Mix.env() == :prod, diff --git a/test/replicate_test.exs b/test/replicate_test.exs index cf143fd..16b99db 100644 --- a/test/replicate_test.exs +++ b/test/replicate_test.exs @@ -6,6 +6,7 @@ defmodule ReplicateTest do doctest Replicate doctest Replicate.Predictions doctest Replicate.Models + doctest Replicate.Deployments # Make sure mocks are verified when the test exits setup :verify_on_exit! @@ -79,4 +80,15 @@ defmodule ReplicateTest do assert first_version.id == "v1" assert first_version.cog_version == "0.3.0" end + + test "create a deployment prediction" do + {:ok, deployment} = Replicate.Deployments.get("test/model") + + {:ok, prediction} = + Replicate.Deployments.create_prediction(deployment, %{ + prompt: "a 19th century portrait of a wombat gentleman" + }) + + assert prediction.status == "starting" + end end