├── .editorconfig ├── .github └── workflows │ ├── documentation-coverage.yaml │ ├── documentation.yaml │ ├── rubocop.yaml │ ├── test-coverage.yaml │ └── test.yaml ├── .gitignore ├── .rubocop.yml ├── async-ollama.gemspec ├── bake └── async │ └── ollama.rb ├── config └── sus.rb ├── examples ├── chat.rb └── conversation.rb ├── gems.rb ├── guides ├── getting-started │ └── readme.md └── links.yaml ├── lib └── async │ ├── ollama.rb │ └── ollama │ ├── client.rb │ ├── generate.rb │ ├── models.rb │ ├── version.rb │ └── wrapper.rb ├── license.md ├── readme.md ├── release.cert └── test └── async ├── ollama.rb └── ollama └── client.rb /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | 7 | [*.{yml,yaml}] 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /.github/workflows/documentation-coverage.yaml: -------------------------------------------------------------------------------- 1 | name: Documentation Coverage 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | CONSOLE_OUTPUT: XTerm 10 | COVERAGE: PartialSummary 11 | 12 | jobs: 13 | validate: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: ruby/setup-ruby@v1 19 | with: 20 | ruby-version: "3.3" 21 | bundler-cache: true 22 | 23 | - name: Validate coverage 24 | timeout-minutes: 5 25 | run: bundle exec bake decode:index:coverage lib 26 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yaml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages: 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | # Allow one concurrent deployment: 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: true 18 | 19 | env: 20 | CONSOLE_OUTPUT: XTerm 21 | BUNDLE_WITH: maintenance 22 | 23 | jobs: 24 | generate: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - uses: ruby/setup-ruby@v1 31 | with: 32 | ruby-version: "3.3" 33 | bundler-cache: true 34 | 35 | - name: Installing packages 36 | run: sudo apt-get install wget 37 | 38 | - name: Generate documentation 39 | timeout-minutes: 5 40 | run: bundle exec bake utopia:project:static --force no 41 | 42 | - name: Upload documentation artifact 43 | uses: actions/upload-pages-artifact@v3 44 | with: 45 | path: docs 46 | 47 | deploy: 48 | runs-on: ubuntu-latest 49 | 50 | environment: 51 | name: github-pages 52 | url: ${{steps.deployment.outputs.page_url}} 53 | 54 | needs: generate 55 | steps: 56 | - name: Deploy to GitHub Pages 57 | id: deployment 58 | uses: actions/deploy-pages@v4 59 | -------------------------------------------------------------------------------- /.github/workflows/rubocop.yaml: -------------------------------------------------------------------------------- 1 | name: RuboCop 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | CONSOLE_OUTPUT: XTerm 10 | 11 | jobs: 12 | check: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: ruby 20 | bundler-cache: true 21 | 22 | - name: Run RuboCop 23 | timeout-minutes: 10 24 | run: bundle exec rubocop 25 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | name: Test Coverage 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | CONSOLE_OUTPUT: XTerm 10 | COVERAGE: PartialSummary 11 | 12 | jobs: 13 | test: 14 | name: ${{matrix.ruby}} on ${{matrix.os}} 15 | runs-on: ${{matrix.os}}-latest 16 | 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu 21 | 22 | ruby: 23 | - "3.3" 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{matrix.ruby}} 30 | bundler-cache: true 31 | 32 | - name: Install ollama 33 | run: | 34 | curl -fsSL https://ollama.com/install.sh | sh 35 | sudo systemctl status ollama 36 | ollama pull llama3 37 | 38 | - name: Run tests 39 | timeout-minutes: 5 40 | run: bundle exec bake test 41 | 42 | - uses: actions/upload-artifact@v4 43 | with: 44 | include-hidden-files: true 45 | if-no-files-found: error 46 | name: coverage-${{matrix.os}}-${{matrix.ruby}} 47 | path: .covered.db 48 | 49 | validate: 50 | needs: test 51 | runs-on: ubuntu-latest 52 | 53 | steps: 54 | - uses: actions/checkout@v4 55 | - uses: ruby/setup-ruby@v1 56 | with: 57 | ruby-version: "3.3" 58 | bundler-cache: true 59 | 60 | - uses: actions/download-artifact@v4 61 | 62 | - name: Validate coverage 63 | timeout-minutes: 5 64 | run: bundle exec bake covered:validate --paths */.covered.db \; 65 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | CONSOLE_OUTPUT: XTerm 10 | 11 | jobs: 12 | test: 13 | name: ${{matrix.ruby}} on ${{matrix.os}} 14 | runs-on: ${{matrix.os}}-latest 15 | continue-on-error: ${{matrix.experimental}} 16 | 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu 21 | 22 | ruby: 23 | - "3.1" 24 | - "3.2" 25 | - "3.3" 26 | 27 | experimental: [false] 28 | 29 | include: 30 | - os: ubuntu 31 | ruby: truffleruby 32 | experimental: true 33 | - os: ubuntu 34 | ruby: jruby 35 | experimental: true 36 | - os: ubuntu 37 | ruby: head 38 | experimental: true 39 | 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: ruby/setup-ruby@v1 43 | with: 44 | ruby-version: ${{matrix.ruby}} 45 | bundler-cache: true 46 | 47 | - name: Install ollama 48 | run: | 49 | curl -fsSL https://ollama.com/install.sh | sh 50 | sudo systemctl status ollama 51 | ollama pull llama3 52 | 53 | - name: Run tests 54 | timeout-minutes: 10 55 | run: bundle exec bake test 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /pkg/ 3 | /gems.locked 4 | /.covered.db 5 | /external 6 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | DisabledByDefault: true 3 | 4 | Layout/IndentationStyle: 5 | Enabled: true 6 | EnforcedStyle: tabs 7 | 8 | Layout/InitialIndentation: 9 | Enabled: true 10 | 11 | Layout/IndentationWidth: 12 | Enabled: true 13 | Width: 1 14 | 15 | Layout/IndentationConsistency: 16 | Enabled: true 17 | EnforcedStyle: normal 18 | 19 | Layout/BlockAlignment: 20 | Enabled: true 21 | 22 | Layout/EndAlignment: 23 | Enabled: true 24 | EnforcedStyleAlignWith: start_of_line 25 | 26 | Layout/BeginEndAlignment: 27 | Enabled: true 28 | EnforcedStyleAlignWith: start_of_line 29 | 30 | Layout/ElseAlignment: 31 | Enabled: true 32 | 33 | Layout/DefEndAlignment: 34 | Enabled: true 35 | 36 | Layout/CaseIndentation: 37 | Enabled: true 38 | 39 | Layout/CommentIndentation: 40 | Enabled: true 41 | 42 | Layout/EmptyLinesAroundClassBody: 43 | Enabled: true 44 | 45 | Layout/EmptyLinesAroundModuleBody: 46 | Enabled: true 47 | 48 | Style/FrozenStringLiteralComment: 49 | Enabled: true 50 | 51 | Style/StringLiterals: 52 | Enabled: true 53 | EnforcedStyle: double_quotes 54 | -------------------------------------------------------------------------------- /async-ollama.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/async/ollama/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "async-ollama" 7 | spec.version = Async::Ollama::VERSION 8 | 9 | spec.summary = "A asynchronous interface to the ollama chat service" 10 | spec.authors = ["Samuel Williams"] 11 | spec.license = "MIT" 12 | 13 | spec.cert_chain = ["release.cert"] 14 | spec.signing_key = File.expand_path("~/.gem/release.pem") 15 | 16 | spec.homepage = "https://github.com/socketry/async-ollama" 17 | 18 | spec.metadata = { 19 | "documentation_uri" => "https://socketry.github.io/async-ollama/", 20 | "source_code_uri" => "https://github.com/socketry/async-ollama.git", 21 | } 22 | 23 | spec.files = Dir.glob(["{lib}/**/*", "*.md"], File::FNM_DOTMATCH, base: __dir__) 24 | 25 | spec.required_ruby_version = ">= 3.1" 26 | 27 | spec.add_dependency "async" 28 | spec.add_dependency "async-rest", "~> 0.17" 29 | end 30 | -------------------------------------------------------------------------------- /bake/async/ollama.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | def initialize(...) 7 | super 8 | 9 | require "async/ollama/client" 10 | end 11 | 12 | def models 13 | Async::Ollama::Client.open do |client| 14 | client.models.names 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /config/sus.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require "covered/sus" 7 | include Covered::Sus 8 | -------------------------------------------------------------------------------- /examples/chat.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # Released under the MIT License. 5 | # Copyright, 2024, by Samuel Williams. 6 | 7 | require_relative "lib/async/ollama" 8 | 9 | require "console" 10 | 11 | terminal = Console::Terminal.for($stdout) 12 | terminal[:reply] = terminal.style(:blue) 13 | terminal[:reset] = terminal.reset 14 | 15 | Async::Ollama::Client.open do |client| 16 | generator = client 17 | 18 | while input = $stdin.gets 19 | generator = generator.generate(input) do |response| 20 | terminal.write terminal[:reply] 21 | 22 | response.body.each do |token| 23 | terminal.write token 24 | end 25 | 26 | terminal.puts terminal[:reset] 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /examples/conversation.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # Released under the MIT License. 5 | # Copyright, 2024, by Samuel Williams. 6 | 7 | require_relative "../lib/async/ollama" 8 | 9 | require "console" 10 | 11 | class Entity 12 | def initialize(client, name, background) 13 | @name = name 14 | @background = background 15 | 16 | @generator = client 17 | end 18 | 19 | attr :name 20 | attr :background 21 | 22 | def broadcast(room, from, input) 23 | Async do 24 | if from 25 | prompt = "#{from.name} says: #{input}" 26 | @generator = @generator.generate(prompt) 27 | else 28 | @generator = @generator.generate(input) 29 | end 30 | 31 | output = @generator.response 32 | 33 | if from 34 | $stderr.puts "*** Entity #{@name} responds to #{from.name} *** #{output}\n\n\n" 35 | else 36 | $stderr.puts "*** Entity #{@name} responds *** #{output}\n\n\n" 37 | end 38 | 39 | sleep(rand(30..60)) 40 | 41 | room.broadcast(self, output, from) 42 | end 43 | end 44 | end 45 | 46 | class Room 47 | SYSTEM = "You are role playing as a character. You will receive textual input from other players and must respond according to your background. Stay in character at all times. Keep your response short and to the point." 48 | 49 | def initialize 50 | @entities = [] 51 | end 52 | 53 | def add(entity) 54 | @entities << entity 55 | 56 | prompt = "#{SYSTEM}\nYour name is #{entity.name} and you are: #{entity.background}" 57 | 58 | entity.broadcast(self, nil, prompt) 59 | end 60 | 61 | def broadcast(entity, input, response_to = nil) 62 | @entities.each do |target| 63 | unless entity.equal?(target) 64 | target.broadcast(self, entity, input) 65 | end 66 | end 67 | end 68 | end 69 | 70 | Async::Ollama::Client.open do |client| 71 | alice = Entity.new(client, "Alice", "A young elegant lady whom is obsessed with computers.") 72 | bob = Entity.new(client, "Bob", "A middle age man having an identity crisis.") 73 | cat = Entity.new(client, "Cat", "A cat that is hungry.") 74 | 75 | room = Room.new 76 | room.add(alice) 77 | room.add(bob) 78 | room.add(cat) 79 | end 80 | -------------------------------------------------------------------------------- /gems.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | source "https://rubygems.org" 7 | 8 | gemspec 9 | 10 | group :maintenance, optional: true do 11 | gem "bake-gem" 12 | gem "bake-modernize" 13 | 14 | gem "utopia-project" 15 | end 16 | 17 | group :test do 18 | gem "sus" 19 | gem "covered" 20 | gem "decode" 21 | gem "rubocop" 22 | 23 | gem "sus-fixtures-async" 24 | 25 | gem "bake-test" 26 | gem "bake-test-external" 27 | end 28 | -------------------------------------------------------------------------------- /guides/getting-started/readme.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | This guide explains how to get started with the `async-ollama` gem. 4 | 5 | ## Installation 6 | 7 | Add the gem to your project: 8 | 9 | ~~~ bash 10 | $ bundle add async-ollama 11 | ~~~ 12 | 13 | ### Ollama 14 | 15 | You'll also need to install and start Ollama. You can find instructions for doing so at [https://ollama.com](https://ollama.com). 16 | 17 | ## Core Concepts 18 | 19 | - The {ruby Async::Ollama::Client} is used for interacting with the Ollama server. 20 | - {ruby Async::Ollama::Generate} represents a single conversation response (with context) from the Ollama server. 21 | 22 | ## Example 23 | 24 | ~~~ ruby 25 | require 'async/ollama' 26 | 27 | Async::Ollama::Client.open do |client| 28 | generator = client.generate("Can you please tell me the first 10 digits of PI?") 29 | puts generator.response 30 | # Of course! The first 10 digits of pi are: 31 | # 32 | # 3.141592653 33 | end 34 | ~~~ 35 | 36 | ### Using a Specific Model 37 | 38 | You can specify a model to use for generating responses: 39 | 40 | ~~~ ruby 41 | require 'async/ollama' 42 | 43 | Async::Ollama::Client.open do |client| 44 | generator = client.generate("Can you please tell me the first 10 digits of PI?", model: "llama3") 45 | puts generator.response 46 | # The first 10 digits of Pi (π) are: 47 | # 48 | # 3.141592653 49 | # 50 | # Let me know if you'd like more! 51 | end 52 | ~~~ 53 | -------------------------------------------------------------------------------- /guides/links.yaml: -------------------------------------------------------------------------------- 1 | getting-started: 2 | order: 0 3 | -------------------------------------------------------------------------------- /lib/async/ollama.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require_relative "ollama/version" 7 | require_relative "ollama/client" 8 | -------------------------------------------------------------------------------- /lib/async/ollama/client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require "async/rest/resource" 7 | 8 | require_relative "generate" 9 | require_relative "models" 10 | 11 | module Async 12 | module Ollama 13 | # Represents a connection to the Ollama service. 14 | class Client < Async::REST::Resource 15 | # The default endpoint to connect to. 16 | ENDPOINT = Async::HTTP::Endpoint.parse("http://localhost:11434") 17 | 18 | # Generate a response from the given prompt. 19 | # @parameter prompt [String] The prompt to generate a response from. 20 | def generate(prompt, **options, &block) 21 | options[:prompt] = prompt 22 | options[:model] ||= "llama3" 23 | 24 | Generate.post(self.with(path: "/api/generate"), options) do |resource, response| 25 | if block_given? 26 | yield response 27 | end 28 | 29 | Generate.new(resource, value: response.read, metadata: response.headers) 30 | end 31 | end 32 | 33 | def models 34 | Models.get(self.with(path: "/api/tags")) 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/async/ollama/generate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require "async/rest/representation" 7 | require_relative "wrapper" 8 | 9 | module Async 10 | module Ollama 11 | class Generate < Async::REST::Representation[Wrapper] 12 | # The response to the prompt. 13 | def response 14 | self.value[:response] 15 | end 16 | 17 | # The conversation context. Used to maintain state between prompts. 18 | def context 19 | self.value[:context] 20 | end 21 | 22 | # The model used to generate the response. 23 | def model 24 | self.value[:model] 25 | end 26 | 27 | # Generate a new response from the given prompt. 28 | # @parameter prompt [String] The prompt to generate a response from. 29 | # @yields {|response| ...} Optional streaming response. 30 | def generate(prompt, &block) 31 | self.class.post(self.resource, prompt: prompt, context: self.context, model: self.model, &block) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/async/ollama/models.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require "async/rest/representation" 7 | require_relative "wrapper" 8 | 9 | module Async 10 | module Ollama 11 | class Models < Async::REST::Representation[Wrapper] 12 | def names 13 | self.value[:models].map{|model| model[:name]} 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/async/ollama/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | module Async 7 | module Ollama 8 | VERSION = "0.4.0" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/async/ollama/wrapper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require "async/rest/wrapper/generic" 7 | require "async/rest/wrapper/json" 8 | 9 | require "protocol/http/body/wrapper" 10 | require "protocol/http/body/buffered" 11 | 12 | require "json" 13 | 14 | module Async 15 | module Ollama 16 | class Wrapper < Async::REST::Wrapper::Generic 17 | APPLICATION_JSON = "application/json" 18 | APPLICATION_JSON_STREAM = "application/x-ndjson" 19 | 20 | def prepare_request(request, payload) 21 | request.headers.add("accept", APPLICATION_JSON) 22 | request.headers.add("accept", APPLICATION_JSON_STREAM) 23 | 24 | if payload 25 | request.headers["content-type"] = APPLICATION_JSON 26 | 27 | request.body = ::Protocol::HTTP::Body::Buffered.new([ 28 | ::JSON.dump(payload) 29 | ]) 30 | end 31 | end 32 | 33 | class StreamingResponseParser < ::Protocol::HTTP::Body::Wrapper 34 | def initialize(...) 35 | super 36 | 37 | @buffer = String.new.b 38 | @offset = 0 39 | 40 | @response = String.new 41 | @value = {response: @response} 42 | end 43 | 44 | def read 45 | return if @buffer.nil? 46 | 47 | while true 48 | if index = @buffer.index("\n", @offset) 49 | line = @buffer.byteslice(@offset, index - @offset) 50 | @buffer = @buffer.byteslice(index + 1, @buffer.bytesize - index - 1) 51 | @offset = 0 52 | 53 | return ::JSON.parse(line, symbolize_names: true) 54 | end 55 | 56 | if chunk = super 57 | @buffer << chunk 58 | else 59 | return nil if @buffer.empty? 60 | 61 | line = @buffer 62 | @buffer = nil 63 | @offset = 0 64 | 65 | return ::JSON.parse(line, symbolize_names: true) 66 | end 67 | end 68 | end 69 | 70 | def each 71 | super do |line| 72 | token = line.delete(:response) 73 | @response << token 74 | @value.merge!(line) 75 | 76 | yield token 77 | end 78 | end 79 | 80 | def join 81 | self.each{} 82 | 83 | return @value 84 | end 85 | end 86 | 87 | def parser_for(response) 88 | content_type = response.headers["content-type"] 89 | media_type = content_type.split(";").first 90 | 91 | case media_type 92 | when APPLICATION_JSON 93 | return Async::REST::Wrapper::JSON::Parser 94 | when APPLICATION_JSON_STREAM 95 | return StreamingResponseParser 96 | end 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright, 2024, by Samuel Williams. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Async::Ollama 2 | 3 | Provides an interface for accessing the Ollama HTTP interface. 4 | 5 | [![Development Status](https://github.com/socketry/async-ollama/workflows/Test/badge.svg)](https://github.com/socketry/async-ollama/actions?workflow=Test) 6 | 7 | ## Usage 8 | 9 | Please see the [project documentation](https://socketry.github.io/async-ollama/) for more details. 10 | 11 | - [Getting Started](https://socketry.github.io/async-ollama/guides/getting-started/index) - This guide explains how to get started with the `async-ollama` gem. 12 | 13 | ## Contributing 14 | 15 | We welcome contributions to this project. 16 | 17 | 1. Fork it. 18 | 2. Create your feature branch (`git checkout -b my-new-feature`). 19 | 3. Commit your changes (`git commit -am 'Add some feature'`). 20 | 4. Push to the branch (`git push origin my-new-feature`). 21 | 5. Create new Pull Request. 22 | 23 | ### Developer Certificate of Origin 24 | 25 | In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed. 26 | 27 | ### Community Guidelines 28 | 29 | This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers. 30 | -------------------------------------------------------------------------------- /release.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11 3 | ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK 4 | CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz 5 | MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd 6 | MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj 7 | bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB 8 | igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2 9 | 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW 10 | sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE 11 | e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN 12 | XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss 13 | RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn 14 | tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM 15 | zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW 16 | xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O 17 | BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs 18 | aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs 19 | aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE 20 | cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl 21 | xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/ 22 | c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp 23 | 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws 24 | JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP 25 | eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt 26 | Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8 27 | voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg= 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /test/async/ollama.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require "async/ollama" 7 | 8 | describe Async::Ollama do 9 | it "should have a version number" do 10 | expect(Async::Ollama::VERSION).to be =~ /\d+\.\d+\.\d+/ 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/async/ollama/client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require "async/ollama" 7 | require "sus/fixtures/async/reactor_context" 8 | 9 | describe Async::Ollama::Client do 10 | include Sus::Fixtures::Async::ReactorContext 11 | 12 | attr :client 13 | 14 | before do 15 | @client = Async::Ollama::Client.open 16 | end 17 | 18 | after do 19 | @client.close 20 | end 21 | 22 | it "can connect to the default endpoint" do 23 | generate = client.generate("Hello, this is a unit test. Please respond with the following text: Hello.") 24 | 25 | expect(generate.response).to be =~ /Hello/ 26 | end 27 | end 28 | --------------------------------------------------------------------------------