Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions lib/ex_aws/bedrock/nova/content.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
defmodule ExAws.Bedrock.Nova.Content do
@moduledoc """
Constructor functions for dynamic content items (Text, Image, Video, Audio).
"""

@doc "Text content item"
def text(string) when is_binary(string), do: %{text: string}

@doc "Image content item"
def image(format, bytes_or_base64) when format in ~w(jpeg png gif webp) do
%{
image: %{
format: format,
source: %{bytes: bytes_or_base64}
}
}
end

@doc "Video content item via bytes/base64"
def video_bytes(format, bytes_or_base64)
when format in ~w(mkv mov mp4 webm three_gp flv mpeg mpg wmv) do
%{
video: %{
format: format,
source: %{bytes: bytes_or_base64}
}
}
end

@doc "Video content item via S3 Location"
def video_s3(format, uri, bucket_owner \\ nil)
when format in ~w(mkv mov mp4 webm three_gp flv mpeg mpg wmv) do
s3_loc =
then(%{uri: uri}, fn map ->
if bucket_owner, do: Map.put(map, :bucketOwner, bucket_owner), else: map
end)

%{
video: %{
format: format,
source: %{s3Location: s3_loc}
}
}
end

@doc "Audio content item via bytes/base64"
def audio_bytes(format, bytes_or_base64)
when format in ~w(mp3 opus wav aac flac mp4 ogg mkv) do
%{
audio: %{
format: format,
source: %{bytes: bytes_or_base64}
}
}
end

@doc "Audio content item via S3 Location"
def audio_s3(format, uri, bucket_owner \\ nil)
when format in ~w(mp3 opus wav aac flac mp4 ogg mkv) do
s3_loc =
then(%{uri: uri}, fn map ->
if bucket_owner, do: Map.put(map, :bucketOwner, bucket_owner), else: map
end)

%{
audio: %{
format: format,
source: %{s3Location: s3_loc}
}
}
end
end
53 changes: 53 additions & 0 deletions lib/ex_aws/bedrock/nova/inference_config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule ExAws.Bedrock.Nova.InferenceConfig do
@moduledoc """
Inference options for Nova text models.
"""

alias ExAws.Bedrock.Nova.ReasoningConfig

defstruct [
:maxTokens,
:temperature,
:topP,
:topK,
:stopSequences,
:reasoningConfig
]

@type t :: %__MODULE__{
maxTokens: pos_integer() | nil,
temperature: float() | nil,
topP: float() | nil,
topK: non_neg_integer() | nil,
stopSequences: list(String.t()) | nil,
reasoningConfig: ReasoningConfig.t() | nil
}

def build(opts \\ []) when is_list(opts) do
reasoning =
case Keyword.get(opts, :reasoning_config) do
%ReasoningConfig{} = config -> config
opts when is_list(opts) -> ReasoningConfig.build(opts)
_ -> nil
end

%__MODULE__{
maxTokens: Keyword.get(opts, :max_tokens),
temperature: Keyword.get(opts, :temperature),
topP: Keyword.get(opts, :top_p),
topK: Keyword.get(opts, :top_k),
stopSequences: Keyword.get(opts, :stop_sequences),
reasoningConfig: reasoning
}
end
end

defimpl Jason.Encoder, for: ExAws.Bedrock.Nova.InferenceConfig do
def encode(struct, opts) do
struct
|> Map.from_struct()
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
|> Map.new()
|> Jason.Encode.map(opts)
end
end
40 changes: 40 additions & 0 deletions lib/ex_aws/bedrock/nova/message.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule ExAws.Bedrock.Nova.Message do
@moduledoc """
Encapsulates user and assistant turns in the conversation.
"""

alias ExAws.Bedrock.Nova.Content

defstruct [:role, :content]

@type role :: :user | :assistant
@type t :: %__MODULE__{
role: String.t(),
content: list(map())
}

@doc "Constructs a user turn with text or multimodal content items."
def user(content) when is_binary(content), do: build("user", [Content.text(content)])
def user(content) when is_list(content), do: build("user", content)

@doc "Constructs an assistant turn (useful for prompt pre-filling)."
def assistant(content) when is_binary(content), do: build("assistant", [Content.text(content)])
def assistant(content) when is_list(content), do: build("assistant", content)

def build(role, content) when role in ["user", "assistant", :user, :assistant] do
%__MODULE__{
role: to_string(role),
content: content
}
end
end

defimpl Jason.Encoder, for: ExAws.Bedrock.Nova.Message do
def encode(struct, opts) do
struct
|> Map.from_struct()
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
|> Map.new()
|> Jason.Encode.map(opts)
end
end
31 changes: 31 additions & 0 deletions lib/ex_aws/bedrock/nova/reasoning_config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule ExAws.Bedrock.Nova.ReasoningConfig do
@moduledoc """
Reasoning configuration parameters for Nova reasoning models.
"""

