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, path: nil, completion_path: nil, headers: nil, params: nil, preprocessors: 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

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

    Override the endpoint path for this request

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

    Deprecated alias for path

  • 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

  • preprocessors (String, Symbol, Array<String, Symbol>, nil) (defaults to: nil)

    Names of request preprocessors to apply to this request. Replaces the provider's configured preprocessors; pass an empty array to clear them for this request.

Returns:

  • (Object)

    Handler-specific identifier for the enqueued request

Raises:

  • (ArgumentError)


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
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/patient_llm.rb', line 74

def ask(session, provider:, callback:, callback_args: {}, url: nil, serializer: nil, path: nil, completion_path: nil, headers: nil, params: nil, preprocessors: nil, tool_iteration: 0, original_request_id: nil) # :nodoc: tool_iteration and original_request_id are internal
  if completion_path
    warn "PatientLLM.ask: the `completion_path:` argument is deprecated; use `path:` instead", uplevel: 1
    path ||= completion_path
  end

  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_path = path || provider_config[:path] || SERIALIZER_PATHS[resolved_serializer] || "/v1/chat/completions"
  if resolved_path.include?("{model}")
    resolved_path = resolved_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 || {})
  resolved_preprocessors = preprocessors || provider_config[:preprocessors]

  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_path)

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

  PatientHttp.post(
    request_url,
    json: payload,
    headers: resolved_headers,
    preprocessors: resolved_preprocessors,
    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:



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

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



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

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

.reset!void

This method returns an undefined value.

Reset configuration. Primarily useful in tests.



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

def reset!
  @configuration = nil
end