phronomy 0.7.0 → 0.8.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/.mutant.yml +8 -7
- data/CHANGELOG.md +151 -1
- data/README.md +170 -47
- data/Rakefile +33 -0
- data/benchmark/baseline.json +1 -1
- data/benchmark/bench_context_assembler.rb +2 -2
- data/benchmark/bench_regression.rb +6 -5
- data/benchmark/bench_token_estimator.rb +5 -5
- data/benchmark/bench_tool_schema.rb +1 -1
- data/benchmark/bench_vector_store.rb +1 -1
- data/docs/decisions/004-invoke-timeout-is-not-cancellation.md +24 -0
- data/docs/decisions/006-no-built-in-guardrails.md +20 -2
- data/docs/decisions/010-cooperative-first-concurrency.md +248 -0
- data/lib/phronomy/agent/base.rb +285 -137
- data/lib/phronomy/agent/checkpoint.rb +118 -0
- data/lib/phronomy/agent/concerns/suspendable.rb +15 -0
- data/lib/phronomy/agent/context/conversation/compaction_context.rb +117 -0
- data/lib/phronomy/agent/context/conversation/trigger_context.rb +43 -0
- data/lib/phronomy/agent/context/conversation/trim_context.rb +82 -0
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +102 -0
- data/lib/phronomy/agent/context/knowledge/embeddings/base.rb +45 -0
- data/lib/phronomy/agent/context/knowledge/embeddings/ruby_llm_embeddings.rb +51 -0
- data/lib/phronomy/agent/context/knowledge/loader/base.rb +31 -0
- data/lib/phronomy/agent/context/knowledge/loader/csv_loader.rb +62 -0
- data/lib/phronomy/agent/context/knowledge/loader/markdown_loader.rb +82 -0
- data/lib/phronomy/agent/context/knowledge/loader/plain_text_loader.rb +28 -0
- data/lib/phronomy/agent/context/knowledge/source/base.rb +60 -0
- data/lib/phronomy/agent/context/knowledge/source/entity_knowledge.rb +102 -0
- data/lib/phronomy/agent/context/knowledge/source/rag_knowledge.rb +63 -0
- data/lib/phronomy/agent/context/knowledge/source/static_knowledge.rb +58 -0
- data/lib/phronomy/agent/context/knowledge/splitter/base.rb +53 -0
- data/lib/phronomy/agent/context/knowledge/splitter/fixed_size_splitter.rb +57 -0
- data/lib/phronomy/agent/context/knowledge/splitter/recursive_splitter.rb +111 -0
- data/lib/phronomy/agent/context/knowledge/vector_store/async_backend.rb +116 -0
- data/lib/phronomy/agent/context/knowledge/vector_store/base.rb +95 -0
- data/lib/phronomy/agent/context/knowledge/vector_store/in_memory.rb +109 -0
- data/lib/phronomy/agent/context/knowledge/vector_store/pgvector.rb +133 -0
- data/lib/phronomy/agent/context/knowledge/vector_store/redis_search.rb +198 -0
- data/lib/phronomy/agent/fsm.rb +42 -65
- data/lib/phronomy/agent/invocation_pipeline.rb +99 -0
- data/lib/phronomy/agent/lifecycle/fsm_session.rb +251 -0
- data/lib/phronomy/agent/lifecycle/phase_machine_builder.rb +249 -0
- data/lib/phronomy/agent/react_agent.rb +27 -14
- data/lib/phronomy/agent/runner.rb +2 -2
- data/lib/phronomy/agent/tool_executor.rb +108 -0
- data/lib/phronomy/concurrency/async_queue.rb +157 -0
- data/lib/phronomy/concurrency/blocking_adapter_pool.rb +443 -0
- data/lib/phronomy/concurrency/cancellation_scope.rb +125 -0
- data/lib/phronomy/concurrency/cancellation_token.rb +140 -0
- data/lib/phronomy/concurrency/concurrency_gate.rb +157 -0
- data/lib/phronomy/concurrency/deadline.rb +65 -0
- data/lib/phronomy/concurrency/gate_registry.rb +52 -0
- data/lib/phronomy/concurrency/pool_registry.rb +57 -0
- data/lib/phronomy/configuration.rb +142 -0
- data/lib/phronomy/context.rb +2 -8
- data/lib/phronomy/diagnostics.rb +62 -0
- data/lib/phronomy/embeddings.rb +2 -2
- data/lib/phronomy/eval/runner.rb +13 -9
- data/lib/phronomy/eval/scorer/llm_judge.rb +12 -1
- data/lib/phronomy/event_loop.rb +184 -46
- data/lib/phronomy/guardrail/prompt_injection_guardrail.rb +58 -0
- data/lib/phronomy/invocation_context.rb +152 -0
- data/lib/phronomy/knowledge_source.rb +0 -5
- data/lib/phronomy/llm_adapter/base.rb +104 -0
- data/lib/phronomy/llm_adapter/ruby_llm.rb +47 -0
- data/lib/phronomy/llm_adapter.rb +20 -0
- data/lib/phronomy/{context → llm_context_window}/assembler.rb +18 -3
- data/lib/phronomy/{context → llm_context_window}/context_version_cache.rb +1 -1
- data/lib/phronomy/{context → llm_context_window}/token_budget.rb +7 -4
- data/lib/phronomy/{context → llm_context_window}/token_estimator.rb +3 -3
- data/lib/phronomy/loader.rb +4 -4
- data/lib/phronomy/metrics.rb +38 -0
- data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent → multi_agent}/orchestrator.rb +151 -126
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +149 -0
- data/lib/phronomy/{agent → multi_agent}/team_coordinator.rb +2 -2
- data/lib/phronomy/runtime/deterministic_scheduler.rb +412 -0
- data/lib/phronomy/runtime/fake_scheduler.rb +165 -0
- data/lib/phronomy/runtime/runtime_metrics.rb +117 -0
- data/lib/phronomy/runtime/scheduler.rb +98 -0
- data/lib/phronomy/runtime/scheduler_timer_adapter.rb +79 -0
- data/lib/phronomy/runtime/task_registry.rb +48 -0
- data/lib/phronomy/runtime/thread_scheduler.rb +30 -0
- data/lib/phronomy/runtime/timer_queue.rb +106 -0
- data/lib/phronomy/runtime/timer_service.rb +42 -0
- data/lib/phronomy/runtime.rb +389 -0
- data/lib/phronomy/splitter.rb +3 -3
- data/lib/phronomy/task/backend.rb +80 -0
- data/lib/phronomy/task/fiber_backend.rb +157 -0
- data/lib/phronomy/task/immediate_backend.rb +89 -0
- data/lib/phronomy/task/thread_backend.rb +84 -0
- data/lib/phronomy/task.rb +275 -0
- data/lib/phronomy/task_group.rb +265 -0
- data/lib/phronomy/testing/fake_clock.rb +109 -0
- data/lib/phronomy/testing/fake_scheduler.rb +104 -0
- data/lib/phronomy/testing/scheduler_helpers.rb +59 -0
- data/lib/phronomy/testing.rb +12 -0
- data/lib/phronomy/tool/base.rb +156 -7
- data/lib/phronomy/tool/mcp_tool.rb +47 -16
- data/lib/phronomy/tool/scope_policy.rb +50 -0
- data/lib/phronomy/tracing/null_tracer.rb +3 -1
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +34 -0
- data/lib/phronomy/vector_store.rb +2 -2
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow.rb +52 -5
- data/lib/phronomy/workflow_context.rb +37 -2
- data/lib/phronomy/workflow_runner.rb +28 -77
- data/lib/phronomy.rb +43 -0
- metadata +73 -33
- data/lib/phronomy/agent/parallel_tool_chat.rb +0 -92
- data/lib/phronomy/cancellation_token.rb +0 -92
- data/lib/phronomy/context/compaction_context.rb +0 -111
- data/lib/phronomy/context/trigger_context.rb +0 -39
- data/lib/phronomy/context/trim_context.rb +0 -75
- data/lib/phronomy/embeddings/base.rb +0 -22
- data/lib/phronomy/embeddings/ruby_llm_embeddings.rb +0 -45
- data/lib/phronomy/fsm_session.rb +0 -201
- data/lib/phronomy/knowledge_source/base.rb +0 -36
- data/lib/phronomy/knowledge_source/entity_knowledge.rb +0 -96
- data/lib/phronomy/knowledge_source/rag_knowledge.rb +0 -57
- data/lib/phronomy/knowledge_source/static_knowledge.rb +0 -52
- data/lib/phronomy/loader/base.rb +0 -25
- data/lib/phronomy/loader/csv_loader.rb +0 -56
- data/lib/phronomy/loader/markdown_loader.rb +0 -76
- data/lib/phronomy/loader/plain_text_loader.rb +0 -22
- data/lib/phronomy/prompt_template.rb +0 -96
- data/lib/phronomy/splitter/base.rb +0 -47
- data/lib/phronomy/splitter/fixed_size_splitter.rb +0 -51
- data/lib/phronomy/splitter/recursive_splitter.rb +0 -105
- data/lib/phronomy/vector_store/base.rb +0 -82
- data/lib/phronomy/vector_store/in_memory.rb +0 -93
- data/lib/phronomy/vector_store/pgvector.rb +0 -127
- data/lib/phronomy/vector_store/redis_search.rb +0 -192
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phronomy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Raizo T.C.S
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby_llm
|
|
@@ -98,6 +98,7 @@ files:
|
|
|
98
98
|
- docs/decisions/007-mcp-is-beta-stability.md
|
|
99
99
|
- docs/decisions/008-orchestrator-uses-os-threads.md
|
|
100
100
|
- docs/decisions/009-state-store-abstraction.md
|
|
101
|
+
- docs/decisions/010-cooperative-first-concurrency.md
|
|
101
102
|
- lib/phronomy.rb
|
|
102
103
|
- lib/phronomy/agent.rb
|
|
103
104
|
- lib/phronomy/agent/base.rb
|
|
@@ -108,28 +109,49 @@ files:
|
|
|
108
109
|
- lib/phronomy/agent/concerns/guardrailable.rb
|
|
109
110
|
- lib/phronomy/agent/concerns/retryable.rb
|
|
110
111
|
- lib/phronomy/agent/concerns/suspendable.rb
|
|
112
|
+
- lib/phronomy/agent/context/conversation/compaction_context.rb
|
|
113
|
+
- lib/phronomy/agent/context/conversation/trigger_context.rb
|
|
114
|
+
- lib/phronomy/agent/context/conversation/trim_context.rb
|
|
115
|
+
- lib/phronomy/agent/context/instruction/prompt_template.rb
|
|
116
|
+
- lib/phronomy/agent/context/knowledge/embeddings/base.rb
|
|
117
|
+
- lib/phronomy/agent/context/knowledge/embeddings/ruby_llm_embeddings.rb
|
|
118
|
+
- lib/phronomy/agent/context/knowledge/loader/base.rb
|
|
119
|
+
- lib/phronomy/agent/context/knowledge/loader/csv_loader.rb
|
|
120
|
+
- lib/phronomy/agent/context/knowledge/loader/markdown_loader.rb
|
|
121
|
+
- lib/phronomy/agent/context/knowledge/loader/plain_text_loader.rb
|
|
122
|
+
- lib/phronomy/agent/context/knowledge/source/base.rb
|
|
123
|
+
- lib/phronomy/agent/context/knowledge/source/entity_knowledge.rb
|
|
124
|
+
- lib/phronomy/agent/context/knowledge/source/rag_knowledge.rb
|
|
125
|
+
- lib/phronomy/agent/context/knowledge/source/static_knowledge.rb
|
|
126
|
+
- lib/phronomy/agent/context/knowledge/splitter/base.rb
|
|
127
|
+
- lib/phronomy/agent/context/knowledge/splitter/fixed_size_splitter.rb
|
|
128
|
+
- lib/phronomy/agent/context/knowledge/splitter/recursive_splitter.rb
|
|
129
|
+
- lib/phronomy/agent/context/knowledge/vector_store/async_backend.rb
|
|
130
|
+
- lib/phronomy/agent/context/knowledge/vector_store/base.rb
|
|
131
|
+
- lib/phronomy/agent/context/knowledge/vector_store/in_memory.rb
|
|
132
|
+
- lib/phronomy/agent/context/knowledge/vector_store/pgvector.rb
|
|
133
|
+
- lib/phronomy/agent/context/knowledge/vector_store/redis_search.rb
|
|
111
134
|
- lib/phronomy/agent/fsm.rb
|
|
112
|
-
- lib/phronomy/agent/
|
|
113
|
-
- lib/phronomy/agent/
|
|
114
|
-
- lib/phronomy/agent/
|
|
135
|
+
- lib/phronomy/agent/invocation_pipeline.rb
|
|
136
|
+
- lib/phronomy/agent/lifecycle/fsm_session.rb
|
|
137
|
+
- lib/phronomy/agent/lifecycle/phase_machine_builder.rb
|
|
115
138
|
- lib/phronomy/agent/react_agent.rb
|
|
116
139
|
- lib/phronomy/agent/runner.rb
|
|
117
140
|
- lib/phronomy/agent/shared_state.rb
|
|
118
141
|
- lib/phronomy/agent/suspend_signal.rb
|
|
119
|
-
- lib/phronomy/agent/
|
|
120
|
-
- lib/phronomy/
|
|
142
|
+
- lib/phronomy/agent/tool_executor.rb
|
|
143
|
+
- lib/phronomy/concurrency/async_queue.rb
|
|
144
|
+
- lib/phronomy/concurrency/blocking_adapter_pool.rb
|
|
145
|
+
- lib/phronomy/concurrency/cancellation_scope.rb
|
|
146
|
+
- lib/phronomy/concurrency/cancellation_token.rb
|
|
147
|
+
- lib/phronomy/concurrency/concurrency_gate.rb
|
|
148
|
+
- lib/phronomy/concurrency/deadline.rb
|
|
149
|
+
- lib/phronomy/concurrency/gate_registry.rb
|
|
150
|
+
- lib/phronomy/concurrency/pool_registry.rb
|
|
121
151
|
- lib/phronomy/configuration.rb
|
|
122
152
|
- lib/phronomy/context.rb
|
|
123
|
-
- lib/phronomy/
|
|
124
|
-
- lib/phronomy/context/compaction_context.rb
|
|
125
|
-
- lib/phronomy/context/context_version_cache.rb
|
|
126
|
-
- lib/phronomy/context/token_budget.rb
|
|
127
|
-
- lib/phronomy/context/token_estimator.rb
|
|
128
|
-
- lib/phronomy/context/trigger_context.rb
|
|
129
|
-
- lib/phronomy/context/trim_context.rb
|
|
153
|
+
- lib/phronomy/diagnostics.rb
|
|
130
154
|
- lib/phronomy/embeddings.rb
|
|
131
|
-
- lib/phronomy/embeddings/base.rb
|
|
132
|
-
- lib/phronomy/embeddings/ruby_llm_embeddings.rb
|
|
133
155
|
- lib/phronomy/eval.rb
|
|
134
156
|
- lib/phronomy/eval/comparison.rb
|
|
135
157
|
- lib/phronomy/eval/dataset.rb
|
|
@@ -144,50 +166,68 @@ files:
|
|
|
144
166
|
- lib/phronomy/eval/scorer/llm_judge.rb
|
|
145
167
|
- lib/phronomy/event.rb
|
|
146
168
|
- lib/phronomy/event_loop.rb
|
|
147
|
-
- lib/phronomy/fsm_session.rb
|
|
148
169
|
- lib/phronomy/generator_verifier.rb
|
|
149
170
|
- lib/phronomy/guardrail.rb
|
|
150
171
|
- lib/phronomy/guardrail/base.rb
|
|
151
172
|
- lib/phronomy/guardrail/input_guardrail.rb
|
|
152
173
|
- lib/phronomy/guardrail/output_guardrail.rb
|
|
174
|
+
- lib/phronomy/guardrail/prompt_injection_guardrail.rb
|
|
175
|
+
- lib/phronomy/invocation_context.rb
|
|
153
176
|
- lib/phronomy/knowledge_source.rb
|
|
154
|
-
- lib/phronomy/
|
|
155
|
-
- lib/phronomy/
|
|
156
|
-
- lib/phronomy/
|
|
157
|
-
- lib/phronomy/
|
|
177
|
+
- lib/phronomy/llm_adapter.rb
|
|
178
|
+
- lib/phronomy/llm_adapter/base.rb
|
|
179
|
+
- lib/phronomy/llm_adapter/ruby_llm.rb
|
|
180
|
+
- lib/phronomy/llm_context_window/assembler.rb
|
|
181
|
+
- lib/phronomy/llm_context_window/context_version_cache.rb
|
|
182
|
+
- lib/phronomy/llm_context_window/token_budget.rb
|
|
183
|
+
- lib/phronomy/llm_context_window/token_estimator.rb
|
|
158
184
|
- lib/phronomy/loader.rb
|
|
159
|
-
- lib/phronomy/
|
|
160
|
-
- lib/phronomy/
|
|
161
|
-
- lib/phronomy/
|
|
162
|
-
- lib/phronomy/
|
|
185
|
+
- lib/phronomy/metrics.rb
|
|
186
|
+
- lib/phronomy/multi_agent/handoff.rb
|
|
187
|
+
- lib/phronomy/multi_agent/orchestrator.rb
|
|
188
|
+
- lib/phronomy/multi_agent/parallel_tool_chat.rb
|
|
189
|
+
- lib/phronomy/multi_agent/team_coordinator.rb
|
|
163
190
|
- lib/phronomy/output_parser.rb
|
|
164
191
|
- lib/phronomy/output_parser/base.rb
|
|
165
192
|
- lib/phronomy/output_parser/json_parser.rb
|
|
166
193
|
- lib/phronomy/output_parser/structured_parser.rb
|
|
167
|
-
- lib/phronomy/prompt_template.rb
|
|
168
194
|
- lib/phronomy/ruby_llm_patches.rb
|
|
169
195
|
- lib/phronomy/runnable.rb
|
|
196
|
+
- lib/phronomy/runtime.rb
|
|
197
|
+
- lib/phronomy/runtime/deterministic_scheduler.rb
|
|
198
|
+
- lib/phronomy/runtime/fake_scheduler.rb
|
|
199
|
+
- lib/phronomy/runtime/runtime_metrics.rb
|
|
200
|
+
- lib/phronomy/runtime/scheduler.rb
|
|
201
|
+
- lib/phronomy/runtime/scheduler_timer_adapter.rb
|
|
202
|
+
- lib/phronomy/runtime/task_registry.rb
|
|
203
|
+
- lib/phronomy/runtime/thread_scheduler.rb
|
|
204
|
+
- lib/phronomy/runtime/timer_queue.rb
|
|
205
|
+
- lib/phronomy/runtime/timer_service.rb
|
|
170
206
|
- lib/phronomy/splitter.rb
|
|
171
|
-
- lib/phronomy/splitter/base.rb
|
|
172
|
-
- lib/phronomy/splitter/fixed_size_splitter.rb
|
|
173
|
-
- lib/phronomy/splitter/recursive_splitter.rb
|
|
174
207
|
- lib/phronomy/state_store/base.rb
|
|
175
208
|
- lib/phronomy/state_store/in_memory.rb
|
|
209
|
+
- lib/phronomy/task.rb
|
|
210
|
+
- lib/phronomy/task/backend.rb
|
|
211
|
+
- lib/phronomy/task/fiber_backend.rb
|
|
212
|
+
- lib/phronomy/task/immediate_backend.rb
|
|
213
|
+
- lib/phronomy/task/thread_backend.rb
|
|
214
|
+
- lib/phronomy/task_group.rb
|
|
215
|
+
- lib/phronomy/testing.rb
|
|
216
|
+
- lib/phronomy/testing/fake_clock.rb
|
|
217
|
+
- lib/phronomy/testing/fake_scheduler.rb
|
|
218
|
+
- lib/phronomy/testing/scheduler_helpers.rb
|
|
176
219
|
- lib/phronomy/token_usage.rb
|
|
177
220
|
- lib/phronomy/tool.rb
|
|
178
221
|
- lib/phronomy/tool/agent_tool.rb
|
|
179
222
|
- lib/phronomy/tool/base.rb
|
|
180
223
|
- lib/phronomy/tool/mcp_tool.rb
|
|
224
|
+
- lib/phronomy/tool/scope_policy.rb
|
|
181
225
|
- lib/phronomy/tracing.rb
|
|
182
226
|
- lib/phronomy/tracing/base.rb
|
|
183
227
|
- lib/phronomy/tracing/langfuse_tracer.rb
|
|
184
228
|
- lib/phronomy/tracing/null_tracer.rb
|
|
185
229
|
- lib/phronomy/tracing/open_telemetry_tracer.rb
|
|
186
230
|
- lib/phronomy/vector_store.rb
|
|
187
|
-
- lib/phronomy/vector_store/base.rb
|
|
188
|
-
- lib/phronomy/vector_store/in_memory.rb
|
|
189
|
-
- lib/phronomy/vector_store/pgvector.rb
|
|
190
|
-
- lib/phronomy/vector_store/redis_search.rb
|
|
191
231
|
- lib/phronomy/version.rb
|
|
192
232
|
- lib/phronomy/workflow.rb
|
|
193
233
|
- lib/phronomy/workflow_context.rb
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
# RubyLLM::Chat subclass that executes multiple tool calls concurrently.
|
|
6
|
-
#
|
|
7
|
-
# When the LLM returns more than one tool call in a single response, each
|
|
8
|
-
# tool is dispatched in a dedicated IO thread and all results are collected
|
|
9
|
-
# before being appended to the message history. This preserves a
|
|
10
|
-
# deterministic message order while reducing wall-clock latency when tools
|
|
11
|
-
# are IO-bound (HTTP calls, DB queries, etc.).
|
|
12
|
-
#
|
|
13
|
-
# Single-tool responses fall through to the standard sequential path via
|
|
14
|
-
# +super+, preserving all existing edge-case behaviour (Tool::Halt,
|
|
15
|
-
# forced_tool_choice, streaming, SuspendSignal, etc.).
|
|
16
|
-
#
|
|
17
|
-
# This class is used automatically when the agent is running inside an
|
|
18
|
-
# {AgentFSM} IO thread (i.e. when the +:phronomy_agent_parallel_tools+
|
|
19
|
-
# thread-local flag is +true+). It is not used for direct synchronous
|
|
20
|
-
# +invoke+ calls so that the streaming callback state remains single-threaded.
|
|
21
|
-
# @api private
|
|
22
|
-
class ParallelToolChat < RubyLLM::Chat
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
# Overrides RubyLLM::Chat#handle_tool_calls to parallelise execution
|
|
26
|
-
# when multiple tool calls are present in a single LLM response.
|
|
27
|
-
#
|
|
28
|
-
# The method preserves the three-phase protocol of the original:
|
|
29
|
-
# 1. Pre-execution callbacks (+on_new_message+, +on_tool_call+) —
|
|
30
|
-
# sequential so that the Suspendable concern's approval hook can
|
|
31
|
-
# raise +SuspendSignal+ before any tool is executed.
|
|
32
|
-
# 2. Parallel tool execution — one IO thread per tool call.
|
|
33
|
-
# 3. Post-execution callbacks and message recording — sequential,
|
|
34
|
-
# in the original tool-call order.
|
|
35
|
-
#
|
|
36
|
-
# @param response [RubyLLM::Message] the LLM response containing tool calls
|
|
37
|
-
# @yield streaming block forwarded to +complete+
|
|
38
|
-
# @api private
|
|
39
|
-
def handle_tool_calls(response, &block)
|
|
40
|
-
tool_calls = response.tool_calls.values
|
|
41
|
-
|
|
42
|
-
# Single tool: delegate to the parent implementation to preserve every
|
|
43
|
-
# edge case (forced_tool_choice, streaming, Halt, SuspendSignal…).
|
|
44
|
-
return super if tool_calls.size <= 1
|
|
45
|
-
|
|
46
|
-
# Phase 1 — pre-execution callbacks (sequential, original order).
|
|
47
|
-
# The SuspendSignal approval hook is registered via on_tool_call, so it
|
|
48
|
-
# MUST fire before execution begins.
|
|
49
|
-
tool_calls.each do |tool_call|
|
|
50
|
-
@on[:new_message]&.call
|
|
51
|
-
@on[:tool_call]&.call(tool_call)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Phase 2 — parallel tool execution.
|
|
55
|
-
# Honour the per-agent concurrency cap (max_parallel_tools DSL).
|
|
56
|
-
# Tool calls are processed in batches of at most `max` threads;
|
|
57
|
-
# batches run sequentially so the total in-flight thread count never
|
|
58
|
-
# exceeds the limit.
|
|
59
|
-
#
|
|
60
|
-
# Check for cancellation before dispatching each batch so that
|
|
61
|
-
# already-cancelled tokens do not start new LLM/tool-round-trips.
|
|
62
|
-
ct = Thread.current[:phronomy_cancellation_token]
|
|
63
|
-
max = Thread.current[:phronomy_max_parallel_tools] || 10
|
|
64
|
-
thread_results = tool_calls.each_slice(max).flat_map do |batch|
|
|
65
|
-
if ct&.cancelled?
|
|
66
|
-
raise Phronomy::CancellationError, "invocation cancelled before tool execution"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
threads = batch.map do |tool_call|
|
|
70
|
-
Thread.new { {tool_call: tool_call, result: execute_tool(tool_call)} }
|
|
71
|
-
end
|
|
72
|
-
threads.map(&:value)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Phase 3 — post-execution callbacks and message recording (sequential).
|
|
76
|
-
halt_result = nil
|
|
77
|
-
thread_results.each do |item|
|
|
78
|
-
result = item[:result]
|
|
79
|
-
@on[:tool_result]&.call(result)
|
|
80
|
-
tool_payload = result.is_a?(RubyLLM::Tool::Halt) ? result.content : result
|
|
81
|
-
content = content_like?(tool_payload) ? tool_payload : tool_payload.to_s
|
|
82
|
-
message = add_message(role: :tool, content: content, tool_call_id: item[:tool_call].id)
|
|
83
|
-
@on[:end_message]&.call(message)
|
|
84
|
-
halt_result = result if result.is_a?(RubyLLM::Tool::Halt)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
reset_tool_choice if forced_tool_choice?
|
|
88
|
-
halt_result || complete(&block)
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
# Provides cooperative cancellation for agent invocations.
|
|
5
|
-
#
|
|
6
|
-
# Pass a token to an agent via +config: { cancellation_token: token }+.
|
|
7
|
-
# The agent checks the token before each LLM call and raises
|
|
8
|
-
# {Phronomy::CancellationError} when the token is cancelled or the
|
|
9
|
-
# optional deadline has passed.
|
|
10
|
-
#
|
|
11
|
-
# A token may be shared across multiple agent invocations and across threads;
|
|
12
|
-
# all access to internal state is protected by a Mutex.
|
|
13
|
-
#
|
|
14
|
-
# @example Explicit cancel from another thread
|
|
15
|
-
# token = Phronomy::CancellationToken.new
|
|
16
|
-
# Thread.new { sleep 5; token.cancel! }
|
|
17
|
-
# result = agent.invoke("...", config: { cancellation_token: token })
|
|
18
|
-
#
|
|
19
|
-
# @example Hard deadline via monotonic clock (recommended)
|
|
20
|
-
# token = Phronomy::CancellationToken.timeout_after(30)
|
|
21
|
-
# result = agent.invoke("...", config: { cancellation_token: token })
|
|
22
|
-
#
|
|
23
|
-
# @example Hard deadline via wall-clock (legacy)
|
|
24
|
-
# token = Phronomy::CancellationToken.new(deadline: Time.now + 30)
|
|
25
|
-
# result = agent.invoke("...", config: { cancellation_token: token })
|
|
26
|
-
#
|
|
27
|
-
# @example Propagate to parallel workers
|
|
28
|
-
# token = Phronomy::CancellationToken.new
|
|
29
|
-
# orchestrator.dispatch_parallel(task1, task2, cancellation_token: token)
|
|
30
|
-
class CancellationToken
|
|
31
|
-
# Returns a new token that will expire after +seconds+ seconds, measured
|
|
32
|
-
# with the monotonic clock (+Process::CLOCK_MONOTONIC+). Unlike constructing
|
|
33
|
-
# a token with +deadline: Time.now + seconds+, this factory is immune to NTP
|
|
34
|
-
# adjustments and DST transitions.
|
|
35
|
-
#
|
|
36
|
-
# @param seconds [Numeric] duration in seconds until the token expires.
|
|
37
|
-
# @return [CancellationToken]
|
|
38
|
-
# @api public
|
|
39
|
-
def self.timeout_after(seconds)
|
|
40
|
-
monotonic_deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
|
|
41
|
-
new(monotonic_deadline: monotonic_deadline)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# @param deadline [Time, nil] optional wall-clock deadline; the token reports
|
|
45
|
-
# +cancelled?+ as +true+ once +Time.now >= deadline+. Prefer
|
|
46
|
-
# {.timeout_after} for duration-based cancellation.
|
|
47
|
-
# @param monotonic_deadline [Float, nil] internal monotonic timestamp set by
|
|
48
|
-
# {.timeout_after}; prefer that factory method over passing this directly.
|
|
49
|
-
# @api public
|
|
50
|
-
def initialize(deadline: nil, monotonic_deadline: nil)
|
|
51
|
-
@cancelled = false
|
|
52
|
-
@deadline = deadline
|
|
53
|
-
@monotonic_deadline = monotonic_deadline
|
|
54
|
-
@mutex = Mutex.new
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# @return [Time, nil] the wall-clock deadline passed to {#initialize}, or +nil+.
|
|
58
|
-
attr_reader :deadline
|
|
59
|
-
|
|
60
|
-
# Mark the token as cancelled. Thread-safe; may be called from any thread.
|
|
61
|
-
# @return [self]
|
|
62
|
-
# @api public
|
|
63
|
-
def cancel!
|
|
64
|
-
@mutex.synchronize { @cancelled = true }
|
|
65
|
-
self
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Returns +true+ when the token has been explicitly cancelled via {#cancel!},
|
|
69
|
-
# when the wall-clock deadline has passed, or when the monotonic deadline
|
|
70
|
-
# (set by {.timeout_after}) has elapsed. Thread-safe.
|
|
71
|
-
# @return [Boolean]
|
|
72
|
-
# @api public
|
|
73
|
-
def cancelled?
|
|
74
|
-
return true if @mutex.synchronize { @cancelled }
|
|
75
|
-
return true if !@deadline.nil? && Time.now >= @deadline
|
|
76
|
-
!@monotonic_deadline.nil? &&
|
|
77
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC) >= @monotonic_deadline
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Raises {Phronomy::CancellationError} if the token is cancelled.
|
|
81
|
-
# A convenience method for cooperative cancellation checks inside tools,
|
|
82
|
-
# RAG loaders, and hooks, replacing the +if cancelled? then raise+ pattern.
|
|
83
|
-
#
|
|
84
|
-
# @param message [String] optional error message
|
|
85
|
-
# @return [nil] when the token is not cancelled
|
|
86
|
-
# @raise [Phronomy::CancellationError] when the token is cancelled
|
|
87
|
-
# @api public
|
|
88
|
-
def raise_if_cancelled!(message = "invocation cancelled")
|
|
89
|
-
raise Phronomy::CancellationError, message if cancelled?
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Context
|
|
5
|
-
# Context object passed to the +on_compact+ callback registered on an agent.
|
|
6
|
-
#
|
|
7
|
-
# The callback calls #compact one or more times to specify which ranges of
|
|
8
|
-
# messages to replace with a summary. Each call:
|
|
9
|
-
# 1. Yields the selected message elements to the block.
|
|
10
|
-
# 2. Receives the block's return value as the summary text.
|
|
11
|
-
# 3. Persists a compaction record to the memory store (if available).
|
|
12
|
-
# 4. Updates #result_messages so that the compacted range is replaced
|
|
13
|
-
# by a single +:system+ summary message.
|
|
14
|
-
#
|
|
15
|
-
# The agent reads #result_messages after the callback returns and uses it
|
|
16
|
-
# as the new message list for this invocation.
|
|
17
|
-
#
|
|
18
|
-
# @example Summarise the oldest half of the conversation
|
|
19
|
-
# on_compact do |ctx|
|
|
20
|
-
# half = ctx.message_elements.length / 2
|
|
21
|
-
# ctx.compact(0...half) do |elements|
|
|
22
|
-
# texts = elements.map { |e| "#{e[:role]}: #{e[:message].content}" }.join("\n")
|
|
23
|
-
# "Summary of earlier conversation:\n#{texts}"
|
|
24
|
-
# end
|
|
25
|
-
# end
|
|
26
|
-
class CompactionContext
|
|
27
|
-
# @return [Array<Hash>] message elements at compaction time
|
|
28
|
-
attr_reader :message_elements
|
|
29
|
-
|
|
30
|
-
# @return [Phronomy::Context::TokenBudget, nil]
|
|
31
|
-
attr_reader :budget
|
|
32
|
-
|
|
33
|
-
# @return [Integer] total estimated token count before compaction
|
|
34
|
-
attr_reader :total_tokens
|
|
35
|
-
|
|
36
|
-
# The current message list to be used after all compact calls have been made.
|
|
37
|
-
# Updated by each call to #compact.
|
|
38
|
-
#
|
|
39
|
-
# @return [Array]
|
|
40
|
-
attr_reader :result_messages
|
|
41
|
-
|
|
42
|
-
# @param message_elements [Array<Hash>]
|
|
43
|
-
# each element: { seq: Integer, message: Object, tokens: Integer, role: Symbol }
|
|
44
|
-
# @param budget [Phronomy::Context::TokenBudget, nil]
|
|
45
|
-
# @param thread_id [String, nil] used when saving compaction records
|
|
46
|
-
# @param memory [Object, nil] memory object; must respond to #save_compaction
|
|
47
|
-
# for compaction records to be persisted
|
|
48
|
-
# @api private
|
|
49
|
-
def initialize(message_elements:, budget:, thread_id: nil, memory: nil)
|
|
50
|
-
@message_elements = message_elements.dup
|
|
51
|
-
@budget = budget
|
|
52
|
-
@total_tokens = message_elements.sum { |e| e[:tokens] }
|
|
53
|
-
@thread_id = thread_id
|
|
54
|
-
@memory = memory
|
|
55
|
-
@result_messages = @message_elements.map { |e| e[:message] }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Replace a range of messages with a summary produced by the block.
|
|
59
|
-
#
|
|
60
|
-
# The block receives the selected Array<Hash> elements and must return a
|
|
61
|
-
# String that serves as the summary text. After the call, #result_messages
|
|
62
|
-
# reflects the replacement.
|
|
63
|
-
#
|
|
64
|
-
# If the memory object responds to #save_compaction, a compaction record
|
|
65
|
-
# { start_seq:, end_seq:, summary_text: } is persisted for auditability.
|
|
66
|
-
#
|
|
67
|
-
# @param range [Range, Integer] index range into message_elements (0-based)
|
|
68
|
-
# @yieldparam elements [Array<Hash>] the selected message elements
|
|
69
|
-
# @yieldreturn [String] summary text to replace the selected messages
|
|
70
|
-
# @return [Array] the updated result_messages array
|
|
71
|
-
# @api private
|
|
72
|
-
def compact(range)
|
|
73
|
-
# Normalise: Integer index → single-element Array; Range → Array slice.
|
|
74
|
-
raw = @message_elements[range]
|
|
75
|
-
elements = if raw.is_a?(Array)
|
|
76
|
-
raw
|
|
77
|
-
elsif raw.nil?
|
|
78
|
-
[]
|
|
79
|
-
else
|
|
80
|
-
[raw]
|
|
81
|
-
end
|
|
82
|
-
return @result_messages if elements.empty?
|
|
83
|
-
|
|
84
|
-
summary_text = yield(elements).to_s
|
|
85
|
-
|
|
86
|
-
start_seq = elements.first[:seq]
|
|
87
|
-
end_seq = elements.last[:seq]
|
|
88
|
-
|
|
89
|
-
if @memory && @thread_id && @memory.respond_to?(:save_compaction)
|
|
90
|
-
@memory.save_compaction(
|
|
91
|
-
thread_id: @thread_id,
|
|
92
|
-
start_seq: start_seq,
|
|
93
|
-
end_seq: end_seq,
|
|
94
|
-
summary_text: summary_text
|
|
95
|
-
)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Compute the last included index in the original @message_elements array.
|
|
99
|
-
last_idx = if range.is_a?(Range)
|
|
100
|
-
range.exclude_end? ? range.last - 1 : range.last
|
|
101
|
-
else
|
|
102
|
-
range.to_i
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
remaining = (@message_elements[(last_idx + 1)..] || []).map { |e| e[:message] }
|
|
106
|
-
summary_msg = RubyLLM::Message.new(role: :system, content: summary_text)
|
|
107
|
-
@result_messages = [summary_msg] + remaining
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Context
|
|
5
|
-
# Read-only context passed to the +on_compaction_trigger+ callback.
|
|
6
|
-
#
|
|
7
|
-
# The callback inspects the current message list and budget, then returns
|
|
8
|
-
# a truthy value to trigger compaction or a falsy value to skip it.
|
|
9
|
-
#
|
|
10
|
-
# No mutations are allowed through this object; use CompactionContext
|
|
11
|
-
# (passed to +on_compact+) for actual modifications.
|
|
12
|
-
#
|
|
13
|
-
# @example Trigger compaction when messages exceed 80% of the input budget
|
|
14
|
-
# on_compaction_trigger do |ctx|
|
|
15
|
-
# limit = ctx.budget&.available(used: 0) || Float::INFINITY
|
|
16
|
-
# ctx.total_tokens > limit * 0.8
|
|
17
|
-
# end
|
|
18
|
-
class TriggerContext
|
|
19
|
-
# @return [Array<Hash>] frozen snapshot of message elements
|
|
20
|
-
# each element: { seq: Integer, message: Object, tokens: Integer, role: Symbol }
|
|
21
|
-
attr_reader :message_elements
|
|
22
|
-
|
|
23
|
-
# @return [Phronomy::Context::TokenBudget, nil] token budget for this invocation
|
|
24
|
-
attr_reader :budget
|
|
25
|
-
|
|
26
|
-
# @return [Integer] total estimated token count of all message elements
|
|
27
|
-
attr_reader :total_tokens
|
|
28
|
-
|
|
29
|
-
# @param message_elements [Array<Hash>]
|
|
30
|
-
# @param budget [Phronomy::Context::TokenBudget, nil]
|
|
31
|
-
# @api private
|
|
32
|
-
def initialize(message_elements:, budget:)
|
|
33
|
-
@message_elements = message_elements.dup.freeze
|
|
34
|
-
@budget = budget
|
|
35
|
-
@total_tokens = message_elements.sum { |e| e[:tokens] }
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Context
|
|
5
|
-
# Context object passed to the +on_trim+ callback registered on an agent class.
|
|
6
|
-
#
|
|
7
|
-
# The callback receives a TrimContext and may call #remove to drop specific
|
|
8
|
-
# messages from the conversation before the LLM is called. Changes affect
|
|
9
|
-
# only the current invocation; the underlying memory store is not modified.
|
|
10
|
-
#
|
|
11
|
-
# Message elements are identified by a +:seq+ integer that is assigned
|
|
12
|
-
# sequentially (0-based) when messages are loaded from memory each turn.
|
|
13
|
-
#
|
|
14
|
-
# @example Remove the oldest two messages when the budget is tight
|
|
15
|
-
# on_trim do |ctx|
|
|
16
|
-
# if ctx.total_tokens > ctx.budget.available(used: 0) * 0.9
|
|
17
|
-
# seqs_to_drop = ctx.message_elements.first(2).map { |e| e[:seq] }
|
|
18
|
-
# ctx.remove(seqs_to_drop)
|
|
19
|
-
# end
|
|
20
|
-
# end
|
|
21
|
-
class TrimContext
|
|
22
|
-
# @return [Phronomy::Context::TokenBudget, nil] token budget for this invocation
|
|
23
|
-
attr_reader :budget
|
|
24
|
-
|
|
25
|
-
# @return [Integer] total estimated token count of all current message elements
|
|
26
|
-
attr_reader :total_tokens
|
|
27
|
-
|
|
28
|
-
# @param message_elements [Array<Hash>]
|
|
29
|
-
# each element: { seq: Integer, message: Object, tokens: Integer, role: Symbol }
|
|
30
|
-
# @param budget [Phronomy::Context::TokenBudget, nil]
|
|
31
|
-
# @api private
|
|
32
|
-
def initialize(message_elements:, budget:)
|
|
33
|
-
@message_elements = message_elements.dup
|
|
34
|
-
@budget = budget
|
|
35
|
-
recalculate!
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Returns a snapshot of the current message elements (defensive copy).
|
|
39
|
-
# Each element is a Hash with +:seq+, +:message+, +:tokens+, and +:role+.
|
|
40
|
-
#
|
|
41
|
-
# @return [Array<Hash>]
|
|
42
|
-
# @api private
|
|
43
|
-
def message_elements
|
|
44
|
-
@message_elements.dup
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Remove messages identified by seq numbers.
|
|
48
|
-
# Calling this multiple times accumulates removals.
|
|
49
|
-
#
|
|
50
|
-
# @param seqs [Integer, Array<Integer>] seq number(s) to remove
|
|
51
|
-
# @return [self]
|
|
52
|
-
# @api private
|
|
53
|
-
def remove(seqs)
|
|
54
|
-
seqs_set = Array(seqs).to_set
|
|
55
|
-
@message_elements.reject! { |e| seqs_set.include?(e[:seq]) }
|
|
56
|
-
recalculate!
|
|
57
|
-
self
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Convenience: returns the plain message objects (without element metadata).
|
|
61
|
-
#
|
|
62
|
-
# @return [Array]
|
|
63
|
-
# @api private
|
|
64
|
-
def messages
|
|
65
|
-
@message_elements.map { |e| e[:message] }
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
private
|
|
69
|
-
|
|
70
|
-
def recalculate!
|
|
71
|
-
@total_tokens = @message_elements.sum { |e| e[:tokens] }
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Embeddings
|
|
5
|
-
# Abstract interface for embedding adapters.
|
|
6
|
-
#
|
|
7
|
-
# Concrete implementations must override {#embed} and return a vector
|
|
8
|
-
# as an +Array<Float>+.
|
|
9
|
-
class Base
|
|
10
|
-
# Embed the given text and return a vector representation.
|
|
11
|
-
#
|
|
12
|
-
# @param text [String] the text to embed
|
|
13
|
-
# @param cancellation_token [Phronomy::CancellationToken, nil] optional; raises CancellationError when cancelled
|
|
14
|
-
# @return [Array<Float>] the embedding vector
|
|
15
|
-
# @api public
|
|
16
|
-
def embed(text, cancellation_token = nil)
|
|
17
|
-
cancellation_token&.raise_if_cancelled!
|
|
18
|
-
raise NotImplementedError, "#{self.class}#embed is not implemented"
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Embeddings
|
|
5
|
-
# Embeddings adapter backed by RubyLLM.
|
|
6
|
-
#
|
|
7
|
-
# Delegates to +RubyLLM.embed+ and returns the resulting vector as an
|
|
8
|
-
# +Array<Float>+.
|
|
9
|
-
#
|
|
10
|
-
# @example Default model
|
|
11
|
-
# embeddings = Phronomy::Embeddings::RubyLLMEmbeddings.new
|
|
12
|
-
# vector = embeddings.embed("Hello, world!")
|
|
13
|
-
#
|
|
14
|
-
# @example Explicit model
|
|
15
|
-
# embeddings = Phronomy::Embeddings::RubyLLMEmbeddings.new(model: "text-embedding-3-small")
|
|
16
|
-
# vector = embeddings.embed("Hello, world!")
|
|
17
|
-
class RubyLLMEmbeddings < Base
|
|
18
|
-
# @param model [String, nil] embedding model identifier; nil uses the RubyLLM default
|
|
19
|
-
# @param provider [Symbol, nil] provider override (e.g. :openai); nil uses the RubyLLM default
|
|
20
|
-
# @param assume_model_exists [Boolean] when true, skips RubyLLM model-registry validation
|
|
21
|
-
# (useful for locally hosted models not in the registry)
|
|
22
|
-
# @api public
|
|
23
|
-
def initialize(model: nil, provider: nil, assume_model_exists: false)
|
|
24
|
-
@model = model
|
|
25
|
-
@provider = provider
|
|
26
|
-
@assume_model_exists = assume_model_exists
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Embed text via RubyLLM.
|
|
30
|
-
#
|
|
31
|
-
# @param text [String]
|
|
32
|
-
# @param cancellation_token [Phronomy::CancellationToken, nil] optional; raises CancellationError when cancelled
|
|
33
|
-
# @return [Array<Float>]
|
|
34
|
-
# @api public
|
|
35
|
-
def embed(text, cancellation_token = nil)
|
|
36
|
-
cancellation_token&.raise_if_cancelled!
|
|
37
|
-
opts = {}
|
|
38
|
-
opts[:model] = @model if @model
|
|
39
|
-
opts[:provider] = @provider if @provider
|
|
40
|
-
opts[:assume_model_exists] = true if @assume_model_exists
|
|
41
|
-
RubyLLM.embed(text, **opts).vectors
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|