phronomy 0.20.0 → 0.22.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.
@@ -3,20 +3,29 @@
3
3
  module Phronomy
4
4
  # A thread-free asynchronous completion handle.
5
5
  #
6
- # Task no longer executes work. Execution belongs to EventLoop/FSMSession or
7
- # OffloadPool. Task only represents completion, failure, cancellation,
8
- # callbacks and a blocking wait for external callers.
6
+ # Task does not execute work. Execution belongs to EventLoop/FSMSession or
7
+ # OffloadPool. Task represents completion, failure, cancellation, callbacks,
8
+ # and a blocking wait for callers outside EventLoop.
9
+ #
10
+ # Framework components own Task settlement. Application code should observe a
11
+ # Task through {#wait_result}, {#on_complete}, {#map}, and state readers rather
12
+ # than calling {#complete}, {#fail}, or {#cancel!}. Operation-wide cancellation
13
+ # is supplied through the CancellationToken accepted by the API that created
14
+ # the Task.
9
15
  class Task
10
16
  STATES = %i[pending completed failed cancelled].freeze
11
17
  TERMINAL_STATES = %i[completed failed cancelled].freeze
12
18
  private_constant :TERMINAL_STATES
13
19
 
20
+ # Creates an unsettled completion handle for framework-owned execution.
21
+ # @api private
14
22
  def self.deferred(name: nil, parent: nil)
15
23
  new(name: name, parent: parent)
16
24
  end
17
25
 
18
26
  attr_reader :name, :parent
19
27
 
28
+ # @api private
20
29
  def initialize(name: nil, parent: nil)
21
30
  @name = name
22
31
  @parent = parent
@@ -30,20 +39,35 @@ module Phronomy
30
39
  parent&.register_child(self)
31
40
  end
32
41
 
42
+ # @return [Symbol] :pending, :completed, :failed, or :cancelled
43
+ # @api public
33
44
  def status
34
45
  @mutex.synchronize { @status }
35
46
  end
36
47
 
48
+ # @return [Boolean] whether the Task has reached a terminal state
49
+ # @api public
37
50
  def done?
38
51
  @mutex.synchronize { TERMINAL_STATES.include?(@status) }
39
52
  end
40
53
 
54
+ # @return [Boolean] whether the Task has not yet reached a terminal state
55
+ # @api public
41
56
  def alive?
42
57
  !done?
43
58
  end
44
59
 
45
- # Blocks the calling thread until settlement. EventLoop is never allowed to
46
- # wait for a Task; it must continue through explicit events instead.
60
+ # Blocks the calling thread until settlement.
61
+ #
62
+ # EventLoop is never allowed to wait for a Task; framework continuation must
63
+ # proceed through explicit events. The optional timeout is waiter-local: it
64
+ # does not settle or cancel the Task.
65
+ #
66
+ # @param timeout [Numeric, nil] maximum seconds this caller will block
67
+ # @return [Object] the completed value
68
+ # @raise [Phronomy::TimeoutError] when the waiter-local timeout expires
69
+ # @raise [Exception] the error that settled the Task
70
+ # @api public
47
71
  def wait_result(timeout: nil)
48
72
  if Phronomy::Runtime.in_event_loop_context? && !done?
49
73
  raise Phronomy::EventLoopReentrancyError,
@@ -71,8 +95,9 @@ module Phronomy
71
95
  value
72
96
  end
73
97
 
74
- # Compatibility wait that does not re-raise the task error.
98
+ # Compatibility wait that does not re-raise the Task error.
75
99
  # Returns self when settled, nil on timeout.
100
+ # @api private
76
101
  def join(limit = nil)
77
102
  if Phronomy::Runtime.in_event_loop_context? && !done?
78
103
  raise Phronomy::EventLoopReentrancyError,
@@ -96,8 +121,15 @@ module Phronomy
96
121
 
97
122
  # Registers an independent completion notification.
98
123
  #
124
+ # The callback execution thread is not guaranteed. It may be the caller that
125
+ # registers after settlement, an OffloadPool worker, or a framework control
126
+ # thread. Callbacks must therefore be thread-safe and should complete quickly.
99
127
  # A callback failure is logged and does not suppress delivery to other
