Module: PatientLLM

Defined in:
lib/patient_llm.rb,
lib/patient_llm/callback.rb,
lib/patient_llm/halt_error.rb,
lib/patient_llm/configuration.rb,
lib/patient_llm/max_tool_iterations_error.rb

Defined Under Namespace

Classes: Callback, Configuration, HaltError, MaxToolIterationsError

Constant Summary collapse

VERSION =
File.read(File.join(__dir__, "../VERSION")).strip
SERIALIZER_PATHS =

Default API paths per serializer format. The Gemini path embeds a ‘model` placeholder that is replaced with the session’s model at dispatch time, matching Google’s ‘/v1beta/models/model:generateContent` endpoint.

{
  chat_completion: "/v1/chat/completions",
  open_responses: "/v1/responses",
  messages: "/v1/messages",
  converse: "/converse",
  gemini: "/v1beta/models/{model}:generateContent"
}.freeze
ANTHROPIC_VERSION =

Required version header for the Anthropic Messages API.

"2023-06-01"
VALID_SERIALIZERS =

Valid serializer format names.

SERIALIZER_PATHS.keys.freeze
AUTHENTICATION_HEADERS =

Headers that must be setup to use the secrets manager. If any of these headers are included in the provider configuration, an error will be raised unless their values are set up as secrets using ‘PatientHttp.secret`.

["authorization", "x-api-key", "x-goog-api-key", "api-key"].freeze

Class Method Summary collapse

Class Method Details

.ask(session, provider:, callback:, callback_args: {}, url: nil, serializer: nil, completion_path: nil, headers: nil, params: nil, tool_iteration: 0, original_request_id: nil) ⇒ Object

Send an LLM request asynchronously using the given session and provider.

Parameters:

  • session (PromptBuilder::Session)

    The prompt session containing conversation state

  • provider (Symbol, String)

    Registered provider name

  • callback (Class, String)

    Callback class for handling completion/error

  • callback_args (Hash) (defaults to: {})

    Custom arguments passed through to the callback

  • url (String, nil) (defaults to: nil)

    Override the provider’s base URL for this request

  • serializer (Symbol, nil) (defaults to: nil)

    Override the provider’s serializer for this request

  • completion_path (String, nil) (defaults to: nil)

    Override the endpoint path for this request

  • headers (Hash, nil) (defaults to: nil)

    Additional headers merged on top of provider headers

  • params (Hash, nil) (defaults to: nil)

    Additional params merged into the request payload

Returns:

  • (Object)

    Handler-specific identifier for the enqueued request

Raises:

  • (ArgumentError)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/patient_llm.rb', line 69

def ask(session, provider:, callback:, callback_args: {}, url: nil, serializer: nil, completion_path: nil, headers: nil, params: nil, tool_iteration: 0, original_request_id: nil) # :nodoc: tool_iteration and original_request_id are internal
  provider_config = self.provider(provider) || {}
  provider_name = provider.to_s

  if tool_iteration.zero?
    PatientLLM::Callback.validate_callback_class!(PatientHttp::ClassHelper.resolve_class_name(callback.to_s))
  end

  resolved_url = url || provider_config[:url]
  raise ArgumentError, "No API base URL configured. Set url: or register a provider with a url." unless resolved_url

  resolved_serializer = (serializer || provider_config[:serializer] || :chat_completion).to_sym
  validate_serializer!(resolved_serializer)
  resolved_completion_path = completion_path || provider_config[:completion_path] || SERIALIZER_PATHS[resolved_serializer] || "/v1/chat/completions"
  if resolved_completion_path.include?("{model}")
    resolved_completion_path = resolved_completion_path.gsub("{model}", session.model.to_s)
  end
  resolved_headers = (provider_config[:headers] || {}).merge(headers || {})
  if resolved_serializer == :messages && !resolved_headers.key?("anthropic-version")
    resolved_headers = {"anthropic-version" => ANTHROPIC_VERSION}.merge(resolved_headers)
  end
  resolved_params = (provider_config[:params] || {}).merge(params || {})

  payload = session.request_payload(resolved_serializer)
  payload = deep_merge(payload, deep_stringify_keys(resolved_params)) unless resolved_params.empty?

  request_url = join_url(resolved_url, resolved_completion_path)

  request_options = {}
  request_options["url"] = url if url
  request_options["serializer"] = serializer.to_s if serializer
  request_options["completion_path"] = completion_path if completion_path
  request_options["headers"] = headers if headers && !headers.empty?
  request_options["params"] = params if params && !params.empty?

  PatientHttp.post(
    request_url,
    json: payload,
    headers: resolved_headers,
    raise_error_responses: true,
    callback: PatientLLM::Callback,
    callback_args: {
      session: session.to_h,
      provider: provider_name,
      callback: callback.to_s,
      custom: callback_args.transform_keys(&:to_s),
      request_options: request_options,
      tool_iteration: tool_iteration,
      original_request_id: original_request_id
    }
  )
end

.configure {|Configuration| ... } ⇒ void

This method returns an undefined value.

Configure providers for LLM requests.

Yields:



37
38
39
40
# File 'lib/patient_llm.rb', line 37

def configure
  @configuration ||= Configuration.new
  yield @configuration
end

.provider(name) ⇒ Hash?

Look up a registered provider by name.

Parameters:

  • name (Symbol, String)

    Provider name

Returns:

  • (Hash, nil)

    Provider config



53
54
55
# File 'lib/patient_llm.rb', line 53

def provider(name)
  @configuration&.lookup(name)
end

.reset!void

This method returns an undefined value.

Reset configuration. Primarily useful in tests.



45
46
47
# File 'lib/patient_llm.rb', line 45

def reset!
  @configuration = nil
end