diff --git a/lib/req_llm/schema.ex b/lib/req_llm/schema.ex index 46936510..18b54c00 100644 --- a/lib/req_llm/schema.ex +++ b/lib/req_llm/schema.ex @@ -662,7 +662,11 @@ defmodule ReqLLM.Schema do @spec to_google_format(ReqLLM.Tool.t()) :: map() def to_google_format(%ReqLLM.Tool{} = tool) do json_schema = to_json(tool.parameter_schema) - parameters = deep_delete_keys(json_schema, @google_forbidden_schema_keys) + + parameters = + json_schema + |> deep_delete_keys(@google_forbidden_schema_keys) + |> stringify_enums() %{ "name" => tool.name, @@ -1073,6 +1077,21 @@ defmodule ReqLLM.Schema do defp deep_delete_keys(value, _keys), do: value + defp stringify_enums(map) when is_map(map) do + Map.new(map, fn + {"enum", values} when is_list(values) -> + {"enum", Enum.map(values, &to_string/1)} + + {k, v} -> + {k, stringify_enums(v)} + end) + end + + defp stringify_enums(list) when is_list(list), + do: Enum.map(list, &stringify_enums/1) + + defp stringify_enums(value), do: value + defp key_variants(keys) when is_list(keys), do: Enum.flat_map(keys, &key_variants/1) defp key_variants(key) when is_binary(key), do: [key, String.to_atom(key)] defp key_variants(key) when is_atom(key), do: [key, Atom.to_string(key)] diff --git a/test/req_llm/schema_test.exs b/test/req_llm/schema_test.exs index 81cef6d0..f223bcf0 100644 --- a/test/req_llm/schema_test.exs +++ b/test/req_llm/schema_test.exs @@ -502,6 +502,37 @@ defmodule ReqLLM.SchemaTest do assert result["parameters"]["required"] == ["tags"] end + test "to_google_format/1 stringifies non-string enum values" do + tool = %ReqLLM.Tool{ + name: "set_level", + description: "Set a level", + parameter_schema: %{ + "type" => "object", + "properties" => %{ + "level" => %{ + "type" => "integer", + "enum" => [1, 2, 3], + "description" => "The level" + }, + "mode" => %{ + "type" => "string", + "enum" => ["fast", "slow"], + "description" => "The mode" + } + }, + "required" => ["level"] + }, + callback: fn _ -> {:ok, "ok"} end + } + + result = Schema.to_google_format(tool) + level = result["parameters"]["properties"]["level"] + mode = result["parameters"]["properties"]["mode"] + + assert level["enum"] == ["1", "2", "3"] + assert mode["enum"] == ["fast", "slow"] + end + test "to_google_format/1 strips atom-keyed forbidden fields recursively" do atom_keyed_schema = %{ :type => :object,