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

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