Module: LLM::HuggingfaceMethods
- Included in:
- Huggingface
- Defined in:
- lib/scout/llm/backends/huggingface.rb
Constant Summary collapse
- MODEL_OPTION_KEYS =
%i[ task checkpoint dir chat_template chat_template_kwargs generation_kwargs response_parser tool_argument tokenizer_args tokenizer_options training_args training_options trust_remote_code torch_dtype device_map device ]
Instance Method Summary collapse
- #embed(text, options = {}) ⇒ Object
- #format_tool_call(message) ⇒ Object
- #format_tool_definitions(tools) ⇒ Object
- #format_tool_output(message, last_id = nil) ⇒ Object
- #model(model_options = {}) ⇒ Object
- #model_options(options = {}) ⇒ Object
- #parse_tool_call(info) ⇒ Object
- #prepare_client(options, messages = nil) ⇒ Object
- #process_response(messages, response, tools, options, &block) ⇒ Object
- #query(client, messages, tools = [], parameters = {}) ⇒ Object
- #reasoning(response, current_meta = nil) ⇒ Object
- #tools(messages, options) ⇒ Object
Instance Method Details
#embed(text, options = {}) ⇒ Object
190 191 192 193 194 195 196 |
# File 'lib/scout/llm/backends/huggingface.rb', line 190 def (text, = {}) = self.() [:task] = 'Embedding' model = self.model() (Array === text) ? model.eval_list(text) : model.eval(text) end |
#format_tool_call(message) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/scout/llm/backends/huggingface.rb', line 88 def format_tool_call() tool_call = IndiferentHash.setup(JSON.parse([:content])) arguments = tool_call.delete('arguments') || tool_call.dig('function', 'arguments') || {} arguments = JSON.parse(arguments) rescue arguments if String === arguments id = tool_call.delete('call_id') || tool_call.delete('id') name = tool_call.delete('name') || tool_call.dig('function', 'name') { role: 'assistant', tool_calls: [IndiferentHash.setup({ type: 'function', id: id, function: { name: name, arguments: arguments } })] } end |
#format_tool_definitions(tools) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/scout/llm/backends/huggingface.rb', line 68 def format_tool_definitions(tools) return [] if tools.nil? tools.values.collect do |obj, definition| definition = obj if Hash === obj definition = IndiferentHash.setup(definition) definition = case definition[:function] when Hash definition else { type: :function, function: definition } end definition[:function][:parameters].delete(:defaults) if definition.dig(:function, :parameters) definition end end |
#format_tool_output(message, last_id = nil) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/scout/llm/backends/huggingface.rb', line 108 def format_tool_output(, last_id = nil) info = IndiferentHash.setup(JSON.parse([:content])) id = info.delete('call_id') || info.delete('id') || last_id name = info.delete('name') content = info.delete('content') { role: 'tool', name: name, content: content, tool_call_id: id, }.compact end |
#model(model_options = {}) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/scout/llm/backends/huggingface.rb', line 26 def model( = {}) require 'scout/model/python/huggingface' require 'scout/model/python/huggingface/causal' = IndiferentHash.setup(.dup) = IndiferentHash.add_defaults(, task: 'CausalLM') model_name = IndiferentHash.(, :model) dir = [:dir] CausalModel.new model_name, dir, end |
#model_options(options = {}) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/scout/llm/backends/huggingface.rb', line 14 def ( = {}) = IndiferentHash.setup(.dup) = IndiferentHash.pull_keys(, :model) || {} MODEL_OPTION_KEYS.each do |key| [key] = [key] if .include?(key) end [:model] ||= Scout::Config.get(:model, :huggingface, env: 'HUGGINGFACE_MODEL,HF_MODEL') end |
#parse_tool_call(info) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/scout/llm/backends/huggingface.rb', line 122 def parse_tool_call(info) info = IndiferentHash.setup(info) function = IndiferentHash.setup(info[:function] || {}) arguments = function[:arguments] || info[:arguments] || info[:parameters] || {} arguments = JSON.parse(arguments) rescue arguments if String === arguments name = function[:name] || info[:name] id = info[:id] || info[:call_id] || info[:tool_call_id] || (name.to_s + '_' + Misc.digest(arguments.to_json)) { arguments: arguments, id: id, name: name } end |
#prepare_client(options, messages = nil) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/scout/llm/backends/huggingface.rb', line 39 def prepare_client(, = nil) client = IndiferentHash.(, :client) if client.nil? = self.() Log.debug "Client options: #{.inspect}" client = self.model() [:model] ||= [:model] else Log.debug "Reusing client: #{Log.fingerprint client}" end client end |
#process_response(messages, response, tools, options, &block) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/scout/llm/backends/huggingface.rb', line 135 def process_response(, response, tools, , &block) = response['message'] content = ['content'] output = [] output << IndiferentHash.setup(role: :assistant, content: content) if String === content && !content.empty? tool_calls = Array([:tool_calls]).collect do |tool_call| parse_tool_call(tool_call) end.compact if tool_calls.any? output.concat LLM.process_calls(tools, tool_calls, &block) elsif output.empty? output << IndiferentHash.setup(role: :assistant, content: '') if .include?(:content) end output end |
#query(client, messages, tools = [], parameters = {}) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/scout/llm/backends/huggingface.rb', line 55 def query(client, , tools = [], parameters = {}) formatted_tools = format_tool_definitions(tools) parameters[:generation_kwargs] ||= IndiferentHash.pull_keys parameters, :generation_kwargs parameters[:chat_template] ||= IndiferentHash.pull_keys parameters, :chat_template parameters = parameters.keys_to_sym response = client.chat(, formatted_tools, parameters) response = ScoutPython.dict2hash(response) IndiferentHash.setup({message: response}) rescue Log.debug 'Input parameters: ' + "\n" + JSON.pretty_generate(parameters.except(:tools)) raise $! end |
#reasoning(response, current_meta = nil) ⇒ Object
181 182 183 184 185 186 187 188 |
# File 'lib/scout/llm/backends/huggingface.rb', line 181 def reasoning(response, = nil) response = IndiferentHash.setup(response) = IndiferentHash.setup(response[:message] || response) reasoning_content = [:thinking] reasoning_content = reasoning_content.gsub("\n", ' ') if String === reasoning_content Log.medium "Reasoning:\n" + Log.color(:cyan, reasoning_content) if reasoning_content reasoning_content end |
#tools(messages, options) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/scout/llm/backends/huggingface.rb', line 155 def tools(, ) tools = .delete :tools case tools when Array tools = tools.inject({}) do |acc, definition| IndiferentHash.setup definition name = definition.dig('name') || definition.dig('function', 'name') acc.merge(name => definition) end when nil tools = {} end = .reject do || [:role].to_s == 'tool' && (.include?(:tool_call_id) || .include?(:name)) end tools.merge!(LLM.tools()) tools.merge!(LLM.associations()) Log.high "Tools: #{Log.fingerprint tools.keys}" if tools tools end |