defstruct [:type, :maxReasoningEffort]

@type type :: String.t()
@type max_effort :: String.t()
@type t :: %__MODULE__{
type: type() | nil,
maxReasoningEffort: max_effort() | nil
}

def build(opts) when is_list(opts) do
%__MODULE__{
type: Keyword.get(opts, :type, "disabled"),
maxReasoningEffort: Keyword.get(opts, :max_reasoning_effort)
}
end
end

defimpl Jason.Encoder, for: ExAws.Bedrock.Nova.ReasoningConfig do
def encode(struct, opts) do
struct
|> Map.from_struct()
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
|> Map.new()
|> Jason.Encode.map(opts)
end
end
64 changes: 64 additions & 0 deletions lib/ex_aws/bedrock/nova/text_model.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
defmodule ExAws.Bedrock.Nova.TextModel do
@moduledoc """
Top-level request payload structure for Amazon Nova models on Bedrock.
"""

alias ExAws.Bedrock.Nova.{InferenceConfig, Message, ToolConfig}

defstruct [
:messages,
:system,
:inferenceConfig,
:toolConfig
]

@type t :: %__MODULE__{
messages: list(Message.t()),
system: list(%{text: String.t()}) | nil,
inferenceConfig: InferenceConfig.t() | nil,
toolConfig: ToolConfig.t() | nil
}

@doc """
Build a `TextModel` struct.

## Options
* `:system` - String system prompt or list of system prompt maps.
* `:inference_config` - Keyword list or `%InferenceConfig{}`.
* `:tool_config` - Keyword list or `%ToolConfig{}`.
"""
def build(messages, opts \\ []) when is_list(messages) do
system =
case Keyword.get(opts, :system) do
nil -> nil
prompt when is_binary(prompt) -> [%{text: prompt}]
list when is_list(list) -> list
end

inference_config =
case Keyword.get(opts, :inference_config) do
%InferenceConfig{} = config -> config
opts when is_list(opts) -> InferenceConfig.build(opts)
_ -> nil
end

tool_config = Keyword.get(opts, :tool_config)

%__MODULE__{
messages: messages,
system: system,
inferenceConfig: inference_config,
toolConfig: tool_config
}
end
end

defimpl Jason.Encoder, for: ExAws.Bedrock.Nova.TextModel do
def encode(struct, opts) do
struct
|> Map.from_struct()
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
|> Map.new()
|> Jason.Encode.map(opts)
end
end
35 changes: 35 additions & 0 deletions lib/ex_aws/bedrock/nova/tool_config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule ExAws.Bedrock.Nova.ToolConfig do
@moduledoc """
Tool specifications and tool choice settings.
"""

defstruct [:tools, :toolChoice]

@type tool_choice :: %{auto: %{}} | %{any: %{}} | %{tool: %{name: String.t()}}
@type t :: %__MODULE__{
tools: list(map()),
toolChoice: tool_choice() | nil
}

def build(tools, tool_choice \\ :auto) do
%__MODULE__{
tools: tools,
toolChoice: format_tool_choice(tool_choice)
}
end

defp format_tool_choice(:auto), do: %{auto: %{}}
defp format_tool_choice(:any), do: %{any: %{}}
defp format_tool_choice({:tool, name}) when is_binary(name), do: %{tool: %{name: name}}
defp format_tool_choice(map) when is_map(map), do: map
end

defimpl Jason.Encoder, for: ExAws.Bedrock.Nova.ToolConfig do
def encode(struct, opts) do
struct
|> Map.from_struct()
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
|> Map.new()
|> Jason.Encode.map(opts)
end
end
33 changes: 33 additions & 0 deletions lib/ex_aws/bedrock/nova/tool_spec.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
defmodule ExAws.Bedrock.Nova.ToolSpec do
@moduledoc """
Defines an individual tool schema for tool use/function calling.
"""

defstruct [:name, :description, :inputSchema]

@type t :: %__MODULE__{
name: String.t(),
description: String.t() | nil,
inputSchema: %{json: map()}
}

def build(name, description, json_schema) do
%{
toolSpec: %__MODULE__{
name: name,
description: description,
inputSchema: %{json: json_schema}
}
}
end
end

defimpl Jason.Encoder, for: ExAws.Bedrock.Nova.ToolSpec do
def encode(struct, opts) do
struct
|> Map.from_struct()
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
|> Map.new()
|> Jason.Encode.map(opts)
end
end
2 changes: 1 addition & 1 deletion test/ex_aws/bedrock_image_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ defmodule ExAws.Bedrock.InvokeModelImageTest do
end

defp setup_image_model(_) do
[model_id: "amazon.titan-image-generator-v1"]
[model_id: "amazon.titan-image-generator-v2:0"]
end

defp setup_image_size(_) do
Expand Down
Loading
Loading