Class: Ask::Providers::Ollama
- Inherits:
-
Ask::Provider
- Object
- Ask::Provider
- Ask::Providers::Ollama
- Includes:
- LLM::ProviderConfig
- Defined in:
- lib/ask/provider/ollama.rb
Overview
Ollama provider for local LLM inference. Connects to a local Ollama server (default: http://localhost:11434).
Class Method Summary collapse
- .capabilities ⇒ Object
- .configuration_options ⇒ Object
- .configuration_requirements ⇒ Object
- .local? ⇒ Boolean
- .slug ⇒ Object
Instance Method Summary collapse
- #api_base ⇒ Object
-
#build_request(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params) ⇒ Object
--- Config transformation contract ---.
- #chat(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params, &block) ⇒ Object
- #embed(texts, model:) ⇒ Object
- #format_message(msg) ⇒ Object
- #format_tools(tools) ⇒ Object
- #headers ⇒ Object
-
#initialize(config = {}) ⇒ Ollama
constructor
A new instance of Ollama.
- #list_models ⇒ Object
- #parse_response(body, model) ⇒ Object
- #parse_stream(raw, stream, model, &block) ⇒ Object
Constructor Details
#initialize(config = {}) ⇒ Ollama
Returns a new instance of Ollama.
10 11 12 13 14 |
# File 'lib/ask/provider/ollama.rb', line 10 def initialize(config = {}) config = normalize_config(config) super(config) @http = build_http end |
Class Method Details
.capabilities ⇒ Object
52 53 54 |
# File 'lib/ask/provider/ollama.rb', line 52 def capabilities { chat: true, streaming: true, tool_calls: true, embed: true, local: true } end |
.configuration_options ⇒ Object
56 |
# File 'lib/ask/provider/ollama.rb', line 56 def ; %i[api_base]; end |
.configuration_requirements ⇒ Object
57 |
# File 'lib/ask/provider/ollama.rb', line 57 def configuration_requirements; %i[]; end |
.local? ⇒ Boolean
58 |
# File 'lib/ask/provider/ollama.rb', line 58 def local?; true; end |
.slug ⇒ Object
50 |
# File 'lib/ask/provider/ollama.rb', line 50 def slug; "ollama"; end |
Instance Method Details
#api_base ⇒ Object
16 17 18 |
# File 'lib/ask/provider/ollama.rb', line 16 def api_base @config.api_base || "http://localhost:11434" end |
#build_request(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params) ⇒ Object
--- Config transformation contract ---
63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/ask/provider/ollama.rb', line 63 def build_request(, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params) payload = { model:, messages: .map { |m| (m) }, stream: stream || false, options: {} } payload[:options][:temperature] = temperature if temperature tool_defs = format_tools(tools) if tools&.any? payload[:tools] = tool_defs if tool_defs payload.merge(params) end |
#chat(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params, &block) ⇒ Object
24 25 26 27 28 29 30 31 32 |
# File 'lib/ask/provider/ollama.rb', line 24 def chat(, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params, &block) msgs = .is_a?(Ask::Conversation) ? .to_a : payload = build_request(msgs, model:, tools:, temperature:, stream:, schema:, **params) if stream chat_stream(payload, model, &block) else chat_nonstream(payload, model) end end |
#embed(texts, model:) ⇒ Object
34 35 36 37 38 39 40 |
# File 'lib/ask/provider/ollama.rb', line 34 def (texts, model:) texts = Array(texts) response = @http.post("api/embeddings") { |r| r.body = { model:, prompt: texts.first } } raise LLM::HTTP.map_error(response.status, response.body, provider: "Ollama") unless response.success? Ask::Result.success(response.body["embedding"]) end |
#format_message(msg) ⇒ Object
111 112 113 |
# File 'lib/ask/provider/ollama.rb', line 111 def (msg) { role: (msg[:role] || msg["role"]).to_s, content: msg[:content] || msg["content"] } end |
#format_tools(tools) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/ask/provider/ollama.rb', line 115 def format_tools(tools) tools.map do |t| { type: "function", function: { name: t.respond_to?(:name) ? t.name : t[:name], description: t.respond_to?(:description) ? t.description : t[:description], parameters: t.respond_to?(:parameters) ? t.parameters : (t[:parameters] || {}) } } end end |
#headers ⇒ Object
20 21 22 |
# File 'lib/ask/provider/ollama.rb', line 20 def headers { "Content-Type" => "application/json" } end |
#list_models ⇒ Object
42 43 44 45 46 47 |
# File 'lib/ask/provider/ollama.rb', line 42 def list_models response = @http.get("api/tags") return [] unless response.success? response.body["models"].map { |m| Ask::ModelInfo.new(id: m["name"], provider: slug) } end |
#parse_response(body, model) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/ask/provider/ollama.rb', line 76 def parse_response(body, model) msg = body["message"] || {} Ask::Message.new( role: :assistant, content: msg["content"], metadata: { model: body["model"] || model, done: body["done"], total_duration: body["total_duration"], raw: body } ) end |
#parse_stream(raw, stream, model, &block) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/ask/provider/ollama.rb', line 90 def parse_stream(raw, stream, model, &block) @_sse_buffer ||= +"" @_sse_buffer << raw while (line_end = @_sse_buffer.index("\n")) line = @_sse_buffer.slice!(0, line_end + 1).strip next if line.empty? parsed = JSON.parse(line) rescue next msg = parsed["message"] || {} chunk = Ask::Chunk.new(content: msg["content"]) stream.add(chunk) yield chunk if block_given? if parsed["done"] chunk = Ask::Chunk.new(finish_reason: "stop", usage: { total_duration: parsed["total_duration"] }) stream.add(chunk) yield chunk if block_given? end end end |