smollama 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f9f331024c63381cad8781105f25f9c86f8d1745f89ee4ec6ce230e65646500b
4
+ data.tar.gz: 14b790b0158ac0cd54a9520e37d68eab471cbc5951e9a38274bbc3da51d9df93
5
+ SHA512:
6
+ metadata.gz: 7f9e09789dcabfc5282332fda7742f7a054547e9c0df57b349282db5ab7d4dba7ead148da52b0a5345aa921b94d07d611feec48f34e20672473933776c6cfff0
7
+ data.tar.gz: e81e862f8534d92d9b929dfe6ee0901b3f39030cb55610572d4a2dbf1f6f5e4c29a90866b9fd26c74226b494795a612ce6df5adf4fb49458d806ab3eb9b9706b
data/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # SmolLama
2
+
3
+ A simple, lightweight Ruby client for the Ollama API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'smollama'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install smollama
20
+
21
+ ## Usage
22
+
23
+ ### Basic Configuration
24
+
25
+ Configure the client at application startup:
26
+
27
+ ```ruby
28
+ require 'smollama'
29
+
30
+ Smollama::Client.configure do |config|
31
+ config.server_ip = '127.0.0.1' # 192.168.0.x or similar if you're running Ollama in a box in your LAN
32
+ config.server_port = 11434 # optional, defaults to 11434
33
+ config.default_model = 'gpt-oss'
34
+ end
35
+ ```
36
+
37
+ ### Simple Chat
38
+
39
+ ```ruby
40
+ client = Smollama::Client.new
41
+
42
+ response = client.ask("Hello, how are you?")
43
+ puts response[:content]
44
+ ```
45
+
46
+ ### Chat with Parameters
47
+
48
+ ```ruby
49
+ response = client.chat(
50
+ "Explain quantum computing",
51
+ temperature: 0.6, # NOTE: use 0.2 for coding tasks
52
+ top_p: 0.98,
53
+ max_tokens: 500
54
+ )
55
+ puts response[:content]
56
+ ```
57
+
58
+ ### Streaming Responses
59
+
60
+ ```ruby
61
+ client.chat("Tell me a story", stream: true) do |chunk|
62
+ print chunk['message']['content'] if chunk['message']
63
+ end
64
+ ```
65
+
66
+ ### Chat with Conversation History
67
+
68
+ ```ruby
69
+ messages = [
70
+ { role: 'system', content: 'You are a helpful assistant.' },
71
+ { role: 'user', content: 'What is Ruby?' },
72
+ { role: 'assistant', content: 'Ruby is a dynamic programming language.' },
73
+ { role: 'user', content: 'What makes it special?' }
74
+ ]
75
+
76
+ response = client.chat_with_history(messages, temperature: 0.8)
77
+ puts response[:content]
78
+ ```
79
+
80
+ ### Using Different Models
81
+
82
+ ```ruby
83
+ # Use a different model for a specific client
84
+ special_client = Smollama::Client.new(model: 'llama2')
85
+ response = special_client.ask("Hello!")
86
+ ```
87
+
88
+ ### Server Health Check
89
+
90
+ ```ruby
91
+ if client.ping
92
+ puts "Ollama server is reachable"
93
+ else
94
+ puts "Cannot reach Ollama server"
95
+ end
96
+ ```
97
+
98
+ ### List Available Models
99
+
100
+ ```ruby
101
+ models = client.list_models
102
+ puts "Available models: #{models['models'].map { |m| m['name'] }.join(', ')}"
103
+ ```
104
+
105
+ ## Configuration Options
106
+
107
+ - `server_ip`: The IP address of your Ollama server (required)
108
+ - `server_port`: The port number (optional, defaults to 11434)
109
+ - `default_model`: The default model to use for all clients
110
+
111
+ ## Chat Parameters
112
+
113
+ - `temperature`: Controls randomness (0.0 to 1.0)
114
+ - `top_p`: Controls nucleus sampling (0.0 to 1.0)
115
+ - `max_tokens`: Maximum number of tokens to generate
116
+ - `stream`: Enable streaming responses (boolean)
117
+
118
+ ## Response Format
119
+
120
+ Non-streaming responses return a hash with:
121
+
122
+ - `:content` - The generated text
123
+ - `:model` - Model used
124
+ - `:created_at` - Timestamp
125
+ - `:total_duration` - Total processing time
126
+ - `:eval_count` - Number of tokens evaluated
127
+ - `:eval_duration` - Evaluation time
128
+
129
+ ## Error Handling
130
+
131
+ The client gracefully handles errors and returns error information in the response:
132
+
133
+ ```ruby
134
+ response = client.ask("Hello")
135
+ if response[:error]
136
+ puts "Error: #{response[:error]}"
137
+ else
138
+ puts response[:content]
139
+ end
140
+ ```
141
+
142
+ ## Development
143
+
144
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
145
+
146
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
147
+
148
+ ## Contributing
149
+
150
+ Bug reports and pull requests are welcome on GitHub at https://github.com/makevoid/smollama.
151
+
152
+ ## License
153
+
154
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ desc "Build and publish gem to RubyGems"
9
+ task :publish do
10
+ puts "Building gem..."
11
+ system("gem build smollama.gemspec")
12
+
13
+ gem_file = Dir["smollama-*.gem"].first
14
+ if gem_file
15
+ puts "Publishing #{gem_file} to RubyGems..."
16
+ system("gem push #{gem_file}")
17
+
18
+ puts "Cleaning up..."
19
+ File.delete(gem_file)
20
+ else
21
+ puts "No gem file found to publish!"
22
+ end
23
+ end
24
+
25
+ desc "Build gem without publishing"
26
+ task :build do
27
+ system("gem build smollama.gemspec")
28
+ end
29
+
30
+ desc "Install gem locally"
31
+ task :install => :build do
32
+ gem_file = Dir["smollama-*.gem"].first
33
+ if gem_file
34
+ system("gem install #{gem_file}")
35
+ File.delete(gem_file)
36
+ end
37
+ end
@@ -0,0 +1,189 @@
1
+ require 'excon'
2
+ require 'json'
3
+
4
+ module Smollama
5
+ class Client
6
+ # Class-level configuration
7
+ class << self
8
+ attr_accessor :server_ip, :server_port, :default_model
9
+
10
+ def configure
11
+ yield self if block_given?
12
+ end
13
+
14
+ def base_url
15
+ raise "Server IP not configured" unless server_ip
16
+ port = server_port || 11434
17
+ "http://#{server_ip}:#{port}"
18
+ end
19
+ end
20
+
21
+ # Initialize with optional overrides
22
+ def initialize(model: nil)
23
+ @model = model || self.class.default_model
24
+ raise "Model not specified" unless @model
25
+
26
+ @connection = Excon.new(
27
+ "#{self.class.base_url}/api/chat",
28
+ persistent: true,
29
+ headers: {
30
+ 'Content-Type' => 'application/json'
31
+ }
32
+ )
33
+ end
34
+
35
+ # Main chat method with configurable parameters
36
+ def chat(message, temperature: nil, top_p: nil, max_tokens: nil, stream: false)
37
+ messages = build_messages(message)
38
+
39
+ payload = {
40
+ model: @model,
41
+ messages: messages,
42
+ stream: stream
43
+ }
44
+
45
+ # Add optional parameters if provided
46
+ payload[:options] = {} if temperature || top_p
47
+ payload[:options][:temperature] = temperature if temperature
48
+ payload[:options][:top_p] = top_p if top_p
49
+ payload[:options][:num_predict] = max_tokens if max_tokens
50
+
51
+ if stream
52
+ stream_response(payload) { |chunk| yield chunk if block_given? }
53
+ else
54
+ send_request(payload)
55
+ end
56
+ end
57
+
58
+ # Convenience method for single message chat
59
+ def ask(prompt, **options)
60
+ chat(prompt, **options)
61
+ end
62
+
63
+ # Chat with conversation history
64
+ def chat_with_history(messages, **options)
65
+ raise "Messages must be an array" unless messages.is_a?(Array)
66
+
67
+ payload = {
68
+ model: @model,
69
+ messages: messages,
70
+ stream: options[:stream] || false
71
+ }
72
+
73
+ # Add optional parameters
74
+ payload[:options] = {}
75
+ payload[:options][:temperature] = options[:temperature] if options[:temperature]
76
+ payload[:options][:top_p] = options[:top_p] if options[:top_p]
77
+ payload[:options][:num_predict] = options[:max_tokens] if options[:max_tokens]
78
+
79
+ if payload[:stream]
80
+ stream_response(payload) { |chunk| yield chunk if block_given? }
81
+ else
82
+ send_request(payload)
83
+ end
84
+ end
85
+
86
+ # Get available models
87
+ def list_models
88
+ response = Excon.get(
89
+ "#{self.class.base_url}/api/tags",
90
+ headers: { 'Content-Type' => 'application/json' }
91
+ )
92
+
93
+ JSON.parse(response.body)
94
+ rescue Excon::Error => e
95
+ { error: "Failed to list models: #{e.message}" }
96
+ end
97
+
98
+ # Check if server is reachable
99
+ def ping
100
+ response = Excon.get("#{self.class.base_url}/")
101
+ response.status == 200
102
+ rescue Excon::Error
103
+ false
104
+ end
105
+
106
+ private
107
+
108
+ def build_messages(input)
109
+ case input
110
+ when String
111
+ [{ role: 'user', content: input }]
112
+ when Hash
113
+ [input]
114
+ when Array
115
+ input
116
+ else
117
+ raise "Invalid message format"
118
+ end
119
+ end
120
+
121
+ def send_request(payload)
122
+ response = @connection.post(
123
+ body: payload.to_json,
124
+ read_timeout: 120,
125
+ write_timeout: 120
126
+ )
127
+
128
+ parse_response(response)
129
+ rescue Excon::Error::Timeout => e
130
+ { error: "Request timeout: #{e.message}" }
131
+ rescue Excon::Error => e
132
+ { error: "Request failed: #{e.message}" }
133
+ end
134
+
135
+ def stream_response(payload)
136
+ buffer = ""
137
+
138
+ @connection.post(
139
+ body: payload.to_json,
140
+ read_timeout: 120,
141
+ write_timeout: 120,
142
+ response_block: lambda do |chunk, remaining_bytes, total_bytes|
143
+ buffer += chunk
144
+
145
+ # Process complete JSON objects from buffer
146
+ while (line_end = buffer.index("\n"))
147
+ line = buffer[0...line_end]
148
+ buffer = buffer[(line_end + 1)..-1]
149
+
150
+ next if line.strip.empty?
151
+
152
+ begin
153
+ data = JSON.parse(line)
154
+ yield data if block_given?
155
+ rescue JSON::ParserError => e
156
+ puts "Failed to parse JSON: #{e.message}"
157
+ end
158
+ end
159
+ end
160
+ )
161
+
162
+ { status: 'stream_complete' }
163
+ rescue Excon::Error => e
164
+ { error: "Stream failed: #{e.message}" }
165
+ end
166
+
167
+ def parse_response(response)
168
+ return { error: "Empty response" } if response.body.nil? || response.body.empty?
169
+
170
+ data = JSON.parse(response.body)
171
+
172
+ # Extract the assistant's message content
173
+ if data['message']
174
+ {
175
+ content: data['message']['content'],
176
+ model: data['model'],
177
+ created_at: data['created_at'],
178
+ total_duration: data['total_duration'],
179
+ eval_count: data['eval_count'],
180
+ eval_duration: data['eval_duration']
181
+ }
182
+ else
183
+ data
184
+ end
185
+ rescue JSON::ParserError => e
186
+ { error: "Failed to parse response: #{e.message}", raw: response.body }
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,3 @@
1
+ module Smollama
2
+ VERSION = "0.1.0"
3
+ end
data/lib/smollama.rb ADDED
@@ -0,0 +1,5 @@
1
+ require_relative "smollama/version"
2
+ require_relative "smollama/client"
3
+
4
+ module Smollama
5
+ end
data/smollama.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ require_relative "lib/smollama/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "smollama"
5
+ spec.version = Smollama::VERSION
6
+ spec.authors = ["makevoid"]
7
+ spec.email = ["makevoid@example.com"]
8
+
9
+ spec.summary = "A simple Ruby client for Ollama API"
10
+ spec.description = "SmolLama is a lightweight Ruby client for interacting with the Ollama API. It provides a simple interface for chat completions, streaming responses, and model management."
11
+ spec.homepage = "https://github.com/makevoid/smollama"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = ">= 3.0.0"
14
+
15
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/makevoid/smollama"
18
+ spec.metadata["changelog_uri"] = "https://github.com/makevoid/smollama/blob/main/CHANGELOG.md"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ spec.files = Dir["{lib}/**/*", "*.md", "*.gemspec", "Rakefile"]
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ # Runtime dependencies
27
+ spec.add_dependency "excon", "~> 0.100"
28
+
29
+ # Development dependencies
30
+ spec.add_development_dependency "bundler", "~> 2.0"
31
+ spec.add_development_dependency "rake", "~> 13.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smollama
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - makevoid
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-08-29 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: excon
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.100'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.100'
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ description: SmolLama is a lightweight Ruby client for interacting with the Ollama
69
+ API. It provides a simple interface for chat completions, streaming responses, and
70
+ model management.
71
+ email:
72
+ - makevoid@example.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - README.md
78
+ - Rakefile
79
+ - lib/smollama.rb
80
+ - lib/smollama/client.rb
81
+ - lib/smollama/version.rb
82
+ - smollama.gemspec
83
+ homepage: https://github.com/makevoid/smollama
84
+ licenses:
85
+ - MIT
86
+ metadata:
87
+ allowed_push_host: https://rubygems.org
88
+ homepage_uri: https://github.com/makevoid/smollama
89
+ source_code_uri: https://github.com/makevoid/smollama
90
+ changelog_uri: https://github.com/makevoid/smollama/blob/main/CHANGELOG.md
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 3.0.0
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.6.2
106
+ specification_version: 4
107
+ summary: A simple Ruby client for Ollama API
108
+ test_files: []