100
128
  # completion callbacks or change the Task's already-settled result.
129
+ #
130
+ # @yield [value, error]
131
+ # @return [self]
132
+ # @api public
101
133
  def on_complete(&callback)
102
134
  raise ArgumentError, "on_complete requires a block" unless callback
103
135
 
@@ -113,15 +145,25 @@ module Phronomy
113
145
  self
114
146
  end
115
147
 
148
+ # Settles this Task successfully. Framework-owned settlement API.
149
+ # @api private
116
150
  def complete(value = nil)
117
151
  settle!(:completed, value: value)
118
152
  end
119
153
 
154
+ # Settles this Task with a failure. Framework-owned settlement API.
155
+ # @api private
120
156
  def fail(error)
121
157
  raise ArgumentError, "error is required" unless error
122
158
  settle!(:failed, error: error)
123
159
  end
124
160
 
161
+ # Settles this Task as cancelled. Framework-owned settlement API.
162
+ #
163
+ # This method does not propagate backwards into a CancellationToken that may
164
+ # have been used to create the Task. Tokens can be shared across operations;
165
+ # operation-wide cancellation is owned by the creating API.
166
+ # @api private
125
167
  def cancel!(error = Phronomy::CancellationError.new("Task cancelled"))
126
168
  changed = settle!(:cancelled, error: error)
127
169
  if changed
@@ -131,6 +173,8 @@ module Phronomy
131
173
  self
132
174
  end
133
175
 
176
+ # Creates a derived Task by transforming this Task's successful result.
177
+ # @api public
134
178
  def map(&block)
135
179
  raise ArgumentError, "map requires a block" unless block
136
180
 
@@ -3,10 +3,9 @@
3
3
  module Phronomy
4
4
  # Carries all per-invocation context values through the call stack.
5
5
  #
6
- # +InvocationContext+ is a plain value object (struct-like, frozen on
7
- # creation) that replaces ad-hoc +Thread.current[...]+ propagation.
8
- # Pass it explicitly wherever context needs to cross a method boundary
9
- # or be handed to a child {Task} / {TaskGroup}.
6
+ # +InvocationContext+ is a plain struct-like value carrier that replaces
7
+ # ad-hoc +Thread.current[...]+ propagation.
8
+ # Pass it explicitly wherever context needs to cross a method boundary.
10
9
  #
11
10
  # @example Build a context for a new agent invocation
12
11
  # ctx = Phronomy::InvocationContext.new(
@@ -14,40 +13,12 @@ module Phronomy
14
13
  # cancellation_token: Phronomy::Concurrency::CancellationToken.timeout_after(30)
15
14
  # )
16
15
  # agent.invoke("Hello", invocation_context: ctx)
16
+ #
17
+ # @api public
17
18
  class InvocationContext
18
- # @return [String, nil] conversation / workflow thread identifier
19
- attr_reader :thread_id
20
-
21
- # @return [String, nil] session identifier (e.g. Rails session id)
22
- attr_reader :session_id
23
-
24
- # @return [String, nil] end-user identifier for tracing / audit
25
- attr_reader :user_id
26
-
27
- # @return [CancellationToken, nil]
28
- attr_reader :cancellation_token
29
-
30
- # @return [Deadline, nil]
31
- attr_reader :deadline
32
-
33
- # @return [Object, nil] OpenTelemetry / tracing span
34
- attr_reader :tracer_span
35
-
36
- # @return [Integer, nil] max tokens the agent may consume this invocation
37
- attr_reader :token_budget
38
-
39
- # @return [#call, nil] invocation-specific Tool approval policy. The callable
40
- # receives Phronomy::Agent::ApprovalEvaluationRequest.
41
- attr_reader :approval_policy
42
-
43
- # @return [Object, nil] redaction policy applied to tool args / results
44
- attr_reader :redaction_policy
45
-
46
- # @return [String, nil] unique identifier for this task in the trace tree
47
- attr_reader :task_id
48
-
49
- # @return [String, nil] task_id of the parent span / task
50
- attr_reader :parent_task_id
19
+ attr_reader :thread_id, :session_id, :user_id, :cancellation_token,
20
+ :deadline, :tracer_span, :token_budget, :approval_policy,
21
+ :redaction_policy, :task_id, :parent_task_id
51
22
 
