Current section

Files

Jump to
langchain lib tools deep_research research_status.ex
Raw

lib/tools/deep_research/research_status.ex

defmodule LangChain.Tools.DeepResearch.ResearchStatus do
@moduledoc """
Represents the status of a Deep Research request.
This schema captures the current state of a research request, including
progress information and any errors that may have occurred.
"""
use Ecto.Schema
import Ecto.Changeset
@type t() :: %__MODULE__{
id: String.t(),
status: String.t(),
created_at: integer() | nil,
error_message: String.t() | nil,
error_code: String.t() | nil,
progress_info: map() | nil
}
@primary_key false
embedded_schema do
field :id, :string
field :status, :string
field :created_at, :integer
field :error_message, :string
field :error_code, :string
field :progress_info, :map
end
@valid_statuses ~w(queued in_progress completed failed cancelled incomplete)
@doc """
Creates a changeset for research status.
"""
@spec changeset(__MODULE__.t(), map()) :: Ecto.Changeset.t()
def changeset(status \\ %__MODULE__{}, attrs) do
status
|> cast(attrs, [:id, :status, :created_at, :error_message, :error_code, :progress_info])
|> validate_required([:id, :status])
|> validate_inclusion(:status, @valid_statuses)
end
@doc """
Creates a ResearchStatus from an OpenAI API response.
"""
@spec from_api_response(map()) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}
def from_api_response(response) do
attrs = %{
id: Map.get(response, "id"),
status: Map.get(response, "status"),
created_at: Map.get(response, "created_at"),
error_message: get_in(response, ["error", "message"]),
error_code: get_in(response, ["error", "code"]),
progress_info: extract_progress_info(response)
}
changeset = changeset(%__MODULE__{}, attrs)
if changeset.valid? do
{:ok, apply_changes(changeset)}
else
{:error, changeset}
end
end
@doc """
Checks if the research is complete (either successfully or with failure).
"""
@spec complete?(__MODULE__.t()) :: boolean()
def complete?(%__MODULE__{status: status})
when status in ["completed", "failed", "cancelled"] do
true
end
def complete?(_), do: false
@doc """
Checks if the research completed successfully.
"""
@spec successful?(__MODULE__.t()) :: boolean()
def successful?(%__MODULE__{status: "completed"}), do: true
def successful?(_), do: false
@doc """
Checks if the research failed.
"""
@spec failed?(__MODULE__.t()) :: boolean()
def failed?(%__MODULE__{status: status}) when status in ["failed", "cancelled"] do
true
end
def failed?(_), do: false
@doc """
Gets a human-readable description of the current status.
"""
@spec status_description(__MODULE__.t()) :: String.t()
def status_description(%__MODULE__{status: "queued"}),
do: "Research request is queued for processing"
def status_description(%__MODULE__{status: "in_progress"}),
do: "Research is currently in progress"
def status_description(%__MODULE__{status: "completed"}), do: "Research completed successfully"
def status_description(%__MODULE__{status: "failed", error_message: msg}) when is_binary(msg),
do: "Research failed: #{msg}"
def status_description(%__MODULE__{status: "failed"}), do: "Research failed"
def status_description(%__MODULE__{status: "cancelled"}), do: "Research was cancelled"
def status_description(%__MODULE__{status: "incomplete"}),
do: "Research completed but may be incomplete"
def status_description(%__MODULE__{status: status}), do: "Unknown status: #{status}"
@spec extract_progress_info(map()) :: map() | nil
defp extract_progress_info(response) do
# Extract any progress-related information from the response
# This could include tool call counts, current step, etc.
case response do
%{"output" => output} when is_list(output) ->
tool_calls =
Enum.count(
output,
&(Map.get(&1, "type") in ["web_search_call", "code_interpreter_call", "mcp_tool_call"])
)
if tool_calls > 0 do
%{tool_calls_made: tool_calls}
else
nil
end
_ ->
nil
end
end
end