Current section

Files

Jump to
phantom_mcp lib phantom cache.ex
Raw

lib/phantom/cache.ex

defmodule Phantom.Cache do
@moduledoc false
def register(router) do
if not :persistent_term.get({Phantom, router, :initialized}, false) do
info = router.__phantom__(:info)
compiled_resource_templates = Map.get(info, :resource_templates, [])
compiled_prompts = Map.get(info, :prompts, [])
compiled_tools = Map.get(info, :tools, [])
:persistent_term.put({Phantom, router, :tools}, compiled_tools)
:persistent_term.put({Phantom, router, :prompts}, compiled_prompts)
:persistent_term.put({Phantom, router, :resource_templates}, compiled_resource_templates)
:persistent_term.put({Phantom, router, :initialized}, true)
end
:ok
end
def add_tool(router, tool_spec) do
tool = Phantom.Tool.build(tool_spec)
existing = :persistent_term.get({Phantom, router, :tools}, [])
tools = [tool | existing]
validate!([tool])
raise_if_duplicates(tools)
:persistent_term.put({Phantom, router, :tools}, tools)
end
def add_prompt(router, prompt_spec) do
prompt = Phantom.Prompt.build(prompt_spec)
existing = :persistent_term.get({Phantom, router, :prompts}, [])
prompts = [prompt | existing]
validate!([prompt])
raise_if_duplicates(prompts)
:persistent_term.put({Phantom, router, :prompts}, prompts)
end
defmacro add_resource_template(router, resource_template_spec) do
resource_template = Phantom.ResourceTemplate.build(resource_template_spec)
existing = :persistent_term.get({Phantom, router, :resource_templates}, [])
resource_templates = [resource_template | existing]
validate!([resource_template])
raise_if_duplicates(resource_templates)
:persistent_term.put({Phantom, router, :resource_templates}, resource_templates)
require Phantom.Router
Phantom.Router.__create_resource_routers__(resource_templates, __CALLER__)
end
def get(session, module, type) do
available = :persistent_term.get({Phantom, module, type}, [])
case Map.get(session, :"allowed_#{type}") do
nil -> available
authorized -> Enum.filter(available, &(&1.name in authorized))
end
end
def initialized?(router) do
:persistent_term.get({__MODULE__, router, :initialized}, false) == true
end
@doc false
def validate!(entities) do
Enum.each(entities, fn %mod{handler: handler, function: function} = entity ->
Code.ensure_loaded!(handler)
if not function_exported?(handler, function, 2) do
func = mod |> to_string() |> String.split(".") |> List.last() |> Macro.underscore()
file = Path.relative_to_cwd(entity.meta.file)
raise "#{func} was defined in #{file}:#{entity.meta.line} to call #{inspect(handler)}.#{function}/3 but that module and function does not exist."
end
end)
end
@doc false
def raise_if_duplicates([]), do: :ok
def raise_if_duplicates([%Phantom.ResourceTemplate{} | _] = resource_templates) do
resource_templates
|> Enum.group_by(&{&1.router, &1.name})
|> Enum.each(fn
{{_router, _name}, [_template]} ->
:ok
{{router, name}, templates} ->
raise """
There are conflicting resources with the name #{inspect(name)}.
Please distinguish them by providing a `:name` option.
#{inspect(Enum.map(templates, &{router, &1.handler, &1.function}), pretty: true)}
"""
end)
:ok
end
def raise_if_duplicates([%mod{} | _] = tools_or_prompts) do
tools_or_prompts
|> Enum.group_by(& &1.name)
|> Enum.each(fn
{_name, [_entity]} ->
:ok
{name, tools_or_prompts} ->
entity = mod |> to_string() |> String.split(".") |> List.last() |> Macro.underscore()
raise """
There are conflicting #{entity}s with the name #{inspect(name)}.
Please distinguish them by providing a `:name` option.
#{inspect(Enum.map(tools_or_prompts, &{&1.handler, &1.function}), pretty: true)}
"""
end)
end
end