52
23
  # @param thread_id [String, nil]
53
24
  # @param session_id [String, nil]
@@ -56,11 +27,11 @@ module Phronomy
56
27
  # @param deadline [Deadline, nil]
57
28
  # @param tracer_span [Object, nil]
58
29
  # @param token_budget [Integer, nil]
59
- # @param approval_policy [#call, nil] invocation-specific Tool approval policy
30
+ # @param approval_policy [#call, nil]
60
31
  # @param redaction_policy [Object, nil]
61
32
  # @param task_id [String, nil]
62
33
  # @param parent_task_id [String, nil]
63
- # @api private
34
+ # @api public
64
35
  def initialize(
65
36
  thread_id: nil,
66
37
  session_id: nil,
@@ -88,10 +59,6 @@ module Phronomy
88
59
  end
89
60
 
90
61
  # Returns a new +InvocationContext+ with the given attributes merged in.
91
- # All other attributes are carried over unchanged.
92
- #
93
- # @param overrides [Hash] keyword arguments to override
94
- # @return [InvocationContext]
95
62
  # @api private
96
63
  def merge(**overrides)
97
64
  InvocationContext.new(
@@ -110,21 +77,13 @@ module Phronomy
110
77
  end
111
78
 
112
79
  # Convenience: returns the cancellation token or a new never-cancelled token.
113
- # @return [CancellationToken]
114
80
  # @api private
115
81
  def effective_cancellation_token
116
82
  @cancellation_token || Phronomy::Concurrency::CancellationToken.new
117
83
  end
118
84
 
119
85
  # Returns the cancellation token to use for an invocation, taking both the
120
- # explicit +cancellation_token+ and the +deadline+ into account.
121
- #
122
- # - When +cancellation_token+ is set, it is returned unchanged.
123
- # - When only +deadline+ is set, a new {CancellationToken} is created and
124
- # the deadline is attached to it via {Deadline#attach_to}.
125
- # - When neither is set, returns +nil+.
126
- #
127
- # @return [CancellationToken, nil]
86
+ # explicit cancellation_token and deadline into account.
128
87
  # @api private
129
88
  def effective_timeout_token
130
89
  return @cancellation_token if @cancellation_token
@@ -2,56 +2,57 @@
2
2
 
3
3
  module Phronomy
4
4
  module LLMAdapter
5
- # Abstract base class for LLM adapters.
5
+ # Beta extension SPI for LLM call adapters.
6
6
  #
7
- # Subclasses must implement {#complete} and {#stream}. The adapter or the
7
+ # External adapters implement {#complete} and {#stream}. The adapter or the
8
8
  # underlying provider client owns transport timeout, retry, backoff, and
9
- # rate-limit behavior. Phronomy supplies cooperative cancellation and
10
- # isolates synchronous provider calls in {OffloadPool}.
9
+ # rate-limit behavior. Phronomy owns cooperative cancellation and isolates
10
+ # synchronous provider calls in {OffloadPool}.
11
11
  #
12
- # The agent pipeline calls {#complete_async} / {#stream_async} which wrap
13
- # those methods in an {OffloadPool} submission.
12
+ # The Agent pipeline calls the framework-owned {#complete_async} and
13
+ # {#stream_async} wrappers. Adapter implementers do not need to depend on
14
+ # EventLoop, FSMSession, OffloadPool, AgentInvocation, or ExecutionCoordinator.
15
+ #
16
+ # The current input to this SPI is the configured/materialized chat runtime
17
+ # object. Formalizing this SPI therefore does not imply a provider-neutral
18
+ # replacement for RubyLLMMaterializer.
19
+ #
20
+ # @api public
14
21
  class Base
15
22
  # Performs a blocking (non-streaming) LLM completion.
16
- # Implementors must call +chat.ask(message)+ (or equivalent) and
17
- # return the response object.
18
23
  #
19
- # @param chat [Object] the configured chat session object
20
- # @param message [String] the user message
24
+ # Implementors call the configured chat/runtime client and return its
25
+ # response object. Transport/retry policy remains adapter-owned.
26
+ #
27
+ # @param chat [Object] the configured/materialized chat runtime object
28
+ # @param message [String, nil] user message, or nil to continue without adding a new user turn
21
29
  # @param config [Hash] invocation config (e.g. +:cancellation_token+)
22
30
  # @return [Object] LLM response object
23
31
  # @raise [NotImplementedError]
24
- # @api private
32
+ # @api public
25
33
  def complete(chat, message, config: {})
26
34
  raise NotImplementedError, "#{self.class}#complete is not implemented"
27
35
  end
28
36
 
29
37
  # Performs a blocking streaming LLM completion.
30
- # Implementors must call +chat.ask(message) { |chunk| block.call(chunk) }+
31
- # (or equivalent) and return the response object.
32
38
  #
33
- # @param chat [Object] the configured chat session object
34
- # @param message [String] the user message
39
+ # @param chat [Object] the configured/materialized chat runtime object
40
+ # @param message [String, nil] user message, or nil to continue without adding a new user turn
35
41
  # @param config [Hash] invocation config
36
42
  # @yield [chunk] streaming chunk from the LLM
37
43
  # @return [Object] LLM response object
38
44
  # @raise [NotImplementedError]
39
- # @api private
45
+ # @api public
40
46
  def stream(chat, message, config: {}, &block)
41
47
  raise NotImplementedError, "#{self.class}#stream is not implemented"
42
48
  end
43
49
 
44
- # Submits a non-streaming LLM call to {OffloadPool} and returns
45
- # an {OffloadPool::PendingOperation}.
50
+ # Submits a non-streaming LLM call to {OffloadPool}.
46
51
  #
47
52
  # Transport timeout and retry remain the responsibility of the adapter or
48
53
  # provider client; Phronomy does not attach an additional operation timeout.
49
54
  #
50
- # @param chat [Object] configured chat session
51
- # @param message [String] user message
52
- # @param config [Hash] invocation config
53
- # @param pool [OffloadPool] pool to submit to
54
- # @return [OffloadPool::PendingOperation]
55
+ # @return [Phronomy::Task] caller-facing completion handle
55
56
  # @api private
56
57
  def complete_async(chat, message, config: {}, pool: default_pool)
57
58
  token = config[:cancellation_token]
@@ -60,18 +61,14 @@ module Phronomy
60
61
  end
61
62
  end
62
63
 
63
- # Submits a streaming LLM call to {OffloadPool} and returns
64
- # an {OffloadPool::PendingOperation}.
65
- #
66
- # The block is invoked on an OffloadPool worker thread. Agent code must
67
- # pass only a lightweight internal sink that posts a value to EventLoop;
68
- # Application callbacks must never be passed directly to this method.
64
+ # Submits a streaming LLM call to {OffloadPool}.
69
65
  #
70
- # Transport timeout and retry remain the responsibility of the adapter or
71
- # provider client; Phronomy does not attach an additional operation timeout.
66
+ # The block is invoked on an OffloadPool worker thread. Agent code must pass
67
+ # only a lightweight internal sink that posts a value to EventLoop;
68
+ # application callbacks must never be passed directly to this method.
72
69
  #
73
70
  # @yield [chunk] streaming chunk on the worker thread
74
- # @return [OffloadPool::PendingOperation]
71
+ # @return [Phronomy::Task] caller-facing completion handle
75
72
  # @api private
76
73
  def stream_async(chat, message, config: {}, pool: default_pool, &block)
77
74
  raise ArgumentError, "stream_async requires a block" unless block
@@ -2,42 +2,43 @@
2
2
 
3
3
  module Phronomy
4
4
  module LLMAdapter
5
- # LLM adapter that delegates to the RubyLLM blocking client.
5
+ # Default LLMAdapter SPI implementation backed by RubyLLM.
6
6
  #
7
- # This is the default adapter used by Phronomy agents. It wraps
8
- # +chat.ask+ (and its streaming variant) so that the synchronous provider
9
- # call runs inside {OffloadPool} rather than on the EventLoop thread.
7
+ # The synchronous +chat.ask+ / +chat.complete+ calls are invoked through the
8
+ # framework-owned async bridge in {LLMAdapter::Base}, so adapter consumers do
9
+ # not need to manage OffloadPool themselves.
10
10
  #
11
11
  # @example Explicitly configuring this adapter
12
12
  # Phronomy.configure do |c|
13
13
  # c.llm_adapter = Phronomy::LLMAdapter::RubyLLM.new
14
14
  # end
15
+ #
16
+ # @api public
15
17
  class RubyLLM < Base
16
18
  # Delegates to +chat.ask(message)+ or +chat.complete+ when message is nil.
17
19
  #
18
20
  # Passing +nil+ for +message+ is used by the ReAct loop for continuation
19
21
  # turns where the user message has already been added to the chat history
20
- # (e.g. after a tool result) and the LLM should continue without a new
21
- # user turn.
22
+ # (for example after a Tool result).
22
23
  #
23
24
  # @param chat [Object] RubyLLM chat session
24
25
  # @param message [String, nil] user message, or nil to continue the chat
25
- # @param config [Hash] invocation config (not used directly by this impl)
26
+ # @param config [Hash] invocation config (not used directly here)
26
27
  # @return [Object] RubyLLM response
27
- # @api private
28
+ # @api public
28
29
  def complete(chat, message, config: {})
29
30
  message ? chat.ask(message) : chat.complete
30
31
  end
31
32
 
32
- # Delegates to +chat.ask(message) { |chunk| block.call(chunk) }+ or
33
- # +chat.complete(&block)+ when message is nil.
33
+ # Delegates to +chat.ask(message) { |chunk| ... }+ or +chat.complete(&block)+
34
+ # when message is nil.
34
35
  #
35
36
  # @param chat [Object] RubyLLM chat session
36
37
  # @param message [String, nil] user message, or nil to continue the chat
37
38
  # @param config [Hash] invocation config
38
- # @yield [chunk] streaming chunk forwarded from +chat.ask+ / +chat.complete+
39
+ # @yield [chunk] streaming chunk forwarded from RubyLLM
39
40
  # @return [Object] RubyLLM response
40
- # @api private
41
+ # @api public
41
42
  def stream(chat, message, config: {}, &block)
42
43
  message ? chat.ask(message, &block) : chat.complete(&block)
43
44
  end
@@ -1,20 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- # Namespace for LLM adapter implementations.
4
+ # Namespace for LLM call adapters.
5
5
  #
6
- # An LLMAdapter decouples Phronomy's agent pipeline from direct
7
- # dependency on the RubyLLM blocking client. All LLM calls in
8
- # {Agent::Base} are routed through the adapter so that:
6
+ # The public extension boundary is {LLMAdapter::Base#complete} and
7
+ # {LLMAdapter::Base#stream}. Those methods receive Phronomy's currently
8
+ # materialized chat/runtime object and perform the provider call. Phronomy owns
9
+ # the async/offload bridge around that synchronous contract.
9
10
  #
10
- # - Synchronous provider work can be submitted to {OffloadPool} for bounded
11
- # off-EventLoop execution.
12
- # - Alternative LLM clients can be swapped in without changing agent code.
11
+ # This SPI does not by itself make the complete input-materialization pipeline
12
+ # provider-neutral: the current Agent pipeline still materializes the canonical
13
+ # LLM input through RubyLLM-specific runtime objects before invoking the adapter.
13
14
  #
14
15
  # @example Configuring a custom adapter
15
16
  # Phronomy.configure do |c|
16
17
  # c.llm_adapter = MyCustomAdapter.new
17
18
  # end
19
+ #
20
+ # @api public
18
21
  module LLMAdapter
19
22
  end
20
23
  end
@@ -14,7 +14,11 @@ module Phronomy
14
14
  parse(input.is_a?(String) ? input : input.to_s)
15
15
  end
16
16
 
17
- # Implement in subclasses.
17
+ # Extension point implemented by custom parsers.
18
+ #
19
+ # @param text [String]
20
+ # @return [Object]
21
+ # @api public
18
22
  def parse(text)
19
23
  raise NotImplementedError, "#{self.class}#parse is not implemented"
20
24
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Tool
5
+ # Public authoring façade for Phronomy Tools.
6
+ #
7
+ # This constant intentionally refers to the exact same Class object as
8
+ # {Phronomy::Agent::Context::Capability::Base}. The implementation namespace
9
+ # remains canonical internally so the existing Tool DSL state, inheritance,
10
+ # built-in Tools, and compatibility surface are not duplicated.
11
+ #
12
+ # @api public
13
+ Base = Phronomy::Agent::Context::Capability::Base
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Public authoring namespace for application-defined Tools.
5
+ #
6
+ # Concrete Tools supplied by Phronomy remain under {Phronomy::Tools}.
7
+ #
8
+ # @api public
9
+ module Tool
10
+ end
11
+ end
@@ -2,43 +2,29 @@
2
2
 
3
3
  module Phronomy
4
4
  module VectorStore
5
- # Mixin that defines the async interface for VectorStore backends.
5
+ # Framework-owned async convenience methods for VectorStore backends.
6
6
  #
7
- # Mixing this module into a VectorStore class provides three choices:
7
+ # The backend extension contract is the synchronous interface defined by
8
+ # {VectorStore::Base}: add/search/remove/clear/size. These async convenience
9
+ # methods are inherited by backends and route that synchronous work through
10
+ # Phronomy's bounded {OffloadPool}. This keeps OS-thread creation, queue
11
+ # backpressure, submit timeout/cancellation, and completion semantics owned by
12
+ # the framework rather than by each backend.
8
13
  #
9
- # 1. **Do nothing** inherits default implementations from {VectorStore::Base}
10
- # that route through {OffloadPool}.
14
+ # A future genuine native-async backend may adapt its completion into a
15
+ # {Phronomy::Task} without an OffloadPool worker, but native async override is
16
+ # not part of the current backend SPI.
11
17
  #
12
- # 2. **Override selectively** — override only the async methods where the
13
- # backend has a native async driver, while the remaining methods fall back
14
- # to the pool.
15
- #
16
- # 3. **Implement all natively** — override all async methods to avoid pool
17
- # allocation entirely.
18
- #
19
- # @example Native async search (no pool worker thread allocated)
20
- # class MyFastStore < Phronomy::VectorStore::Base
21
- # include Phronomy::VectorStore::AsyncBackend
22
- #
23
- # def search_async(query_embedding:, k: 5, cancellation_token: nil, timeout: nil)
24
- # # Returns a PendingOperation backed by a native async driver.
25
- # native_async_search(query_embedding, k)
26
- # end
27
- # end
28
- #
29
- # @api public
18
+ # @api private
30
19
  module AsyncBackend
31
20
  # Async variant of {VectorStore::Base#add}.
32
21
  #
33
- # Submits the add call to {OffloadPool} by default.
34
- # Override to use a native async driver.
35
- #
36
22
  # @param id [String]
37
23
  # @param embedding [Array<Float>]
38
24
  # @param metadata [Hash]
39
25
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
40
26
  # @param timeout [Numeric, nil]
41
- # @return [OffloadPool::PendingOperation]
27
+ # @return [Phronomy::Task]
42
28
  # @api public
43
29
  def add_async(id:, embedding:, metadata: {}, cancellation_token: nil, timeout: nil)
44
30
  Phronomy::Runtime.instance.offload.submit(
@@ -52,14 +38,11 @@ module Phronomy
52
38
 
53
39
  # Async variant of {VectorStore::Base#search}.
54
40
  #
55
- # Submits the search call to {OffloadPool} by default.
56
- # Override to use a native async driver.
57
- #
58
41
  # @param query_embedding [Array<Float>]
59
42
  # @param k [Integer]
60
43
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
61
44
  # @param timeout [Numeric, nil]
62
- # @return [OffloadPool::PendingOperation]
45
+ # @return [Phronomy::Task]
63
46
  # @api public
64
47
  def search_async(query_embedding:, k: 5, cancellation_token: nil, timeout: nil)
65
48
  Phronomy::Runtime.instance.offload.submit(
@@ -73,13 +56,10 @@ module Phronomy
73
56
 
74
57
  # Async variant of {VectorStore::Base#remove}.
75
58
  #
76
- # Submits the remove call to {OffloadPool} by default.
77
- # Override to use a native async driver.
78
- #
79
59
  # @param id [String]
80
60
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
81
61
  # @param timeout [Numeric, nil]
82
- # @return [OffloadPool::PendingOperation]
62
+ # @return [Phronomy::Task]
83
63
  # @api public
84
64
  def remove_async(id:, cancellation_token: nil, timeout: nil)
85
65
  Phronomy::Runtime.instance.offload.submit(
@@ -93,12 +73,9 @@ module Phronomy
93
73
 
94
74
  # Async variant of {VectorStore::Base#clear}.
95
75
  #
96
- # Submits the clear call to {OffloadPool} by default.
97
- # Override to use a native async driver.
98
- #
99
76
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
100
77
  # @param timeout [Numeric, nil]
101
- # @return [OffloadPool::PendingOperation]
78
+ # @return [Phronomy::Task]
102
79
  # @api public
103
80
  def clear_async(cancellation_token: nil, timeout: nil)
104
81
  Phronomy::Runtime.instance.offload.submit(
@@ -2,24 +2,22 @@
2
2
 
3
3
  module Phronomy
4
4
  module VectorStore
5
- # Abstract interface for vector stores.
5
+ # Public extension SPI for vector stores.
6
6
  #
7
- # Implementations manage a collection of (embedding, metadata) pairs and
8
- # support similarity search.
7
+ # Backends implement the synchronous add/search/remove/clear/size contract.
8
+ # Phronomy supplies async convenience methods through {AsyncBackend}; blocking
9
+ # backend work is offloaded through the framework-owned bounded OffloadPool.
9
10
  #
10
- # Async methods (`search_async`, `add_async`, `remove_async`, `clear_async`)
11
- # are provided by the {AsyncBackend} mixin which defaults to routing calls
12
- # through {OffloadPool}. Backends with native async drivers may override
13
- # individual async methods without touching the pool at all.
11
+ # @api public
14
12
  class Base
15
13
  include AsyncBackend
16
14
 
17
15
  # Add a document with its vector embedding.
18
16
  #
19
- # @param id [String] unique document identifier
20
- # @param embedding [Array<Float>] vector embedding
21
- # @param metadata [Hash] arbitrary metadata (e.g. the original message object)
22
- # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil] optional; raises CancellationError when cancelled
17
+ # @param id [String] unique document identifier
18
+ # @param embedding [Array<Float>] vector embedding
19
+ # @param metadata [Hash] arbitrary metadata
20
+ # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
23
21
  # @api public
24
22
  def add(id:, embedding:, metadata: {}, cancellation_token: nil)
25
23
  cancellation_token&.raise_if_cancelled!
@@ -29,8 +27,8 @@ module Phronomy
29
27
  # Return the k most similar documents to the query embedding.
30
28
  #
31
29
  # @param query_embedding [Array<Float>]
32
- # @param k [Integer] number of results
33
- # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil] optional; raises CancellationError when cancelled
30
+ # @param k [Integer] number of results
31
+ # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
34
32
  # @return [Array<Hash>] each element: { id:, score:, metadata: }
35
33
  # @api public
36
34
  def search(query_embedding:, k: 5, cancellation_token: nil)
@@ -47,6 +45,7 @@ module Phronomy
47
45
  end
48
46
 
49
47
  # Remove all documents.
48
+ # @api public
50
49
  def clear
51
50
  raise NotImplementedError, "#{self.class}#clear is not implemented"
52
51
  end