roseflow 0.0.1 → 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 +4 -4
- data/.standard.yml +3 -1
- data/CHANGELOG.md +2 -2
- data/Gemfile +2 -0
- data/examples/github-repo-chat/lib/actions/clone_and_load_repository.rb +52 -0
- data/examples/github-repo-chat/lib/actions/create_prompt.rb +15 -0
- data/examples/github-repo-chat/lib/actions/embed_repository.rb +35 -0
- data/examples/github-repo-chat/lib/actions/initialize_vector_store.rb +40 -0
- data/examples/github-repo-chat/lib/actions/interact_with_model.rb +29 -0
- data/examples/github-repo-chat/lib/actions/load_documents_to_database.rb +0 -0
- data/examples/github-repo-chat/lib/actions/split_files_to_documents.rb +55 -0
- data/examples/github-repo-chat/lib/document_database.rb +0 -0
- data/examples/github-repo-chat/lib/github_chat_prompt.rb +24 -0
- data/examples/github-repo-chat/lib/github_repository_chat.rb +12 -0
- data/examples/github-repo-chat/lib/interactions/ask_llm.rb +31 -0
- data/examples/github-repo-chat/lib/interactions/github_repository_chat.rb +36 -0
- data/examples/github-repo-chat/lib/interactions/load_files_to_document_database.rb +18 -0
- data/examples/github-repo-chat/lib/interactions/load_repository.rb +20 -0
- data/examples/github-repo-chat/lib/interactions/prepare_vector_store.rb +21 -0
- data/examples/github-repo-chat/lib/repository.rb +9 -0
- data/examples/github-repo-chat/lib/repository_file.rb +31 -0
- data/examples/github-repo-chat/spec/actions/clone_and_load_repository_spec.rb +28 -0
- data/examples/github-repo-chat/spec/actions/embed_repository_spec.rb +24 -0
- data/examples/github-repo-chat/spec/actions/initialize_vector_store_spec.rb +20 -0
- data/examples/github-repo-chat/spec/actions/load_files_to_document_database_spec.rb +23 -0
- data/examples/github-repo-chat/spec/fixtures/ulid-ruby.zip +0 -0
- data/examples/github-repo-chat/spec/github_repository_chat_spec.rb +16 -0
- data/examples/github-repo-chat/spec/interactions/prepare_vector_store_spec.rb +4 -0
- data/examples/github-repo-chat/spec/spec_helper.rb +12 -0
- data/lib/roseflow/action.rb +13 -0
- data/lib/roseflow/actions/ai/resolve_model.rb +27 -0
- data/lib/roseflow/actions/ai/resolve_provider.rb +31 -0
- data/lib/roseflow/ai/model.rb +19 -0
- data/lib/roseflow/ai/provider.rb +30 -0
- data/lib/roseflow/chat/dialogue.rb +80 -0
- data/lib/roseflow/chat/exchange.rb +12 -0
- data/lib/roseflow/chat/message.rb +39 -0
- data/lib/roseflow/chat/personality.rb +10 -0
- data/lib/roseflow/embeddings/embedding.rb +26 -0
- data/lib/roseflow/finite_machine.rb +298 -0
- data/lib/roseflow/interaction/with_http_api.rb +10 -0
- data/lib/roseflow/interaction.rb +14 -0
- data/lib/roseflow/interaction_context.rb +10 -0
- data/lib/roseflow/interactions/ai/initialize_llm.rb +26 -0
- data/lib/roseflow/primitives/vector.rb +19 -0
- data/lib/roseflow/prompt.rb +17 -0
- data/lib/roseflow/text/completion.rb +16 -0
- data/lib/roseflow/text/recursive_character_splitter.rb +43 -0
- data/lib/roseflow/text/sentence_splitter.rb +42 -0
- data/lib/roseflow/text/splitter.rb +18 -0
- data/lib/roseflow/text/tokenized_text.rb +20 -0
- data/lib/roseflow/text/word_splitter.rb +14 -0
- data/lib/roseflow/tokenizer.rb +13 -0
- data/lib/roseflow/types.rb +9 -0
- data/lib/roseflow/vector_stores/base.rb +39 -0
- data/lib/roseflow/vector_stores/hnsw.proto +18 -0
- data/lib/roseflow/vector_stores/hnsw_memory_store.rb +442 -0
- data/lib/roseflow/vector_stores/hnsw_pb.rb +27 -0
- data/lib/roseflow/vector_stores/type/vector.rb +38 -0
- data/lib/roseflow/vector_stores/vector.rb +19 -0
- data/lib/roseflow/version.rb +12 -1
- data/lib/roseflow.rb +10 -1
- data/roseflow.gemspec +53 -0
- metadata +274 -7
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "roseflow/action"
|
4
|
+
require "roseflow/ai/provider"
|
5
|
+
require "roseflow/openai/config"
|
6
|
+
|
7
|
+
module Roseflow
|
8
|
+
module Actions
|
9
|
+
module AI
|
10
|
+
class ResolveProvider
|
11
|
+
extend Roseflow::Action
|
12
|
+
|
13
|
+
expects :provider
|
14
|
+
promises :provider
|
15
|
+
|
16
|
+
executed do |context|
|
17
|
+
context[:provider] = resolve_provider(context[:provider])
|
18
|
+
end
|
19
|
+
|
20
|
+
private_class_method
|
21
|
+
|
22
|
+
def self.resolve_provider(provider)
|
23
|
+
case provider
|
24
|
+
when :openai
|
25
|
+
Roseflow::AI::Provider.new(name: :openai, credentials: Roseflow::OpenAI::Config.new)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Roseflow
|
4
|
+
module AI
|
5
|
+
class Model
|
6
|
+
attr_reader :name, :provider
|
7
|
+
|
8
|
+
def initialize(name:, provider:)
|
9
|
+
@name = name
|
10
|
+
@provider = provider
|
11
|
+
@model_ = provider.models.find(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(operation, input)
|
15
|
+
@model_.call(operation, input)
|
16
|
+
end
|
17
|
+
end # Model
|
18
|
+
end # AI
|
19
|
+
end # Roseflow
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "roseflow/openai/provider"
|
4
|
+
|
5
|
+
module Roseflow
|
6
|
+
module AI
|
7
|
+
class Provider
|
8
|
+
def initialize(name:, credentials:)
|
9
|
+
@name = name
|
10
|
+
@credentials = credentials
|
11
|
+
initialize_provider
|
12
|
+
end
|
13
|
+
|
14
|
+
def models
|
15
|
+
@models ||= provider.models
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :name, :credentials, :provider
|
21
|
+
|
22
|
+
def initialize_provider
|
23
|
+
case name
|
24
|
+
when :openai
|
25
|
+
@provider = Roseflow::OpenAI::Provider.new(credentials: credentials)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end # Provider
|
29
|
+
end # AI
|
30
|
+
end # Roseflow
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "roseflow/chat/exchange"
|
4
|
+
require "roseflow/chat/message"
|
5
|
+
|
6
|
+
module Roseflow
|
7
|
+
module Chat
|
8
|
+
class Dialogue
|
9
|
+
attr_reader :messages
|
10
|
+
attr_reader :exchanges
|
11
|
+
attr_reader :personality
|
12
|
+
|
13
|
+
def initialize(personality: nil)
|
14
|
+
@messages = []
|
15
|
+
@exchanges = []
|
16
|
+
initialize_with_personality(personality) if personality
|
17
|
+
end
|
18
|
+
|
19
|
+
def model
|
20
|
+
@model ||= provider.models.chattable.first
|
21
|
+
end
|
22
|
+
|
23
|
+
def provider
|
24
|
+
@provider ||= OpenAI::Provider.new(Roseflow::OpenAI::Config.new)
|
25
|
+
end
|
26
|
+
|
27
|
+
def recall(messages)
|
28
|
+
@messages = messages
|
29
|
+
parse_exchanges
|
30
|
+
end
|
31
|
+
|
32
|
+
# This method takes a string input as input, which is a message to send to the LLM model.
|
33
|
+
# If the model is chattable, it sends the messages to the model and returns a response.
|
34
|
+
def say(input, **options)
|
35
|
+
if model.chattable?
|
36
|
+
message = create_user_message(input)
|
37
|
+
@messages.push(message)
|
38
|
+
|
39
|
+
model_response = provider.chat(model: model, messages: @messages, **options)
|
40
|
+
|
41
|
+
if model_response && model_response.success?
|
42
|
+
exchange = create_exchange(message, model_response.choices.first.message)
|
43
|
+
@exchanges.push(exchange)
|
44
|
+
@messages.push(exchange.response)
|
45
|
+
exchange.response
|
46
|
+
else
|
47
|
+
raise "Did not receive a response from the model"
|
48
|
+
end
|
49
|
+
else
|
50
|
+
raise "Model is not chattable"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def initialize_with_personality(personality)
|
57
|
+
@personality = personality
|
58
|
+
@messages << SystemMessage.new(role: "system", content: personality.description)
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_user_message(input)
|
62
|
+
UserMessage.new(role: "user", content: input)
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_exchange(prompt, response)
|
66
|
+
Exchange.new(prompt: prompt, response: response)
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_exchanges
|
70
|
+
user_assistant_messages.each_cons(2) do |prompt, response|
|
71
|
+
@exchanges << Exchange.new(prompt: prompt, response: response)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def user_assistant_messages
|
76
|
+
@messages.reject { |message| message.role == "system" }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-struct"
|
4
|
+
require "roseflow/types"
|
5
|
+
|
6
|
+
module Roseflow
|
7
|
+
module Chat
|
8
|
+
class Message < Dry::Struct
|
9
|
+
attribute :role, Types::String
|
10
|
+
attribute :content, Types::String
|
11
|
+
attribute? :name, Types::String
|
12
|
+
attribute? :token_count, Types::Integer
|
13
|
+
|
14
|
+
def user?
|
15
|
+
role == "user"
|
16
|
+
end
|
17
|
+
|
18
|
+
def system?
|
19
|
+
role == "system"
|
20
|
+
end
|
21
|
+
|
22
|
+
def model?
|
23
|
+
%w(system assistant).include?(role)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ModelMessage < Message
|
28
|
+
attribute :role, Types::String.constrained(included_in: %w(system assistant))
|
29
|
+
end
|
30
|
+
|
31
|
+
class UserMessage < Message
|
32
|
+
attribute :role, Types::String.constrained(included_in: %w(user))
|
33
|
+
end
|
34
|
+
|
35
|
+
class SystemMessage < Message
|
36
|
+
attribute :role, Types::String.constrained(included_in: %w(system))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Roseflow
|
4
|
+
module Embeddings
|
5
|
+
EmbeddingModelNotSpecifiedError = Class.new(StandardError)
|
6
|
+
|
7
|
+
class Embedding
|
8
|
+
attr_reader :length, :vector
|
9
|
+
attr_writer :input
|
10
|
+
|
11
|
+
def initialize(**kwargs)
|
12
|
+
@length = kwargs.fetch(:length, 1024)
|
13
|
+
@input = kwargs.fetch(:input, nil)
|
14
|
+
@model = kwargs.fetch(:model, nil)
|
15
|
+
@vector = kwargs.fetch(:vector, nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
raise ArgumentError, "An input must be provided" unless @input
|
20
|
+
raise EmbeddingModelNotSpecifiedError, "An embedding model must be specified" unless @model
|
21
|
+
response = @model.provider.create_embedding(model: @model, input: @input)
|
22
|
+
response.embedding
|
23
|
+
end
|
24
|
+
end # Embedding
|
25
|
+
end # Embeddings
|
26
|
+
end # Roseflow
|
@@ -0,0 +1,298 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ulid"
|
4
|
+
require "finite_machine"
|
5
|
+
|
6
|
+
## FIXME:
|
7
|
+
## Monkey patching FiniteMachine::GenericDSL for Ruby 3.2 support.
|
8
|
+
## See: https://github.com/piotrmurach/finite_machine/pull/74
|
9
|
+
|
10
|
+
module FiniteMachine
|
11
|
+
# A class responsible for observing state changes
|
12
|
+
class Observer < GenericDSL
|
13
|
+
include Safety
|
14
|
+
|
15
|
+
# Clean up callback queue
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def cleanup_callback_queue(_id)
|
19
|
+
if callback_queue.alive?
|
20
|
+
callback_queue.shutdown
|
21
|
+
end
|
22
|
+
rescue MessageQueueDeadError
|
23
|
+
end
|
24
|
+
|
25
|
+
# The current state machine
|
26
|
+
attr_reader :machine
|
27
|
+
|
28
|
+
# The hooks to trigger around the transition lifecycle.
|
29
|
+
attr_reader :hooks
|
30
|
+
|
31
|
+
# Initialize an Observer
|
32
|
+
#
|
33
|
+
# @param [StateMachine] machine
|
34
|
+
# reference to the current machine
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def initialize(machine)
|
38
|
+
@id = ULID.generate
|
39
|
+
@machine = machine
|
40
|
+
@hooks = Hooks.new
|
41
|
+
|
42
|
+
@machine.subscribe(self)
|
43
|
+
ObjectSpace.define_finalizer(@id, method(:cleanup_callback_queue))
|
44
|
+
end
|
45
|
+
|
46
|
+
def callback_queue
|
47
|
+
@callback_queue ||= MessageQueue.new
|
48
|
+
end
|
49
|
+
|
50
|
+
# Evaluate in current context
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
def call(&block)
|
54
|
+
instance_eval(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Register callback for a given hook type
|
58
|
+
#
|
59
|
+
# @param [HookEvent] hook_type
|
60
|
+
# @param [Symbol] state_or_event_name
|
61
|
+
# @param [Proc] callback
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# observer.on HookEvent::Enter, :green
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def on(hook_type, state_or_event_name = nil, async = nil, &callback)
|
68
|
+
sync_exclusive do
|
69
|
+
if state_or_event_name.nil?
|
70
|
+
state_or_event_name = HookEvent.any_state_or_event(hook_type)
|
71
|
+
end
|
72
|
+
async = false if async.nil?
|
73
|
+
ensure_valid_callback_name!(hook_type, state_or_event_name)
|
74
|
+
callback.extend(Async) if async == :async
|
75
|
+
hooks.register(hook_type, state_or_event_name, callback)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Unregister callback for a given event
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
def off(hook_type, name = ANY_STATE, &callback)
|
83
|
+
sync_exclusive do
|
84
|
+
hooks.unregister hook_type, name, callback
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module Once; end
|
89
|
+
|
90
|
+
module Async; end
|
91
|
+
|
92
|
+
def on_enter(*args, &callback)
|
93
|
+
on HookEvent::Enter, *args, &callback
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_transition(*args, &callback)
|
97
|
+
on HookEvent::Transition, *args, &callback
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_exit(*args, &callback)
|
101
|
+
on HookEvent::Exit, *args, &callback
|
102
|
+
end
|
103
|
+
|
104
|
+
def once_on_enter(*args, &callback)
|
105
|
+
on HookEvent::Enter, *args, &callback.extend(Once)
|
106
|
+
end
|
107
|
+
|
108
|
+
def once_on_transition(*args, &callback)
|
109
|
+
on HookEvent::Transition, *args, &callback.extend(Once)
|
110
|
+
end
|
111
|
+
|
112
|
+
def once_on_exit(*args, &callback)
|
113
|
+
on HookEvent::Exit, *args, &callback.extend(Once)
|
114
|
+
end
|
115
|
+
|
116
|
+
def on_before(*args, &callback)
|
117
|
+
on HookEvent::Before, *args, &callback
|
118
|
+
end
|
119
|
+
|
120
|
+
def on_after(*args, &callback)
|
121
|
+
on HookEvent::After, *args, &callback
|
122
|
+
end
|
123
|
+
|
124
|
+
def once_on_before(*args, &callback)
|
125
|
+
on HookEvent::Before, *args, &callback.extend(Once)
|
126
|
+
end
|
127
|
+
|
128
|
+
def once_on_after(*args, &callback)
|
129
|
+
on HookEvent::After, *args, &callback.extend(Once)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Execute each of the hooks in order with supplied data
|
133
|
+
#
|
134
|
+
# @param [HookEvent] event
|
135
|
+
# the hook event
|
136
|
+
#
|
137
|
+
# @param [Array[Object]] data
|
138
|
+
#
|
139
|
+
# @return [nil]
|
140
|
+
#
|
141
|
+
# @api public
|
142
|
+
def emit(event, *data)
|
143
|
+
sync_exclusive do
|
144
|
+
[event.type].each do |hook_type|
|
145
|
+
any_state_or_event = HookEvent.any_state_or_event(hook_type)
|
146
|
+
[any_state_or_event, event.name].each do |event_name|
|
147
|
+
hooks[hook_type][event_name].each do |hook|
|
148
|
+
handle_callback(hook, event, *data)
|
149
|
+
off(hook_type, event_name, &hook) if hook.is_a?(Once)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Cancel the current event
|
157
|
+
#
|
158
|
+
# This should be called inside a on_before or on_exit callbacks
|
159
|
+
# to prevent event transition.
|
160
|
+
#
|
161
|
+
# @param [String] msg
|
162
|
+
# the message used for failure
|
163
|
+
#
|
164
|
+
# @api public
|
165
|
+
def cancel_event(msg = nil)
|
166
|
+
raise CallbackError.new(msg)
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# Handle callback and decide if run synchronously or asynchronously
|
172
|
+
#
|
173
|
+
# @param [Proc] :hook
|
174
|
+
# The hook to evaluate
|
175
|
+
#
|
176
|
+
# @param [HookEvent] :event
|
177
|
+
# The event for which the hook is called
|
178
|
+
#
|
179
|
+
# @param [Array[Object]] :data
|
180
|
+
#
|
181
|
+
# @api private
|
182
|
+
def handle_callback(hook, event, *data)
|
183
|
+
to = machine.events_map.move_to(event.event_name, event.from, *data)
|
184
|
+
trans_event = TransitionEvent.new(event.event_name, event.from, to)
|
185
|
+
callable = create_callable(hook)
|
186
|
+
|
187
|
+
if hook.is_a?(Async)
|
188
|
+
defer(callable, trans_event, *data)
|
189
|
+
else
|
190
|
+
callable.call(trans_event, *data)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Defer callback execution
|
195
|
+
#
|
196
|
+
# @api private
|
197
|
+
def defer(callable, trans_event, *data)
|
198
|
+
async_call = AsyncCall.new(machine, callable, trans_event, *data)
|
199
|
+
callback_queue.start unless callback_queue.running?
|
200
|
+
callback_queue << async_call
|
201
|
+
end
|
202
|
+
|
203
|
+
# Create callable instance
|
204
|
+
#
|
205
|
+
# @api private
|
206
|
+
def create_callable(hook)
|
207
|
+
callback = proc do |trans_event, *data|
|
208
|
+
machine.instance_exec(trans_event, *data, &hook)
|
209
|
+
end
|
210
|
+
Callable.new(callback)
|
211
|
+
end
|
212
|
+
|
213
|
+
# Callback names including all states and events
|
214
|
+
#
|
215
|
+
# @return [Array[Symbol]]
|
216
|
+
# valid callback names
|
217
|
+
#
|
218
|
+
# @api private
|
219
|
+
def callback_names
|
220
|
+
machine.states + machine.events + [ANY_EVENT, ANY_STATE]
|
221
|
+
end
|
222
|
+
|
223
|
+
# Forward the message to observer
|
224
|
+
#
|
225
|
+
# @param [String] method_name
|
226
|
+
#
|
227
|
+
# @param [Array] args
|
228
|
+
#
|
229
|
+
# @return [self]
|
230
|
+
#
|
231
|
+
# @api private
|
232
|
+
def method_missing(method_name, *args, &block)
|
233
|
+
_, event_name, callback_name = *method_name.to_s.match(/^(\w*?on_\w+?)_(\w+)$/)
|
234
|
+
if callback_name && callback_names.include?(callback_name.to_sym)
|
235
|
+
public_send(event_name, :"#{callback_name}", *args, &block)
|
236
|
+
else
|
237
|
+
super
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Test if a message can be handled by observer
|
242
|
+
#
|
243
|
+
# @param [String] method_name
|
244
|
+
#
|
245
|
+
# @param [Boolean] include_private
|
246
|
+
#
|
247
|
+
# @return [Boolean]
|
248
|
+
#
|
249
|
+
# @api private
|
250
|
+
def respond_to_missing?(method_name, include_private = false)
|
251
|
+
*_, callback_name = *method_name.to_s.match(/^(\w*?on_\w+?)_(\w+)$/)
|
252
|
+
callback_name && callback_names.include?(:"#{callback_name}")
|
253
|
+
end
|
254
|
+
end # Observer
|
255
|
+
|
256
|
+
class StateMachine
|
257
|
+
def initialize(*args, &block)
|
258
|
+
options = args.last.is_a?(::Hash) ? args.pop : {}
|
259
|
+
@initial_state = DEFAULT_STATE
|
260
|
+
@auto_methods = options.fetch(:auto_methods, true)
|
261
|
+
@subscribers = Subscribers.new
|
262
|
+
@observer = Observer.new(self)
|
263
|
+
@events_map = EventsMap.new
|
264
|
+
@env = Env.new(self, [])
|
265
|
+
@dsl = DSL.new(self, options)
|
266
|
+
@name = options.fetch(:name) { ULID.generate }
|
267
|
+
|
268
|
+
env.target = args.pop unless args.empty?
|
269
|
+
env.aliases << options[:alias_target] if options[:alias_target]
|
270
|
+
dsl.call(&block) if block
|
271
|
+
trigger_init
|
272
|
+
end
|
273
|
+
|
274
|
+
def transition!(event_name, *data, &block)
|
275
|
+
from_state = current
|
276
|
+
to_state = events_map.move_to(event_name, from_state, *data)
|
277
|
+
|
278
|
+
block.call(from_state, to_state) if block
|
279
|
+
|
280
|
+
if log_transitions
|
281
|
+
Logger.report_transition(@name, event_name, from_state, to_state, *data)
|
282
|
+
end
|
283
|
+
|
284
|
+
try_trigger(event_name) { transition_to!(to_state) }
|
285
|
+
end
|
286
|
+
end # StateMachine
|
287
|
+
|
288
|
+
module Logger
|
289
|
+
def report_transition(machine_name, event_name, from, to, *args)
|
290
|
+
message = ["Transition: @machine=#{machine_name} @event=#{event_name} "]
|
291
|
+
unless args.empty?
|
292
|
+
message << "@with=[#{args.join(",")}] "
|
293
|
+
end
|
294
|
+
message << "#{from} -> #{to}"
|
295
|
+
info(message.join)
|
296
|
+
end
|
297
|
+
end # Logger
|
298
|
+
end # FiniteMachine
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "light-service"
|
4
|
+
|
5
|
+
module Roseflow
|
6
|
+
module Interaction
|
7
|
+
extend LightService::Organizer
|
8
|
+
|
9
|
+
def self.extended(base_class)
|
10
|
+
base_class.extend LightService::Organizer::ClassMethods
|
11
|
+
base_class.extend LightService::Organizer::Macros
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "roseflow/interaction"
|
4
|
+
require "roseflow/actions/ai/resolve_model"
|
5
|
+
require "roseflow/actions/ai/resolve_provider"
|
6
|
+
|
7
|
+
module Roseflow
|
8
|
+
module Interactions
|
9
|
+
module AI
|
10
|
+
class InitializeLlm
|
11
|
+
extend Roseflow::Interaction
|
12
|
+
|
13
|
+
def self.call(context)
|
14
|
+
with(context).reduce(actions)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.actions
|
18
|
+
[
|
19
|
+
Actions::AI::ResolveProvider,
|
20
|
+
Actions::AI::ResolveModel
|
21
|
+
]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-types"
|
4
|
+
|
5
|
+
module Types
|
6
|
+
include Dry.Types()
|
7
|
+
Number = Types::Float | Types::Integer
|
8
|
+
end
|
9
|
+
|
10
|
+
module Roseflow
|
11
|
+
module Primitives
|
12
|
+
class Vector < Dry::Struct
|
13
|
+
transform_keys(&:to_sym)
|
14
|
+
|
15
|
+
attribute :values, Types::Array.of(Types::Number)
|
16
|
+
attribute :dimensions, Types::Integer
|
17
|
+
end # Vector
|
18
|
+
end # Primitives
|
19
|
+
end # Roseflow
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "phlex"
|
4
|
+
|
5
|
+
module Roseflow
|
6
|
+
class Prompt < Phlex::SGML
|
7
|
+
private
|
8
|
+
|
9
|
+
def plain_raw(content)
|
10
|
+
unsafe_raw(content)
|
11
|
+
end
|
12
|
+
|
13
|
+
def condensed(string)
|
14
|
+
string.gsub(/\s+/, " ").strip
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Roseflow
|
4
|
+
module Text
|
5
|
+
class Completion
|
6
|
+
def initialize(input)
|
7
|
+
@input = input
|
8
|
+
end
|
9
|
+
|
10
|
+
# Creates a new completion for the given input.
|
11
|
+
def call(model:, prompt:, **options)
|
12
|
+
provider.completions(model: model, prompt: @input, **options).choices
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|