MingaAgent.Providers.Native (Minga v0.1.0)

Copy Markdown View Source

Native Elixir agent provider backed by ReqLLM.

Runs entirely inside the BEAM with no external dependencies. Supports any provider that ReqLLM supports (Anthropic, OpenAI, Ollama, Groq, Bedrock, etc.) by accepting a model string like "anthropic:claude-sonnet-4-20250514".

The provider manages conversation history via ReqLLM.Context, executes tools locally, and emits Agent.Event structs to its subscriber (the Agent.Session) for rendering in the chat panel.

Architecture

When send_prompt/2 is called, the provider spawns a linked Task to run the agent turn loop. The loop:

  1. Calls ReqLLM.stream_text/3 with the conversation history and tools
  2. Uses StreamResponse.process_stream/2 with callbacks to emit events in real time as chunks arrive
  3. If the response contains tool calls, executes each tool, appends results to the conversation context, and loops back to step 1
  4. If no tool calls, the turn is complete

The task sends events to the GenServer via send/2, and the GenServer forwards them to the subscriber. This keeps the GenServer responsive for abort and state queries while streaming is in progress.

Summary

Types

Function that executes a matching hook.

Function that performs the LLM streaming call.

Captures the immutable parameters for one agent turn loop invocation.

Internal state for the native provider.

Functions

Returns the agent hooks from the provider's config.

Returns the changeset PID, or nil.

Returns a specification to start this module under a supervisor.

Manually triggers context compaction.

Continues from an interrupted stream, asking the model to pick up where it left off.

Returns the PID of the fork store, or nil if not active.

Returns the project view associated with this provider, or nil.

Refreshes the project view and rebuilds file tools around the new overlay.

Returns the current tool list registered with this provider.

Types

hook_runner()

Function that executes a matching hook.

llm_client()

@type llm_client() :: (String.t(), [ReqLLM.Message.t()], keyword() ->
                   {:ok, ReqLLM.StreamResponse.t()} | {:error, term()})

Function that performs the LLM streaming call.

loop_ctx()

@type loop_ctx() :: MingaAgent.Providers.Native.LoopCtx.t()

Captures the immutable parameters for one agent turn loop invocation.

state()

@type state() :: %{
  subscriber: pid(),
  model: String.t(),
  config: MingaAgent.Config.t(),
  context: ReqLLM.Context.t(),
  tools: [term()],
  project_root: String.t(),
  project_view: MingaAgent.ProjectView.t() | nil,
  thinking_level: String.t(),
  max_tokens: pos_integer(),
  max_retries: non_neg_integer(),
  llm_client: llm_client(),
  hook_runner: hook_runner(),
  task: Task.t() | nil,
  streaming: boolean(),
  interrupted: boolean(),
  last_user_prompt: String.t() | nil,
  active_skills: [MingaAgent.Skills.skill()],
  internal_state: MingaAgent.InternalState.t(),
  max_turns: pos_integer(),
  max_cost: float() | nil,
  session_cost: float(),
  fork_store: pid() | nil,
  changeset: pid() | nil,
  base_tools: [term()],
  mcp_tools: [term()],
  internal_tools: [term()],
  custom_tools?: boolean(),
  configured_mcp_configs: [MingaAgent.MCP.ServerConfig.t()],
  mcp_configs: [MingaAgent.MCP.ServerConfig.t()],
  mcp_client_opts: keyword(),
  mcp_enabled_override: boolean() | nil,
  mcp_errors: %{required(String.t()) => String.t()},
  mcp_registry: MingaAgent.MCP.Registry.t() | nil,
  read_only?: boolean(),
  tool_allowlist: :all | [String.t()],
  tool_workers: %{required(reference()) => pid()}
}

Internal state for the native provider.

Functions

agent_hooks(pid)

@spec agent_hooks(GenServer.server()) :: [MingaAgent.Hooks.Hook.t()]

Returns the agent hooks from the provider's config.

changeset(pid)

@spec changeset(GenServer.server()) :: pid() | nil

Returns the changeset PID, or nil.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

compact(pid)

@spec compact(GenServer.server()) :: {:ok, String.t()} | {:error, String.t()}

Manually triggers context compaction.

continue(pid)

@spec continue(GenServer.server()) :: :ok | {:error, term()}

Continues from an interrupted stream, asking the model to pick up where it left off.

detect_project_root()

@spec detect_project_root() :: String.t()

See Minga.Project.resolve_root/0.

fork_store(pid)

@spec fork_store(GenServer.server()) :: pid() | nil

Returns the PID of the fork store, or nil if not active.

project_view(pid)

@spec project_view(GenServer.server()) :: MingaAgent.ProjectView.t() | nil

Returns the project view associated with this provider, or nil.

refresh_project_view(pid, project_view)

@spec refresh_project_view(GenServer.server(), MingaAgent.ProjectView.t() | nil) ::
  :ok | {:error, term()}

Refreshes the project view and rebuilds file tools around the new overlay.

strip_provider_prefix(model)

See MingaAgent.Config.strip_provider_prefix/1.

tools(pid)

@spec tools(GenServer.server()) :: [ReqLLM.Tool.t()]

Returns the current tool list registered with this provider.