ollama_chat 0.0.20 → 0.0.22
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 +4 -4
- data/CHANGES.md +16 -0
- data/README.md +8 -0
- data/VERSION +1 -1
- data/bin/ollama_chat_send +3 -2
- data/lib/ollama_chat/chat.rb +176 -8
- data/lib/ollama_chat/dialog.rb +8 -5
- data/lib/ollama_chat/follow_chat.rb +36 -0
- data/lib/ollama_chat/information.rb +1 -0
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +1 -0
- data/lib/ollama_chat/server_socket.rb +36 -15
- data/lib/ollama_chat/source_fetching.rb +91 -1
- data/lib/ollama_chat/switches.rb +79 -16
- data/lib/ollama_chat/utils/cache_fetcher.rb +30 -0
- data/lib/ollama_chat/utils/fetcher.rb +117 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat/vim.rb +53 -0
- data/lib/ollama_chat/web_searching.rb +35 -1
- data/lib/ollama_chat.rb +1 -0
- data/ollama_chat.gemspec +6 -6
- data/spec/assets/api_tags.json +17 -0
- data/spec/ollama_chat/chat_spec.rb +1 -1
- data/spec/ollama_chat/server_socket_spec.rb +133 -0
- data/spec/ollama_chat/switches_spec.rb +9 -14
- data/spec/spec_helper.rb +2 -6
- metadata +7 -3
@@ -1,4 +1,13 @@
|
|
1
1
|
module OllamaChat::SourceFetching
|
2
|
+
# The http_options method prepares HTTP options for requests based on
|
3
|
+
# configuration settings.
|
4
|
+
# It determines whether SSL peer verification should be disabled for a given
|
5
|
+
# URL and whether a proxy should be used, then returns a hash of options.
|
6
|
+
#
|
7
|
+
# @param url [ String ] the URL for which HTTP options are being prepared
|
8
|
+
#
|
9
|
+
# @return [ Hash ] a hash containing HTTP options such as ssl_verify_peer and
|
10
|
+
# proxy settings
|
2
11
|
def http_options(url)
|
3
12
|
options = {}
|
4
13
|
if ssl_no_verify = config.ssl_no_verify?
|
@@ -11,6 +20,13 @@ module OllamaChat::SourceFetching
|
|
11
20
|
options
|
12
21
|
end
|
13
22
|
|
23
|
+
# The fetch_source method retrieves content from various source types
|
24
|
+
# including commands, URLs, and file paths. It processes the source based on
|
25
|
+
# its type and yields a temporary file handle for further processing.
|
26
|
+
#
|
27
|
+
# @param source [ String ] the source identifier which can be a command, URL, or file path
|
28
|
+
#
|
29
|
+
# @yield [ tmp ]
|
14
30
|
def fetch_source(source, &block)
|
15
31
|
case source
|
16
32
|
when %r(\A!(.*))
|
@@ -36,18 +52,38 @@ module OllamaChat::SourceFetching
|
|
36
52
|
block.(tmp)
|
37
53
|
end
|
38
54
|
else
|
39
|
-
raise "invalid source"
|
55
|
+
raise "invalid source #{source.inspect}"
|
40
56
|
end
|
41
57
|
rescue => e
|
42
58
|
STDERR.puts "Cannot fetch source #{source.to_s.inspect}: #{e.class} #{e}\n#{e.backtrace * ?\n}"
|
43
59
|
end
|
44
60
|
|
61
|
+
|
62
|
+
# Adds an image to the images collection from the given source IO and source
|
63
|
+
# identifier.
|
64
|
+
#
|
65
|
+
# This method takes an IO object containing image data and associates it with
|
66
|
+
# a source, creating an Ollama::Image instance and adding it to the images
|
67
|
+
# array.
|
68
|
+
#
|
69
|
+
# @param images [Array] The collection of images to which the new image will be added
|
70
|
+
# @param source_io [IO] The input stream containing the image data
|
71
|
+
# @param source [String, #to_s] The identifier or path for the source of the image
|
45
72
|
def add_image(images, source_io, source)
|
46
73
|
STDERR.puts "Adding #{source_io&.content_type} image #{source.to_s.inspect}."
|
47
74
|
image = Ollama::Image.for_io(source_io, path: source.to_s)
|
48
75
|
(images << image).uniq!
|
49
76
|
end
|
50
77
|
|
78
|
+
# The import_source method processes and imports content from a given source,
|
79
|
+
# displaying information about the document type and returning a formatted
|
80
|
+
# string that indicates the import result along with the parsed content.
|
81
|
+
#
|
82
|
+
# @param source_io [ IO ] the input stream containing the document content
|
83
|
+
# @param source [ String ] the source identifier or path
|
84
|
+
#
|
85
|
+
# @return [ String ] a formatted message indicating the import result and the
|
86
|
+
# parsed content
|
51
87
|
def import_source(source_io, source)
|
52
88
|
source = source.to_s
|
53
89
|
document_type = source_io&.content_type.full? { |ct| italic { ct } + ' ' }
|
@@ -56,6 +92,16 @@ module OllamaChat::SourceFetching
|
|
56
92
|
"Imported #{source.inspect}:\n\n#{source_content}\n\n"
|
57
93
|
end
|
58
94
|
|
95
|
+
# Imports content from the specified source and processes it.
|
96
|
+
#
|
97
|
+
# This method fetches content from a given source (command, URL, or file) and
|
98
|
+
# passes the resulting IO object to the import_source method for processing.
|
99
|
+
#
|
100
|
+
# @param source [String] The source identifier which can be a command, URL,
|
101
|
+
# or file path
|
102
|
+
#
|
103
|
+
# @return [String, nil] A formatted message indicating the import result and
|
104
|
+
# parsed content, # or nil if the operation fails
|
59
105
|
def import(source)
|
60
106
|
fetch_source(source) do |source_io|
|
61
107
|
content = import_source(source_io, source) or return
|
@@ -64,6 +110,16 @@ module OllamaChat::SourceFetching
|
|
64
110
|
end
|
65
111
|
end
|
66
112
|
|
113
|
+
|
114
|
+
# Summarizes content from the given source IO and source identifier.
|
115
|
+
#
|
116
|
+
# This method takes an IO object containing document content and generates a
|
117
|
+
# summary based on the configured prompt template and word count.
|
118
|
+
#
|
119
|
+
# @param source_io [IO] The input stream containing the document content to summarize
|
120
|
+
# @param source [String, #to_s] The identifier or path for the source of the content
|
121
|
+
# @param words [Integer, nil] The target number of words for the summary (defaults to 100)
|
122
|
+
# @return [String, nil] The formatted summary message or nil if content is empty or cannot be processed
|
67
123
|
def summarize_source(source_io, source, words: nil)
|
68
124
|
STDOUT.puts "Summarizing #{italic { source_io&.content_type }} document #{source.to_s.inspect} now."
|
69
125
|
words = words.to_i
|
@@ -73,6 +129,15 @@ module OllamaChat::SourceFetching
|
|
73
129
|
config.prompts.summarize % { source_content:, words: }
|
74
130
|
end
|
75
131
|
|
132
|
+
|
133
|
+
# Summarizes content from the specified source.
|
134
|
+
#
|
135
|
+
# This method fetches content from a given source (command, URL, or file) and
|
136
|
+
# generates a summary using the summarize_source method.
|
137
|
+
#
|
138
|
+
# @param source [String] The source identifier which can be a command, URL, or file path
|
139
|
+
# @param words [Integer, nil] The target number of words for the summary (defaults to 100)
|
140
|
+
# @return [String, nil] The formatted summary message or nil if the operation fails
|
76
141
|
def summarize(source, words: nil)
|
77
142
|
fetch_source(source) do |source_io|
|
78
143
|
content = summarize_source(source_io, source, words:) or return
|
@@ -81,6 +146,19 @@ module OllamaChat::SourceFetching
|
|
81
146
|
end
|
82
147
|
end
|
83
148
|
|
149
|
+
|
150
|
+
# Embeds content from the given source IO and source identifier.
|
151
|
+
#
|
152
|
+
# This method processes document content by splitting it into chunks using
|
153
|
+
# various splitting strategies (Character, RecursiveCharacter, Semantic) and
|
154
|
+
# adds the chunks to a document store for embedding.
|
155
|
+
#
|
156
|
+
# @param source_io [IO] The input stream containing the document content to embed
|
157
|
+
# @param source [String, #to_s] The identifier or path for the source of the content
|
158
|
+
# @param count [Integer, nil] An optional counter for tracking processing order
|
159
|
+
#
|
160
|
+
# @return [Array, String, nil] The embedded chunks or processed content, or
|
161
|
+
# nil if embedding is disabled or fails
|
84
162
|
def embed_source(source_io, source, count: nil)
|
85
163
|
@embedding.on? or return parse_source(source_io)
|
86
164
|
m = "Embedding #{italic { source_io&.content_type }} document #{source.to_s.inspect}."
|
@@ -127,6 +205,18 @@ module OllamaChat::SourceFetching
|
|
127
205
|
@documents.add(inputs, source:, batch_size: config.embedding.batch_size?)
|
128
206
|
end
|
129
207
|
|
208
|
+
|
209
|
+
# Embeds content from the specified source.
|
210
|
+
#
|
211
|
+
# This method fetches content from a given source (command, URL, or file) and
|
212
|
+
# processes it for embedding using the embed_source method. If embedding is
|
213
|
+
# disabled, it falls back to generating a summary instead.
|
214
|
+
#
|
215
|
+
# @param source [String] The source identifier which can be a command, URL,
|
216
|
+
# or file path
|
217
|
+
#
|
218
|
+
# @return [String, nil] The formatted embedding result or summary message, or
|
219
|
+
# nil if the operation fails
|
130
220
|
def embed(source)
|
131
221
|
if @embedding.on?
|
132
222
|
STDOUT.puts "Now embedding #{source.to_s.inspect}."
|
data/lib/ollama_chat/switches.rb
CHANGED
@@ -6,28 +6,56 @@ module OllamaChat::Switches
|
|
6
6
|
alias_method :on?, :value
|
7
7
|
end
|
8
8
|
|
9
|
+
# The off? method returns true if the switch is in the off state, false
|
10
|
+
# otherwise.
|
11
|
+
#
|
12
|
+
# @return [ TrueClass, FalseClass ] indicating whether the switch is off
|
9
13
|
def off?
|
10
14
|
!on?
|
11
15
|
end
|
12
16
|
|
17
|
+
# The show method outputs the current value of the message to standard
|
18
|
+
# output.
|
19
|
+
#
|
20
|
+
# @return [ void ]
|
13
21
|
def show
|
14
22
|
STDOUT.puts @msg[value]
|
15
23
|
end
|
16
24
|
end
|
17
25
|
|
18
26
|
class Switch
|
19
|
-
|
20
|
-
|
27
|
+
# The initialize method sets up the switch with a default value and
|
28
|
+
# message.
|
29
|
+
#
|
30
|
+
# @param msg [ Hash ] a hash containing true and false messages
|
31
|
+
# @param value [ Object ] the default state of the switch
|
32
|
+
#
|
33
|
+
# @return [ void ]
|
34
|
+
def initialize(msg:, value:)
|
35
|
+
@value = !!value
|
21
36
|
@msg = msg
|
22
37
|
end
|
23
38
|
|
39
|
+
# The value reader returns the current value of the attribute.
|
24
40
|
attr_reader :value
|
25
41
|
|
42
|
+
# The set method assigns a boolean value to the instance variable @value
|
43
|
+
# and optionally displays it.
|
44
|
+
#
|
45
|
+
# @param value [ Object ] the value to be converted to a boolean and
|
46
|
+
# assigned
|
47
|
+
# @param show [ TrueClass, FalseClass ] determines whether to display the
|
48
|
+
# value after setting
|
26
49
|
def set(value, show: false)
|
27
50
|
@value = !!value
|
28
51
|
show && self.show
|
29
52
|
end
|
30
53
|
|
54
|
+
# The toggle method switches the current value of the instance variable and
|
55
|
+
# optionally displays it.
|
56
|
+
#
|
57
|
+
# @param show [ TrueClass, FalseClass ] determines whether to show the
|
58
|
+
# value after toggling
|
31
59
|
def toggle(show: true)
|
32
60
|
@value = !@value
|
33
61
|
show && self.show
|
@@ -37,11 +65,17 @@ module OllamaChat::Switches
|
|
37
65
|
end
|
38
66
|
|
39
67
|
class CombinedSwitch
|
68
|
+
# The initialize method sets up the switch with a value and message.
|
69
|
+
#
|
70
|
+
# @param value [ Object ] the value to be stored
|
71
|
+
# @param msg [ Hash ] the message hash containing true and false keys
|
40
72
|
def initialize(value:, msg:)
|
41
73
|
@value = value
|
42
74
|
@msg = msg
|
43
75
|
end
|
44
76
|
|
77
|
+
# The value method returns the result of calling the stored proc with no
|
78
|
+
# arguments.
|
45
79
|
def value
|
46
80
|
@value.()
|
47
81
|
end
|
@@ -49,26 +83,61 @@ module OllamaChat::Switches
|
|
49
83
|
include CheckSwitch
|
50
84
|
end
|
51
85
|
|
86
|
+
# The think method returns the current state of the stream switch.
|
87
|
+
#
|
88
|
+
# @return [ OllamaChat::Switches::Switch ] the stream switch instance
|
52
89
|
attr_reader :stream
|
53
90
|
|
91
|
+
# The think method returns the current state of the thinking switch.
|
92
|
+
#
|
93
|
+
# @return [ OllamaChat::Switches::Switch ] the thinking switch instance
|
54
94
|
attr_reader :think
|
55
95
|
|
96
|
+
# The markdown attribute reader returns the markdown switch object.
|
97
|
+
# The voice reader returns the voice switch instance.
|
98
|
+
#
|
99
|
+
# @return [ OllamaChat::Switches::Switch ] the markdown switch instance
|
56
100
|
attr_reader :markdown
|
57
101
|
|
102
|
+
# The voice reader returns the voice switch instance.
|
103
|
+
#
|
104
|
+
# @return [ OllamaChat::Switches::Switch ] the voice switch instance
|
58
105
|
attr_reader :voice
|
59
106
|
|
107
|
+
# The embedding attribute reader returns the embedding switch object.
|
108
|
+
#
|
109
|
+
# @return [ OllamaChat::Switches::CombinedSwitch ] the embedding switch
|
110
|
+
# instance
|
60
111
|
attr_reader :embedding
|
61
112
|
|
113
|
+
# The embedding_enabled reader returns the embedding enabled switch instance.
|
114
|
+
#
|
115
|
+
# @return [ OllamaChat::Switches::Switch ] the embedding enabled switch
|
116
|
+
# instance
|
62
117
|
attr_reader :embedding_enabled
|
63
118
|
|
119
|
+
# The embedding_paused method returns the current state of the embedding pause flag.
|
120
|
+
#
|
121
|
+
# @return [ OllamaChat::Switches::Switch ] the embedding pause flag switch instance
|
64
122
|
attr_reader :embedding_paused
|
65
123
|
|
124
|
+
# The location method returns the current location setting.
|
125
|
+
#
|
126
|
+
# @return [ OllamaChat::Switches::Switch ] the location setting object
|
66
127
|
attr_reader :location
|
67
128
|
|
129
|
+
# The setup_switches method initializes various switches for configuring the
|
130
|
+
# application's behavior.
|
131
|
+
#
|
132
|
+
# This method creates and configures multiple switch objects that control
|
133
|
+
# different aspects of the application, such as streaming, thinking, markdown
|
134
|
+
# output, voice output, embedding, and location settings.
|
135
|
+
#
|
136
|
+
# @param config [ ComplexConfig::Settings ] the configuration object
|
137
|
+
# containing settings for the switches
|
68
138
|
def setup_switches(config)
|
69
139
|
@stream = Switch.new(
|
70
|
-
:stream,
|
71
|
-
config:,
|
140
|
+
value: config.stream,
|
72
141
|
msg: {
|
73
142
|
true => "Streaming enabled.",
|
74
143
|
false => "Streaming disabled.",
|
@@ -76,8 +145,7 @@ module OllamaChat::Switches
|
|
76
145
|
)
|
77
146
|
|
78
147
|
@think = Switch.new(
|
79
|
-
:think,
|
80
|
-
config:,
|
148
|
+
value: config.think,
|
81
149
|
msg: {
|
82
150
|
true => "Thinking enabled.",
|
83
151
|
false => "Thinking disabled.",
|
@@ -85,8 +153,7 @@ module OllamaChat::Switches
|
|
85
153
|
)
|
86
154
|
|
87
155
|
@markdown = Switch.new(
|
88
|
-
:markdown,
|
89
|
-
config:,
|
156
|
+
value: config.markdown,
|
90
157
|
msg: {
|
91
158
|
true => "Using #{italic{'ANSI'}} markdown to output content.",
|
92
159
|
false => "Using plaintext for outputting content.",
|
@@ -94,8 +161,7 @@ module OllamaChat::Switches
|
|
94
161
|
)
|
95
162
|
|
96
163
|
@voice = Switch.new(
|
97
|
-
:
|
98
|
-
config: config.voice,
|
164
|
+
value: config.voice.enabled,
|
99
165
|
msg: {
|
100
166
|
true => "Voice output enabled.",
|
101
167
|
false => "Voice output disabled.",
|
@@ -103,8 +169,7 @@ module OllamaChat::Switches
|
|
103
169
|
)
|
104
170
|
|
105
171
|
@embedding_enabled = Switch.new(
|
106
|
-
:
|
107
|
-
config:,
|
172
|
+
value: config.embedding.enabled,
|
108
173
|
msg: {
|
109
174
|
true => "Embedding enabled.",
|
110
175
|
false => "Embedding disabled.",
|
@@ -112,8 +177,7 @@ module OllamaChat::Switches
|
|
112
177
|
)
|
113
178
|
|
114
179
|
@embedding_paused = Switch.new(
|
115
|
-
:
|
116
|
-
config:,
|
180
|
+
value: config.embedding.paused,
|
117
181
|
msg: {
|
118
182
|
true => "Embedding paused.",
|
119
183
|
false => "Embedding resumed.",
|
@@ -129,8 +193,7 @@ module OllamaChat::Switches
|
|
129
193
|
)
|
130
194
|
|
131
195
|
@location = Switch.new(
|
132
|
-
:location,
|
133
|
-
config: config.location.enabled,
|
196
|
+
value: config.location.enabled,
|
134
197
|
msg: {
|
135
198
|
true => "Location and localtime enabled.",
|
136
199
|
false => "Location and localtime disabled.",
|
@@ -1,10 +1,24 @@
|
|
1
1
|
require 'digest/md5'
|
2
2
|
|
3
3
|
class OllamaChat::Utils::CacheFetcher
|
4
|
+
# The initialize method sets up the cache instance variable for the object.
|
5
|
+
#
|
6
|
+
# @param cache [ Object ] the cache object to be stored
|
7
|
+
#
|
8
|
+
# @return [ void ]
|
4
9
|
def initialize(cache)
|
5
10
|
@cache = cache
|
6
11
|
end
|
7
12
|
|
13
|
+
# The get method retrieves cached content by key and yields it as an IO object.
|
14
|
+
# It first checks if the body and content type are present in the cache.
|
15
|
+
# If both are found, it creates a StringIO object from the body,
|
16
|
+
# extends it with HeaderExtension, sets the content type,
|
17
|
+
# and then yields the IO object to the provided block.
|
18
|
+
#
|
19
|
+
# @param url [ String ] the URL used as a key for caching
|
20
|
+
#
|
21
|
+
# @yield [ io ] yields the cached IO object if found
|
8
22
|
def get(url, &block)
|
9
23
|
block or raise ArgumentError, 'require block argument'
|
10
24
|
body = @cache[key(:body, url)]
|
@@ -19,6 +33,13 @@ class OllamaChat::Utils::CacheFetcher
|
|
19
33
|
end
|
20
34
|
end
|
21
35
|
|
36
|
+
# The put method stores the body and content type of an IO object in the
|
37
|
+
# cache using a URL-based key.
|
38
|
+
#
|
39
|
+
# @param url [ String ] the URL used to generate the cache key
|
40
|
+
# @param io [ StringIO, Tempfile ] the IO object containing the body and content type
|
41
|
+
#
|
42
|
+
# @return [ CacheFetcher ] returns itself to allow for method chaining
|
22
43
|
def put(url, io)
|
23
44
|
io.rewind
|
24
45
|
body = io.read
|
@@ -32,6 +53,15 @@ class OllamaChat::Utils::CacheFetcher
|
|
32
53
|
|
33
54
|
private
|
34
55
|
|
56
|
+
# The key method generates a unique identifier by combining a type prefix
|
57
|
+
# with a URL digest.
|
58
|
+
# It returns a string that consists of the type, a hyphen, and the MD5 hash
|
59
|
+
# of the URL.
|
60
|
+
#
|
61
|
+
# @param type [ String ] the type prefix for categorizing the key
|
62
|
+
# @param url [ String ] the URL to be hashed
|
63
|
+
#
|
64
|
+
# @return [ String ] a hyphen-separated string of the type and URL's MD5 digest
|
35
65
|
def key(type, url)
|
36
66
|
[ type, Digest::MD5.hexdigest(url) ] * ?-
|
37
67
|
end
|
@@ -7,10 +7,22 @@ require 'ollama_chat/utils/cache_fetcher'
|
|
7
7
|
|
8
8
|
class OllamaChat::Utils::Fetcher
|
9
9
|
module HeaderExtension
|
10
|
+
# The content_type method accesses the content type attribute of the object.
|
11
|
+
#
|
12
|
+
# @return [ String ] the content type of the object.
|
10
13
|
attr_accessor :content_type
|
11
14
|
|
15
|
+
# The ex accessor is used to get or set the expiry value in seconds.
|
12
16
|
attr_accessor :ex
|
13
17
|
|
18
|
+
# The failed method creates a StringIO object with a text/plain content type.
|
19
|
+
#
|
20
|
+
# This method is used to generate a failed response object that can be used
|
21
|
+
# when an operation does not succeed. It initializes a new StringIO object
|
22
|
+
# and extends it with the current class, setting its content type to
|
23
|
+
# text/plain.
|
24
|
+
#
|
25
|
+
# @return [ StringIO ] a StringIO object with text/plain content type
|
14
26
|
def self.failed
|
15
27
|
object = StringIO.new.extend(self)
|
16
28
|
object.content_type = MIME::Types['text/plain'].first
|
@@ -20,6 +32,22 @@ class OllamaChat::Utils::Fetcher
|
|
20
32
|
|
21
33
|
class RetryWithoutStreaming < StandardError; end
|
22
34
|
|
35
|
+
# The get method retrieves content from a URL, using caching when available.
|
36
|
+
# It processes the URL with optional headers and additional options,
|
37
|
+
# then yields a temporary file containing the retrieved content.
|
38
|
+
# If caching is enabled and content is found in the cache,
|
39
|
+
# it returns the cached result instead of fetching again.
|
40
|
+
# The method handles both cached and fresh fetches,
|
41
|
+
# ensuring that cache is updated when new content is retrieved.
|
42
|
+
#
|
43
|
+
# @param url [ String ] the URL to fetch content from
|
44
|
+
# @param headers [ Hash ] optional headers to include in the request
|
45
|
+
# @param options [ Hash ] additional options for the fetch operation
|
46
|
+
#
|
47
|
+
# @yield [ tmp ]
|
48
|
+
#
|
49
|
+
# @return [ Object ] the result of the block execution
|
50
|
+
# @return [ nil ] if no block is given or if the fetch fails
|
23
51
|
def self.get(url, headers: {}, **options, &block)
|
24
52
|
cache = options.delete(:cache) and
|
25
53
|
cache = OllamaChat::Utils::CacheFetcher.new(cache)
|
@@ -38,6 +66,9 @@ class OllamaChat::Utils::Fetcher
|
|
38
66
|
end
|
39
67
|
end
|
40
68
|
|
69
|
+
# The normalize_url method processes a URL by converting it to a string,
|
70
|
+
# decoding any URI components, removing anchors, and then escaping the URL to
|
71
|
+
# ensure it is properly formatted.
|
41
72
|
def self.normalize_url(url)
|
42
73
|
url = url.to_s
|
43
74
|
url = URI.decode_uri_component(url)
|
@@ -45,6 +76,17 @@ class OllamaChat::Utils::Fetcher
|
|
45
76
|
URI::Parser.new.escape(url).to_s
|
46
77
|
end
|
47
78
|
|
79
|
+
# The read method opens a file and extends it with header extension metadata.
|
80
|
+
# It then yields the file to the provided block for processing.
|
81
|
+
# If the file does not exist, it outputs an error message to standard error.
|
82
|
+
#
|
83
|
+
# @param filename [ String ] the path to the file to be read
|
84
|
+
#
|
85
|
+
# @yield [ file ] yields the opened file with header extension
|
86
|
+
#
|
87
|
+
# @return [ nil ] returns nil if the file does not exist
|
88
|
+
# @return [ Object ] returns the result of the block execution if the file
|
89
|
+
# exists
|
48
90
|
def self.read(filename, &block)
|
49
91
|
if File.exist?(filename)
|
50
92
|
File.open(filename) do |file|
|
@@ -57,6 +99,16 @@ class OllamaChat::Utils::Fetcher
|
|
57
99
|
end
|
58
100
|
end
|
59
101
|
|
102
|
+
# The execute method runs a shell command and processes its output.
|
103
|
+
#
|
104
|
+
# It captures the command's standard output and error streams,
|
105
|
+
# writes them to a temporary file, and yields the file to the caller.
|
106
|
+
# If an exception occurs during execution, it reports the error
|
107
|
+
# and yields a failed temporary file instead.
|
108
|
+
#
|
109
|
+
# @param command [ String ] the shell command to execute
|
110
|
+
#
|
111
|
+
# @yield [ tmpfile ]
|
60
112
|
def self.execute(command, &block)
|
61
113
|
Tempfile.open do |tmp|
|
62
114
|
unless command =~ /2>&1/
|
@@ -80,6 +132,11 @@ class OllamaChat::Utils::Fetcher
|
|
80
132
|
yield HeaderExtension.failed
|
81
133
|
end
|
82
134
|
|
135
|
+
# The initialize method sets up the fetcher instance with debugging and HTTP
|
136
|
+
# configuration options.
|
137
|
+
#
|
138
|
+
# @param debug [ TrueClass, FalseClass ] enables or disables debug output
|
139
|
+
# @param http_options [ Hash ] additional options to pass to the HTTP client
|
83
140
|
def initialize(debug: false, http_options: {})
|
84
141
|
@debug = debug
|
85
142
|
@started = false
|
@@ -89,11 +146,34 @@ class OllamaChat::Utils::Fetcher
|
|
89
146
|
|
90
147
|
private
|
91
148
|
|
149
|
+
# The excon method creates a new Excon client instance configured with the
|
150
|
+
# specified URL and options.
|
151
|
+
#
|
152
|
+
# @param url [ String ] the URL to be used for the Excon client
|
153
|
+
# @param options [ Hash ] additional options to be merged with http_options
|
154
|
+
#
|
155
|
+
# @return [ Excon ] a new Excon client instance
|
156
|
+
#
|
157
|
+
# @see #normalize_url
|
158
|
+
# @see #http_options
|
92
159
|
def excon(url, **options)
|
93
160
|
url = self.class.normalize_url(url)
|
94
161
|
Excon.new(url, options.merge(@http_options))
|
95
162
|
end
|
96
163
|
|
164
|
+
# Makes an HTTP GET request to the specified URL with optional headers and
|
165
|
+
# processing block.
|
166
|
+
#
|
167
|
+
# This method handles both streaming and non-streaming HTTP requests, using
|
168
|
+
# Excon for the actual HTTP communication. The response body is written to a
|
169
|
+
# temporary file which is then decorated with additional behavior before
|
170
|
+
# being passed to the provided block.
|
171
|
+
#
|
172
|
+
# @param url [String] The URL to make the GET request to
|
173
|
+
# @param headers [Hash] Optional headers to include in the request (keys will
|
174
|
+
# be converted to strings)
|
175
|
+
# @yield [Tempfile] The temporary file containing the response body, after
|
176
|
+
# decoration
|
97
177
|
def get(url, headers: {}, &block)
|
98
178
|
headers |= self.headers
|
99
179
|
headers = headers.transform_keys(&:to_s)
|
@@ -130,18 +210,41 @@ class OllamaChat::Utils::Fetcher
|
|
130
210
|
yield HeaderExtension.failed
|
131
211
|
end
|
132
212
|
|
213
|
+
# The headers method returns a hash containing the default HTTP headers
|
214
|
+
# that should be used for requests, including a User-Agent header
|
215
|
+
# configured with the application's user agent string.
|
216
|
+
#
|
217
|
+
# @return [ Hash ] a hash mapping header names to their values
|
218
|
+
# @note The returned hash includes the 'User-Agent' header
|
219
|
+
# set to OllamaChat::Chat.user_agent.
|
133
220
|
def headers
|
134
221
|
{
|
135
222
|
'User-Agent' => OllamaChat::Chat.user_agent,
|
136
223
|
}
|
137
224
|
end
|
138
225
|
|
226
|
+
# The middlewares method returns the combined array of default Excon
|
227
|
+
# middlewares and the RedirectFollower middleware, ensuring there are no
|
228
|
+
# duplicates.
|
229
|
+
#
|
230
|
+
# @return [ Array ] an array of middleware classes including RedirectFollower
|
231
|
+
# deduplicated from the default Excon middlewares.
|
139
232
|
def middlewares
|
140
233
|
(Excon.defaults[:middlewares] + [ Excon::Middleware::RedirectFollower ]).uniq
|
141
234
|
end
|
142
235
|
|
143
236
|
private
|
144
237
|
|
238
|
+
# Decorates a temporary IO object with header information from an HTTP
|
239
|
+
# response.
|
240
|
+
#
|
241
|
+
# This method extends the given temporary IO object with HeaderExtension
|
242
|
+
# module and populates it with content type and cache expiration information
|
243
|
+
# extracted from the provided response headers.
|
244
|
+
#
|
245
|
+
# @param tmp [IO] The temporary IO object to decorate (typically a file handle)
|
246
|
+
# @param response [Object] HTTP response object containing headers
|
247
|
+
# @option response [Hash] :headers HTTP headers hash
|
145
248
|
def decorate_io(tmp, response)
|
146
249
|
tmp.rewind
|
147
250
|
tmp.extend(HeaderExtension)
|
@@ -156,6 +259,13 @@ class OllamaChat::Utils::Fetcher
|
|
156
259
|
end
|
157
260
|
end
|
158
261
|
|
262
|
+
# The callback method creates a proc that handles chunked data processing by
|
263
|
+
# updating progress information and writing chunks to a temporary file.
|
264
|
+
#
|
265
|
+
# @param tmp [ Tempfile ] the temporary file to which data chunks are written
|
266
|
+
#
|
267
|
+
# @return [ Proc ] a proc that accepts chunk, remaining_bytes, and total_bytes
|
268
|
+
# parameters for processing streamed data
|
159
269
|
def callback(tmp)
|
160
270
|
-> chunk, remaining_bytes, total_bytes do
|
161
271
|
total = total_bytes or next
|
@@ -171,6 +281,13 @@ class OllamaChat::Utils::Fetcher
|
|
171
281
|
end
|
172
282
|
end
|
173
283
|
|
284
|
+
# The message method formats progress information by combining current and
|
285
|
+
# total values with unit formatting, along with timing details.
|
286
|
+
#
|
287
|
+
# @param current [ Integer ] the current progress value
|
288
|
+
# @param total [ Integer ] the total progress value
|
289
|
+
#
|
290
|
+
# @return [ String ] a formatted progress string including units and timing information
|
174
291
|
def message(current, total)
|
175
292
|
progress = '%s/%s' % [ current, total ].map {
|
176
293
|
Tins::Unit.format(_1, format: '%.2f %U')
|
data/lib/ollama_chat/version.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class OllamaChat::Vim
|
5
|
+
# Initializes a new Vim server connection
|
6
|
+
#
|
7
|
+
# Creates a new OllamaChat::Vim instance for interacting with a specific Vim
|
8
|
+
# server. If no server name is provided, it defaults to using the current
|
9
|
+
# working directory as the server identifier.
|
10
|
+
#
|
11
|
+
# @param server_name [String, nil] The name of the Vim server to connect to.
|
12
|
+
# If nil or empty, defaults to the current working directory path in
|
13
|
+
# uppercase
|
14
|
+
def initialize(server_name)
|
15
|
+
server_name.full? or server_name = default_server_name
|
16
|
+
@server_name = server_name
|
17
|
+
end
|
18
|
+
|
19
|
+
# The default server name is derived from the current working directory It
|
20
|
+
# converts the absolute path to uppercase for consistent identification This
|
21
|
+
# approach ensures each working directory gets a unique server identifier The
|
22
|
+
# server name format makes it easy to distinguish different Vim sessions
|
23
|
+
def default_server_name
|
24
|
+
Pathname.pwd.to_s.upcase
|
25
|
+
end
|
26
|
+
|
27
|
+
# Inserts text at the current cursor position in Vim
|
28
|
+
#
|
29
|
+
# This method writes the provided text to a temporary file and uses Vim's
|
30
|
+
# remote-send functionality to insert it at the current cursor position.
|
31
|
+
# The text is automatically indented to match the current column position.
|
32
|
+
#
|
33
|
+
# @param text [String] The text to be inserted into the Vim buffer
|
34
|
+
def insert(text)
|
35
|
+
spaces = (col - 1).clamp(0..)
|
36
|
+
text = text.gsub(/^/, ' ' * spaces)
|
37
|
+
Tempfile.open do |tmp|
|
38
|
+
tmp.write(text)
|
39
|
+
tmp.flush
|
40
|
+
system %{vim --servername "#@server_name" --remote-send "<ESC>:r #{tmp.path}<CR>"}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the current column position of the cursor in the Vim server
|
45
|
+
#
|
46
|
+
# This method queries the specified Vim server for the current cursor position
|
47
|
+
# using Vim's remote expression feature. It executes a Vim command that returns
|
48
|
+
# the result of `col('.')`, which represents the current column number (1-indexed)
|
49
|
+
# of the cursor position.
|
50
|
+
def col
|
51
|
+
`vim --servername "#@server_name" --remote-expr "col('.')"`.chomp.to_i
|
52
|
+
end
|
53
|
+
end
|