Commit bca2227
Changed files (4)
lib/elelem/api.rb
@@ -1,120 +1,45 @@
# frozen_string_literal: true
+require "net/llm"
+
module Elelem
class Api
- attr_reader :configuration, :uri
+ attr_reader :configuration, :client
def initialize(configuration)
@configuration = configuration
- @uri = build_uri(configuration.host)
+ @client = Net::Llm::Ollama.new(
+ host: configuration.host,
+ model: configuration.model
+ )
end
def chat(messages, &block)
- Net::HTTP.start(uri.hostname, uri.port, http_options) do |http|
- http.request(build_request(messages)) do |response|
- if !response.is_a?(Net::HTTPSuccess)
- configuration.logger.error("API: HTTP error - #{response.code} #{response.message}")
- raise response.inspect
- end
-
- buffer = ""
- chunk_count = 0
-
- response.read_body do |chunk|
- buffer += chunk
-
- while (message = extract_sse_message(buffer))
- next if message.empty?
-
- if message == "[DONE]"
- block.call({ "done" => true })
- break
- end
-
- json = JSON.parse(message)
- delta = json.dig("choices", 0, "delta")
- finish_reason = json.dig("choices", 0, "finish_reason")
-
- if finish_reason
- block.call({ "finish_reason" => finish_reason })
- end
-
- if delta
- chunk_count += 1
- block.call(normalize(delta))
- end
- end
- end
- end
+ tools = configuration.tools.to_h
+ client.chat(messages, tools) do |chunk|
+ normalized = normalize_ollama_response(chunk)
+ block.call(normalized) if normalized
end
end
private
- def extract_sse_message(buffer)
- message_end = buffer.index("\n\n")
- return nil unless message_end
-
- message_data = buffer[0...message_end]
- buffer.replace(buffer[(message_end + 2)..-1] || "")
-
- data_lines = message_data.split("\n").filter_map do |line|
- if line.start_with?("data: ")
- line[6..-1]
- elsif line == "data:"
- ""
- end
- end
-
- return "" if data_lines.empty?
- data_lines.join("\n")
- end
-
- def normalize(message)
- message.reject { |_key, value| value&.empty? }
- end
-
- def http_options
- {
- open_timeout: 10,
- read_timeout: 3_600,
- use_ssl: uri.scheme == "https"
- }
- end
-
- def build_uri(raw_host)
- if raw_host =~ %r{^https?://}
- host = raw_host
- else
- # No scheme โ decide which one to add.
- # * localhost or 127.0.0.1 โ http
- # * anything else โ https
- scheme = raw_host.start_with?("localhost", "127.0.0.1") ? "http://" : "https://"
- host = scheme + raw_host
+ def normalize_ollama_response(chunk)
+ if chunk["done"]
+ finish_reason = chunk["done_reason"] || "stop"
+ return { "done" => true, "finish_reason" => finish_reason }
end
- URI("#{host.sub(%r{/?$}, "")}/v1/chat/completions")
- end
+ message = chunk["message"]
+ return nil unless message
- def build_request(messages)
- Net::HTTP::Post.new(uri).tap do |request|
- request["Accept"] = "application/json"
- request["Content-Type"] = "application/json"
- request["Authorization"] = "Bearer #{configuration.token}" if configuration.token
- request.body = build_payload(messages).to_json
- end
- end
+ result = {}
+ result["role"] = message["role"] if message["role"]
+ result["content"] = message["content"] if message["content"]
+ result["reasoning"] = message["thinking"] if message["thinking"]
+ result["tool_calls"] = message["tool_calls"] if message["tool_calls"]
- def build_payload(messages)
- {
- messages: messages,
- model: configuration.model,
- stream: true,
- temperature: 0.1,
- tools: configuration.tools.to_h
- }.tap do |payload|
- configuration.logger.debug(JSON.pretty_generate(payload)) if configuration.debug
- end
+ result.empty? ? nil : result
end
end
end
lib/elelem.rb
@@ -5,12 +5,11 @@ require "erb"
require "json"
require "json-schema"
require "logger"
-require "net/http"
+require "net/llm"
require "open3"
require "reline"
require "thor"
require "timeout"
-require "uri"
require_relative "elelem/agent"
require_relative "elelem/api"
elelem.gemspec
@@ -70,10 +70,9 @@ Gem::Specification.new do |spec|
spec.add_dependency "json"
spec.add_dependency "json-schema"
spec.add_dependency "logger"
- spec.add_dependency "net-http"
+ spec.add_dependency "net-llm"
spec.add_dependency "open3"
spec.add_dependency "reline"
spec.add_dependency "thor"
spec.add_dependency "timeout"
- spec.add_dependency "uri"
end
Gemfile.lock
@@ -7,12 +7,11 @@ PATH
json
json-schema
logger
- net-http
+ net-llm
open3
reline
thor
timeout
- uri
GEM
remote: https://rubygems.org/
@@ -20,6 +19,7 @@ GEM
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3)
+ base64 (0.3.0)
bigdecimal (3.2.2)
cli-ui (2.4.0)
date (3.4.1)
@@ -37,9 +37,20 @@ GEM
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
logger (1.7.0)
+ net-hippie (1.4.0)
+ base64 (~> 0.1)
+ json (~> 2.0)
+ logger (~> 1.0)
+ net-http (~> 0.6)
+ openssl (~> 3.0)
net-http (0.6.0)
uri
+ net-llm (0.4.0)
+ json (~> 2.0)
+ net-hippie (~> 1.0)
+ uri (~> 1.0)
open3 (0.2.1)
+ openssl (3.3.1)
parallel (1.27.0)
parser (3.3.9.0)
ast (~> 2.4.1)