riffer 0.4.2 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb60a1ab6edf223c701c1b3ff1af14a6ff52520541d1d5e757df963436517402
4
- data.tar.gz: 723b875438e9d787f7199d8ee8d83d8485dfed47686e85e2c43c221a0482c41e
3
+ metadata.gz: 12e6843cdf4824294d9c9470c2dad55125bfa490b49389530145bd334041ce15
4
+ data.tar.gz: 636133b4f5b2a046311a33a3b294ce2eb893bcb3a16292bd83198fc24f3dc500
5
5
  SHA512:
6
- metadata.gz: e288711d911bbf4803f770afccf95685067496d0b1443a31ab8f0291ee3a9e58deac4215820e8e261e8966fac9c14f749880d0b07e47b41458d4801406ad75b1
7
- data.tar.gz: a877dc42d6e07d0f31ca95213603a44fb47d865ea0e1b69ffad8cc898e69f3cce4e96ae6c750d4fe70c0e5e85a1c7efb7bb870c905306db1b67ea0d39adbbb82
6
+ metadata.gz: a26010e36c164851b3a9cbd6d3b7eebd9dcd935d4fdd405793998d2760b871b01a487724a8628eddc6bf366db164f924990ee12a2c93f99602e2c0f99c341d88
7
+ data.tar.gz: 18428555a37e27ee29c35d21c7b1f6f00f988a22e02e6a25141d6a3d6b9d0c49da43a7bf408886d28734e485fd7ac32015643ac7d1c9fe2406bffd6d9a6602d7
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.4.2"
2
+ ".": "0.5.0"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.0](https://github.com/bottrall/riffer/compare/riffer/v0.4.2...riffer/v0.5.0) (2026-01-06)
9
+
10
+
11
+ ### Features
12
+
13
+ * streaming via agents ([#63](https://github.com/bottrall/riffer/issues/63)) ([b4171c2](https://github.com/bottrall/riffer/commit/b4171c20f64a7ada1264ce90ab5278c19ff8a47a))
14
+
8
15
  ## [0.4.2](https://github.com/bottrall/riffer/compare/riffer/v0.4.1...riffer/v0.4.2) (2025-12-29)
9
16
 
10
17
 
data/README.md CHANGED
@@ -68,13 +68,12 @@ puts agent.generate('Hello world')
68
68
  # => "Hello world"
69
69
  ```
70
70
 
71
- Streaming example (OpenAI):
71
+ Streaming example:
72
72
 
73
73
  ```ruby
74
- provider = Riffer::Providers::OpenAI.new
75
- enum = provider.stream_text(prompt: 'Stream something', model: 'gpt-4')
76
- enum.each do |chunk|
77
- puts chunk.content
74
+ agent = EchoAgent.new
75
+ agent.stream('Tell me a story').each do |event|
76
+ print event.content
78
77
  end
79
78
  ```
80
79
 
data/lib/riffer/agent.rb CHANGED
@@ -91,6 +91,31 @@ class Riffer::Agent
91
91
  extract_final_response
92
92
  end
93
93
 
94
+ # Streams a response from the agent
95
+ # @param prompt_or_messages [String, Array<Hash, Riffer::Messages::Base>]
96
+ # @return [Enumerator] an enumerator yielding stream events
97
+ def stream(prompt_or_messages)
98
+ initialize_messages(prompt_or_messages)
99
+
100
+ Enumerator.new do |yielder|
101
+ accumulated_content = ""
102
+
103
+ call_llm_stream.each do |event|
104
+ yielder << event
105
+
106
+ case event
107
+ when Riffer::StreamEvents::TextDelta
108
+ accumulated_content += event.content
109
+ when Riffer::StreamEvents::TextDone
110
+ accumulated_content = event.content
111
+ end
112
+ end
113
+
114
+ response = Riffer::Messages::Assistant.new(accumulated_content)
115
+ @messages << response
116
+ end
117
+ end
118
+
94
119
  private
95
120
 
96
121
  def initialize_messages(prompt_or_messages)
@@ -110,9 +135,13 @@ class Riffer::Agent
110
135
  provider_instance.generate_text(messages: @messages, model: @model_name)
111
136
  end
112
137
 
138
+ def call_llm_stream
139
+ provider_instance.stream_text(messages: @messages, model: @model_name)
140
+ end
141
+
113
142
  def provider_instance
114
143
  @provider_instance ||= begin
115
- provider_class = Riffer::Providers::Base.find(@provider_name)
144
+ provider_class = Riffer::Providers::Repository.find(@provider_name)
116
145
  raise Riffer::ArgumentError, "Provider not found: #{@provider_name}" unless provider_class
117
146
  provider_class.new
118
147
  end
@@ -4,32 +4,6 @@ class Riffer::Providers::Base
4
4
  include Riffer::Helpers::Dependencies
5
5
  include Riffer::Messages::Converter
6
6
 
7
- class << self
8
- def identifier(value = nil)
9
- return @identifier if value.nil?
10
-
11
- @identifier = value
12
- end
13
-
14
- # Finds a provider class by identifier
15
- # @param identifier [String, Symbol] the identifier to search for
16
- # @return [Class, nil] the provider class, or nil if not found
17
- def find(identifier)
18
- ensure_providers_loaded
19
- subclasses.find { |provider_class| provider_class.identifier == identifier }
20
- end
21
-
22
- private
23
-
24
- def ensure_providers_loaded
25
- return if @providers_loaded
26
-
27
- Zeitwerk::Loader.eager_load_namespace(Riffer::Providers)
28
-
29
- @providers_loaded = true
30
- end
31
- end
32
-
33
7
  # Generates text using the provider.
34
8
  #
35
9
  # @param prompt [String, nil] the user prompt (required when `messages` is not provided)
@@ -3,8 +3,6 @@
3
3
  require "openai"
4
4
 
5
5
  class Riffer::Providers::OpenAI < Riffer::Providers::Base
6
- identifier "openai"
7
-
8
6
  # Initializes the OpenAI provider.
9
7
  # @param options [Hash] optional client options. Use `:api_key` to override `Riffer.config.openai.api_key`.
10
8
  # @raise [Riffer::ArgumentError] if an API key is not provided either via `:api_key` or `Riffer.config.openai.api_key`.
@@ -78,19 +76,16 @@ class Riffer::Providers::OpenAI < Riffer::Providers::Base
78
76
  end
79
77
 
80
78
  def process_stream_events(stream, yielder)
81
- stream.each do |event|
82
- next unless should_process_event?(event)
79
+ stream.each do |raw_event|
80
+ event = convert_event(raw_event)
83
81
 
84
- content = extract_event_content(event)
85
- yielder << content if content
86
- end
87
- end
82
+ next unless event
88
83
 
89
- def should_process_event?(event)
90
- [:"response.output_text.delta", :"response.output_text.done"].include?(event.type)
84
+ yielder << event if event
85
+ end
91
86
  end
92
87
 
93
- def extract_event_content(event)
88
+ def convert_event(event)
94
89
  case event.type
95
90
  when :"response.output_text.delta"
96
91
  Riffer::StreamEvents::TextDelta.new(event.delta)
@@ -0,0 +1,15 @@
1
+ class Riffer::Providers::Repository
2
+ REPO = {
3
+ openai: -> { Riffer::Providers::OpenAI },
4
+ test: -> { Riffer::Providers::Test }
5
+ }.freeze
6
+
7
+ class << self
8
+ # Finds a provider class by identifier
9
+ # @param identifier [String, Symbol] the identifier to search for
10
+ # @return [Class, nil] the provider class, or nil if not found
11
+ def find(identifier)
12
+ REPO.fetch(identifier.to_sym, nil)&.call
13
+ end
14
+ end
15
+ end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Riffer::Providers::Test < Riffer::Providers::Base
4
- identifier "test"
5
-
6
4
  attr_reader :calls
7
5
 
8
6
  def initialize(**options)
@@ -35,11 +33,14 @@ class Riffer::Providers::Test < Riffer::Providers::Base
35
33
  response = @stubbed_response || @responses[@current_index] || {role: "assistant", content: "Test response"}
36
34
  @current_index += 1
37
35
  Enumerator.new do |yielder|
38
- content_parts = response[:content].split(". ").map { |part| part + (part.end_with?(".") ? "" : ".") }
36
+ full_content = response[:content]
37
+ content_parts = full_content.split(". ").map { |part| part + (part.end_with?(".") ? "" : ".") }
38
+
39
39
  content_parts.each do |part|
40
- yielder << {role: "assistant", content: part + " "}
41
- sleep 0.5
40
+ yielder << Riffer::StreamEvents::TextDelta.new(part + " ")
42
41
  end
42
+
43
+ yielder << Riffer::StreamEvents::TextDone.new(full_content)
43
44
  end
44
45
  end
45
46
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Riffer
4
- VERSION = "0.4.2"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riffer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall
@@ -150,6 +150,7 @@ files:
150
150
  - lib/riffer/providers.rb
151
151
  - lib/riffer/providers/base.rb
152
152
  - lib/riffer/providers/open_ai.rb
153
+ - lib/riffer/providers/repository.rb
153
154
  - lib/riffer/providers/test.rb
154
155
  - lib/riffer/stream_events.rb
155
156
  - lib/riffer/stream_events/base.rb