askcii 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: 18a37a3681323f04071f337d1b0e8f7b0351fe2879de62224951d4b4c8c7d8d4
4
+ data.tar.gz: 3e994f5ea755fed88a74fd07be374a412825c8e2227cc048f5c2d2dc219d2b5b
5
+ SHA512:
6
+ metadata.gz: 9c7c23cc79eb32819c310774894800f6f77cde9fa8466ac4f5674ebeaa626b006f39a487a8d99b8324b9e152a2f8393ecea0c61a9c1e822148c63732c4e9ea64
7
+ data.tar.gz: e10d8a1726d612e6aeab85aaf717d48d6616eac31dcd744b56a761fad2e0ce71c1f8ad4e2e574590817e1c6a1b12f87c772793a72d1747e69095c9868547e548
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ **/.claude/settings.local.json
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ askcii (0.1.0)
5
+ amalgalite (~> 1.9)
6
+ ruby_llm (= 1.3.0rc1)
7
+ sequel (~> 5.92)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ amalgalite (1.9.4)
13
+ arrayfields (~> 4.9)
14
+ arrayfields (4.9.2)
15
+ base64 (0.2.0)
16
+ bigdecimal (3.1.9)
17
+ event_stream_parser (1.0.0)
18
+ faraday (2.13.1)
19
+ faraday-net_http (>= 2.0, < 3.5)
20
+ json
21
+ logger
22
+ faraday-multipart (1.1.0)
23
+ multipart-post (~> 2.0)
24
+ faraday-net_http (3.4.0)
25
+ net-http (>= 0.5.0)
26
+ faraday-retry (2.3.1)
27
+ faraday (~> 2.0)
28
+ json (2.12.0)
29
+ logger (1.7.0)
30
+ minitest (5.25.5)
31
+ multipart-post (2.4.1)
32
+ net-http (0.6.0)
33
+ uri
34
+ rake (13.2.1)
35
+ ruby_llm (1.3.0rc1)
36
+ base64
37
+ event_stream_parser (~> 1)
38
+ faraday (~> 2)
39
+ faraday-multipart (~> 1)
40
+ faraday-net_http (~> 3)
41
+ faraday-retry (~> 2)
42
+ zeitwerk (~> 2)
43
+ sequel (5.92.0)
44
+ bigdecimal
45
+ uri (1.0.3)
46
+ zeitwerk (2.7.2)
47
+
48
+ PLATFORMS
49
+ aarch64-linux-gnu
50
+ aarch64-linux-musl
51
+ arm-linux-gnu
52
+ arm-linux-musl
53
+ arm64-darwin
54
+ x86-linux-gnu
55
+ x86-linux-musl
56
+ x86_64-darwin
57
+ x86_64-linux-gnu
58
+ x86_64-linux-musl
59
+
60
+ DEPENDENCIES
61
+ askcii!
62
+ minitest (~> 5.25)
63
+ rake (~> 13.0)
64
+
65
+ BUNDLED WITH
66
+ 2.6.8
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Roel Bondoc
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Askcii
2
+
3
+ A command-line application for interacting with LLM models in a terminal-friendly way.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'askcii'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install askcii
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ # Basic usage
25
+ askcii 'Your prompt here'
26
+
27
+ # Pipe input
28
+ echo 'Your context text' | askcii 'Your prompt here'
29
+
30
+ # File input
31
+ askcii 'Your prompt here' < input.txt
32
+
33
+ # Set a custom session ID to maintain conversation context
34
+ ASKCII_SESSION_ID="project-research" askcii 'What did we talk about earlier?'
35
+
36
+ # Or add the following to your .bashrc or .zshrc to always start a new session
37
+ export ASKCII_SESSION=$(openssl rand -hex 16)
38
+
39
+ # Configure the API key, endpoint, and model ID
40
+ askcii -c
41
+
42
+ # Get the last response
43
+ askcii -r
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ You can configure your API key, endpoint, and model ID using the `-c` option:
49
+
50
+ ```
51
+ $ askcii -c
52
+ Configuring askcii...
53
+ Enter API key: your_api_key_here
54
+ Enter API endpoint: http://localhost:11434/v1
55
+ Enter model ID: gemma3:12b
56
+ Configuration saved successfully!
57
+ ```
58
+
59
+ Configuration settings are stored in a SQLite database located at `~/.local/share/askcii/askcii.db`.
60
+
61
+ You can also use environment variables to override the stored configuration:
62
+
63
+ ```
64
+ ASKCII_API_KEY="your_api_key" askcii 'Hello!'
65
+ ASKCII_API_ENDPOINT="https://api.example.com/v1" askcii 'Hello!'
66
+ ASKCII_MODEL_ID="gpt-4" askcii 'Hello!'
67
+ ```
68
+
69
+ ## Development
70
+
71
+ 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.
72
+
73
+ 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
74
+
75
+ ## License
76
+
77
+ 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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/askcii.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/askcii/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'askcii'
7
+ spec.version = Askcii::VERSION
8
+ spec.authors = ['Roel Bondoc']
9
+ spec.email = ['roelbondoc@example.com']
10
+
11
+ spec.summary = 'Command line application for LLM interactions'
12
+ spec.description = 'A terminal-friendly interface for interacting with LLM models'
13
+ spec.homepage = 'https://github.com/roelbondoc/askcii'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = spec.homepage
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'bin'
26
+ spec.executables = ['askcii']
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'sequel', '~> 5.92'
30
+ spec.add_dependency 'amalgalite', '~> 1.9'
31
+ spec.add_dependency 'ruby_llm', '1.3.0rc1'
32
+
33
+ spec.add_development_dependency 'minitest', '~> 5.25'
34
+ spec.add_development_dependency 'rake', '~> 13.0'
35
+ end
data/bin/askcii ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Find the right load path
5
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
6
+ require 'askcii'
7
+ require 'optparse'
8
+
9
+ Askcii.setup_database
10
+ Askcii.configure_llm
11
+ Askcii.require_models
12
+
13
+ # Parse command-line options
14
+ options = {}
15
+ opt_parser = OptionParser.new do |opts|
16
+ opts.banner = "Usage: askcii [options] 'Your prompt here'"
17
+
18
+ opts.on('-r', '--last-response', 'Output the last response') do
19
+ options[:last_response] = true
20
+ end
21
+
22
+ opts.on('-c', '--configure', 'Configure API key, endpoint, and model ID') do
23
+ options[:configure] = true
24
+ end
25
+
26
+ opts.on('-h', '--help', 'Show this help message') do
27
+ puts opts
28
+ exit
29
+ end
30
+ end
31
+
32
+ # Parse options, keeping remaining arguments in ARGV
33
+ opt_parser.parse!
34
+
35
+ # Handle configuration if requested
36
+ if options[:configure]
37
+ puts 'Configuring askcii...'
38
+
39
+ # Prompt for API key
40
+ print 'Enter API key: '
41
+ api_key = gets.chomp
42
+
43
+ # Prompt for API endpoint
44
+ print "Enter API endpoint: "
45
+ api_endpoint = gets.chomp
46
+
47
+ # Prompt for model ID
48
+ print 'Enter model ID: '
49
+ model_id = gets.chomp
50
+
51
+ # Save configuration to database
52
+ Askcii::Config.set('api_key', api_key) unless api_key.empty?
53
+ Askcii::Config.set('api_endpoint', api_endpoint) unless api_endpoint.empty?
54
+ Askcii::Config.set('model_id', model_id) unless model_id.empty?
55
+
56
+ puts 'Configuration saved successfully!'
57
+ exit 0
58
+ end
59
+
60
+ context = ENV['ASKCII_SESSION']
61
+ model_id = Askcii::Config.model_id || ENV['ASKCII_MODEL_ID']
62
+ chat = Askcii::Chat.find_or_create(context: context, model_id: model_id).to_llm
63
+
64
+ # Output last response if requested
65
+ if options[:last_response]
66
+ last_message = chat.messages.where(role: 'assistant').last
67
+ if last_message
68
+ puts last_message.content
69
+ exit 0
70
+ else
71
+ puts 'No previous response found.'
72
+ exit 1
73
+ end
74
+ end
75
+
76
+ # Process input
77
+ input = nil
78
+ input = $stdin.read unless $stdin.tty?
79
+
80
+ prompt = ARGV.join(' ')
81
+
82
+ if prompt.empty?
83
+ puts 'Usage:'
84
+ puts " askcii [options] 'Your prompt here'"
85
+ puts " echo 'Your prompt here' | askcii 'Your prompt here'"
86
+ puts " askcii 'Your prompt here' < prompt.txt"
87
+ puts ' askcii -r (to get the last response)'
88
+ puts ' askcii -c (to configure API key, endpoint, and model ID)'
89
+ puts "\nOptions:"
90
+ puts ' -r, --last-response Output the last response'
91
+ puts ' -c, --configure Configure API key, endpoint, and model ID'
92
+ puts ' -h, --help Show help'
93
+ exit 1
94
+ end
95
+
96
+ chat.with_instructions 'You are a command line application. Your responses should be suitable to be read in a terminal. Your responses should only include the necessary text. Do not include any explanations unless prompted for it.'
97
+ prompt = "With the following text:\n\n#{input}\n\n#{prompt}" if input
98
+
99
+ chat.ask(prompt) do |chunk|
100
+ print chunk.content
101
+ end
102
+ puts ''
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'askcii'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require 'irb'
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Askcii
4
+ class Chat < Sequel::Model(Askcii.database[:chats])
5
+ one_to_many :messages, class: 'Askcii::Message', key: :chat_id
6
+
7
+ def to_llm
8
+ @chat = RubyLLM.chat(
9
+ model: model_id,
10
+ provider: :openai,
11
+ assume_model_exists: true
12
+ )
13
+ messages.each do |msg|
14
+ @chat.add_message(msg.to_llm)
15
+ end
16
+ @chat.on_new_message { persist_new_message }
17
+ @chat.on_end_message { |msg| persist_message_completion(msg) }
18
+ @chat
19
+ end
20
+
21
+ def persist_new_message
22
+ @message = add_message(
23
+ role: :assistant,
24
+ content: String.new
25
+ )
26
+ end
27
+
28
+ def persist_message_completion(message)
29
+ return unless message
30
+
31
+ @message.update(
32
+ role: message.role,
33
+ content: message.content,
34
+ model_id: message.model_id,
35
+ input_tokens: message.input_tokens,
36
+ output_tokens: message.output_tokens
37
+ )
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Askcii
4
+ class Config < Sequel::Model(Askcii.database[:configs])
5
+ def self.set(key, value)
6
+ config = find_or_create(key: key)
7
+ config.update(value: value)
8
+ end
9
+
10
+ def self.get(key)
11
+ config = find(key: key)
12
+ config ? config.value : nil
13
+ end
14
+
15
+ def self.api_key
16
+ get('api_key')
17
+ end
18
+
19
+ def self.api_endpoint
20
+ get('api_endpoint')
21
+ end
22
+
23
+ def self.model_id
24
+ get('model_id')
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Askcii
4
+ class Message < Sequel::Model(Askcii.database[:messages])
5
+ many_to_one :chat, class: 'Askcii::Chat', key: :chat_id
6
+
7
+ def to_llm
8
+ RubyLLM::Message.new(
9
+ role: role.to_sym,
10
+ content: content,
11
+ tool_calls: {},
12
+ tool_call_id: nil,
13
+ input_tokens: input_tokens,
14
+ output_tokens: output_tokens,
15
+ model_id: model_id
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Askcii
4
+ VERSION = '0.1.0'
5
+ end
data/lib/askcii.rb ADDED
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sequel'
4
+ require 'fileutils'
5
+ require 'ruby_llm'
6
+ require 'ruby_llm/model_info'
7
+ require_relative './askcii/version'
8
+
9
+ module Askcii
10
+ class Error < StandardError; end
11
+
12
+ def self.database
13
+ @@database ||= Sequel.amalgalite(db_path)
14
+ end
15
+
16
+ # Get the path to the database file
17
+ def self.db_path
18
+ db_dir = File.join(ENV['HOME'], '.local', 'share', 'askcii')
19
+ FileUtils.mkdir_p(db_dir) unless Dir.exist?(db_dir)
20
+ File.join(db_dir, 'askcii.db')
21
+ end
22
+
23
+ # Initialize the database
24
+ def self.setup_database
25
+ database.create_table :chats do
26
+ primary_key :id
27
+ String :model_id, null: true
28
+ String :context, null: true
29
+ Datetime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
30
+ end unless database.table_exists?(:chats)
31
+
32
+ database.create_table :messages do
33
+ primary_key :id
34
+ foreign_key :chat_id, :chats, null: false
35
+ String :role, null: true
36
+ Text :content, null: true
37
+ String :model_id, null: true
38
+ Integer :input_tokens, null: true
39
+ Integer :output_tokens, null: true
40
+ Datetime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
41
+ end unless database.table_exists?(:messages)
42
+
43
+ database.create_table :configs do
44
+ primary_key :id
45
+ String :key, null: false, unique: true
46
+ Text :value, null: true
47
+ end unless database.table_exists?(:configs)
48
+ end
49
+
50
+ def self.configure_llm
51
+ RubyLLM.configure do |config|
52
+ config.log_file = '/dev/null'
53
+
54
+ # Try to get configuration from the database first, then fallback to ENV variables
55
+ config.openai_api_key = begin
56
+ Askcii::Config.api_key || ENV['ASKCII_API_KEY'] || 'blank'
57
+ rescue StandardError
58
+ ENV['ASKCII_API_KEY'] || 'blank'
59
+ end
60
+
61
+ config.openai_api_base = begin
62
+ Askcii::Config.api_endpoint || ENV['ASKCII_API_ENDPOINT'] || 'http://localhost:11434/v1'
63
+ rescue StandardError
64
+ ENV['ASKCII_API_ENDPOINT'] || 'http://localhost:11434/v1'
65
+ end
66
+ end
67
+ end
68
+
69
+ def self.require_models
70
+ require_relative './askcii/models/chat'
71
+ require_relative './askcii/models/message'
72
+ require_relative './askcii/models/config'
73
+ end
74
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: askcii
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Roel Bondoc
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: sequel
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.92'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.92'
26
+ - !ruby/object:Gem::Dependency
27
+ name: amalgalite
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.9'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.9'
40
+ - !ruby/object:Gem::Dependency
41
+ name: ruby_llm
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 1.3.0rc1
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.3.0rc1
54
+ - !ruby/object:Gem::Dependency
55
+ name: minitest
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '5.25'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '5.25'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ description: A terminal-friendly interface for interacting with LLM models
83
+ email:
84
+ - roelbondoc@example.com
85
+ executables:
86
+ - askcii
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - askcii.gemspec
97
+ - bin/askcii
98
+ - bin/console
99
+ - bin/setup
100
+ - lib/askcii.rb
101
+ - lib/askcii/models/chat.rb
102
+ - lib/askcii/models/config.rb
103
+ - lib/askcii/models/message.rb
104
+ - lib/askcii/version.rb
105
+ homepage: https://github.com/roelbondoc/askcii
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ homepage_uri: https://github.com/roelbondoc/askcii
110
+ source_code_uri: https://github.com/roelbondoc/askcii
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 2.6.0
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubygems_version: 3.6.8
126
+ specification_version: 4
127
+ summary: Command line application for LLM interactions
128
+ test_files: []