llm.rb 12.6.0 → 13.0.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +387 -0
  3. data/LICENSE +21 -93
  4. data/README.md +46 -155
  5. data/data/deepinfra.json +3 -0
  6. data/data/xai.json +1 -1
  7. data/lib/llm/a2a.rb +1 -1
  8. data/lib/llm/active_record/acts_as_llm.rb +6 -6
  9. data/lib/llm/agent.rb +62 -23
  10. data/lib/llm/buffer.rb +85 -3
  11. data/lib/llm/compactor/null.rb +19 -0
  12. data/lib/llm/compactor/truncate.rb +80 -0
  13. data/lib/llm/compactor.rb +42 -124
  14. data/lib/llm/context.rb +31 -37
  15. data/lib/llm/contract.rb +4 -25
  16. data/lib/llm/function/array.rb +15 -14
  17. data/lib/llm/function/async/group.rb +54 -0
  18. data/lib/llm/function/async/reactor.rb +48 -0
  19. data/lib/llm/function/async/task.rb +83 -0
  20. data/lib/llm/function/fiber/group.rb +46 -0
  21. data/lib/llm/function/fiber/task.rb +62 -0
  22. data/lib/llm/function/{fork_group.rb → fork/group.rb} +14 -5
  23. data/lib/llm/function/fork/job.rb +2 -2
  24. data/lib/llm/function/fork/task.rb +19 -10
  25. data/lib/llm/function/group.rb +40 -0
  26. data/lib/llm/function/{ractor_group.rb → ractor/group.rb} +13 -5
  27. data/lib/llm/function/ractor/job.rb +9 -3
  28. data/lib/llm/function/ractor/mailbox.rb +2 -0
  29. data/lib/llm/function/ractor/task.rb +23 -15
  30. data/lib/llm/function/{call_group.rb → sequential/group.rb} +12 -8
  31. data/lib/llm/function/sequential/task.rb +49 -0
  32. data/lib/llm/function/task.rb +25 -48
  33. data/lib/llm/function/thread/group.rb +46 -0
  34. data/lib/llm/function/thread/task.rb +60 -0
  35. data/lib/llm/function.rb +54 -64
  36. data/lib/llm/loop_guard.rb +1 -2
  37. data/lib/llm/mcp.rb +22 -0
  38. data/lib/llm/object.rb +2 -1
  39. data/lib/llm/provider.rb +6 -3
  40. data/lib/llm/providers/google.rb +2 -2
  41. data/lib/llm/repl/command.rb +35 -8
  42. data/lib/llm/repl/commands/compact.rb +33 -0
  43. data/lib/llm/repl/input.rb +80 -15
  44. data/lib/llm/repl/markdown/table.rb +76 -0
  45. data/lib/llm/repl/markdown.rb +31 -1
  46. data/lib/llm/repl/status.rb +1 -1
  47. data/lib/llm/repl/stream.rb +10 -3
  48. data/lib/llm/repl/transcript.rb +1 -1
  49. data/lib/llm/repl/walker.rb +46 -0
  50. data/lib/llm/repl.rb +18 -12
  51. data/lib/llm/response.rb +10 -0
  52. data/lib/llm/schema/leaf.rb +5 -0
  53. data/lib/llm/schema/object.rb +11 -5
  54. data/lib/llm/sequel/plugin.rb +6 -6
  55. data/lib/llm/stream.rb +24 -17
  56. data/lib/llm/tool.rb +20 -4
  57. data/lib/llm/tools/chdir.rb +0 -2
  58. data/lib/llm/tools/git.rb +8 -4
  59. data/lib/llm/tools/mkdir.rb +1 -1
  60. data/lib/llm/tools/pwd.rb +0 -2
  61. data/lib/llm/tools/read_file.rb +0 -2
  62. data/lib/llm/tools/rg.rb +8 -4
  63. data/lib/llm/tools/shell.rb +8 -4
  64. data/lib/llm/tools/utils.rb +31 -0
  65. data/lib/llm/version.rb +1 -1
  66. data/lib/llm.rb +25 -5
  67. data/llm.gemspec +3 -3
  68. data/resources/deepdive.md +645 -58
  69. metadata +24 -13
  70. data/lib/llm/function/call_task.rb +0 -46
  71. data/lib/llm/function/fiber_group.rb +0 -105
  72. data/lib/llm/function/task_group.rb +0 -97
  73. data/lib/llm/function/thread_group.rb +0 -102
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLM::Function::Async
4
+ ##
5
+ # {LLM::Function::Async::Task} wraps a function call in an
6
+ # {Async::Task} running on a shared
7
+ # {LLM::Function::Async::Reactor}. The task is spawned lazily
8
+ # in {#wait} or explicitly in {#spawn}.
9
+ #
10
+ # Work is submitted to the reactor through its inbox queue.
11
+ # Results are bridged back through the task's own queue.
12
+ class Task < LLM::Function::Task
13
+ ##
14
+ # @param [LLM::Function] fn
15
+ # @param [Hash] options
16
+ # @option options [LLM::Function::Async::Reactor] :reactor
17
+ def initialize(fn, options = {})
18
+ super
19
+ @reactor = options[:reactor]
20
+ end
21
+
22
+ ##
23
+ # Assign the reactor. Used when a task is created before its
24
+ # reactor is available.
25
+ # @param [LLM::Function::Async::Reactor] reactor
26
+ def reactor=(reactor)
27
+ @reactor = reactor
28
+ end
29
+
30
+ ##
31
+ # Submit the function call to the reactor. The result is
32
+ # pushed to a queue that {#wait} consumes.
33
+ # @return [nil]
34
+ def spawn
35
+ @queue = Queue.new
36
+ @alive = true
37
+ @reactor.submit do
38
+ @queue << function.call
39
+ rescue LLM::Interrupt => e
40
+ @queue << e
41
+ raise
42
+ end
43
+ nil
44
+ end
45
+
46
+ ##
47
+ # @return [Boolean]
48
+ def alive?
49
+ @alive || false
50
+ end
51
+
52
+ ##
53
+ # Push an interrupt sentinel to the result queue. The reactor
54
+ # thread continues running but the result is discarded.
55
+ # @return [nil]
56
+ def interrupt!
57
+ if @queue
58
+ @alive = false
59
+ @queue << LLM::Interrupt.new
60
+ end
61
+ nil
62
+ end
63
+ alias_method :cancel!, :interrupt!
64
+
65
+ ##
66
+ # Wait for the result queue to contain a value.
67
+ # @return [LLM::Function::Return]
68
+ def wait
69
+ spawn unless @queue
70
+ result = @queue.pop
71
+ @alive = false
72
+ raise result if LLM::Interrupt === result
73
+ result
74
+ end
75
+ alias_method :value, :wait
76
+
77
+ ##
78
+ # @return [Class]
79
+ def group_class
80
+ LLM::Function::Async::Group
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLM::Function::Fiber
4
+ ##
5
+ # Wraps an array of {Fiber::Task} objects for cooperative
6
+ # concurrent execution. Interrupts all tasks and waits
7
+ # for them to complete.
8
+ class Group < LLM::Function::Group
9
+ ##
10
+ # @param [Array<LLM::Function::Fiber::Task>] tasks
11
+ def initialize(tasks)
12
+ @tasks = tasks
13
+ end
14
+
15
+ ##
16
+ # @return [nil]
17
+ def spawn
18
+ @tasks.each(&:spawn)
19
+ nil
20
+ ensure
21
+ @spawned = true
22
+ end
23
+
24
+ ##
25
+ # @return [Boolean]
26
+ def alive?
27
+ @tasks.any?(&:alive?)
28
+ end
29
+
30
+ ##
31
+ # @return [nil]
32
+ def interrupt!
33
+ @tasks.each(&:interrupt!)
34
+ nil
35
+ end
36
+ alias_method :cancel!, :interrupt!
37
+
38
+ ##
39
+ # @return [Array<LLM::Function::Return>]
40
+ def wait
41
+ spawn unless @spawned
42
+ @tasks.map(&:wait)
43
+ end
44
+ alias_method :value, :wait
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLM::Function::Fiber
4
+ ##
5
+ # {LLM::Function::Fiber::Task LLM::Function::Fiber::Task}
6
+ # wraps a function call in a scheduler-backed fiber for
7
+ # cooperative concurrent execution. The fiber is created
8
+ # lazily when {#wait} is called, not at construction time.
9
+ #
10
+ # Requires `Fiber.scheduler` — without one, raise early in
11
+ # {#wait}. Interrupting a running task raises
12
+ # {LLM::Interrupt} on the fiber, which stops it at the next
13
+ # yield point.
14
+ class Task < LLM::Function::Task
15
+ ##
16
+ # @param [LLM::Function] fn
17
+ # @param [Hash] options
18
+ def initialize(fn, options = {})
19
+ super
20
+ end
21
+
22
+ ##
23
+ # @return [nil]
24
+ def spawn
25
+ if Fiber.scheduler.nil?
26
+ raise ArgumentError, "Fiber concurrency requires Fiber.scheduler"
27
+ else
28
+ @fiber = Fiber.schedule { function.call }
29
+ nil
30
+ end
31
+ end
32
+
33
+ ##
34
+ # @return [Boolean]
35
+ def alive?
36
+ @fiber&.alive? || false
37
+ end
38
+
39
+ ##
40
+ # @return [nil]
41
+ def interrupt!
42
+ @fiber&.raise(LLM::Interrupt) if @fiber&.alive?
43
+ function.interrupt!
44
+ nil
45
+ end
46
+ alias_method :cancel!, :interrupt!
47
+
48
+ ##
49
+ # @return [LLM::Function::Return]
50
+ def wait
51
+ spawn unless @fiber
52
+ @result ||= @fiber.value
53
+ end
54
+ alias_method :value, :wait
55
+
56
+ ##
57
+ # @return [Class]
58
+ def group_class
59
+ LLM::Function::Fiber::Group
60
+ end
61
+ end
62
+ end
@@ -1,17 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class LLM::Function
3
+ module LLM::Function::Fork
4
4
  ##
5
- # The {LLM::Function::Fork::Group} class wraps an array of
6
- # {LLM::Function::Task} objects that are running in forked child processes.
7
- class Fork::Group
5
+ # Wraps an array of {Fork::Task} objects that are running
6
+ # in forked child processes.
7
+ class Group < LLM::Function::Group
8
8
  ##
9
9
  # @param [Array<LLM::Function::Task>] tasks
10
- # @return [LLM::Function::Fork::Group]
11
10
  def initialize(tasks)
12
11
  @tasks = tasks
13
12
  end
14
13
 
14
+ ##
15
+ # @return [nil]
16
+ def spawn
17
+ @tasks.each(&:spawn)
18
+ nil
19
+ ensure
20
+ @spawned = true
21
+ end
22
+
15
23
  ##
16
24
  # @return [Boolean]
17
25
  def alive?
@@ -29,6 +37,7 @@ class LLM::Function
29
37
  ##
30
38
  # @return [Array<LLM::Function::Return>]
31
39
  def wait
40
+ spawn unless @spawned
32
41
  @tasks.map(&:wait)
33
42
  end
34
43
  alias_method :value, :wait
@@ -54,11 +54,11 @@ class LLM::Function
54
54
 
55
55
  def setup(runner)
56
56
  ready = Queue.new
57
- thread = Thread.new do
57
+ thread = ::Thread.new do
58
58
  ready << true
59
59
  kind = @ch.control.recv
60
60
  next unless kind == :interrupt
61
- Thread.main.raise(LLM::Interrupt)
61
+ ::Thread.main.raise(LLM::Interrupt)
62
62
  rescue IOError, ArgumentError
63
63
  end
64
64
  ready.pop
@@ -4,31 +4,39 @@ class LLM::Function
4
4
  ##
5
5
  # The {LLM::Function::Fork::Task} class wraps a fork-backed function call
6
6
  # and exchanges control and result messages with the child process.
7
- class Fork::Task
7
+ class Fork::Task < LLM::Function::Task
8
8
  ##
9
- # @param [LLM::Function] function
10
- # @param [LLM::Tracer, nil] tracer
11
- # @param [Object, nil] span
9
+ # @param [LLM::Function] fn
10
+ # @param [Hash] options
11
+ # @option options [LLM::Tracer, nil] :tracer
12
12
  # @return [LLM::Function::Fork::Task]
13
- def initialize(function, tracer: nil, span: nil)
14
- @function = function
15
- @tracer = tracer
16
- @span = span
13
+ def initialize(fn, options = {})
14
+ super
15
+ @tracer = options.fetch(:tracer, nil)
16
+ @spawned = false
17
17
  @waited = false
18
18
  end
19
19
 
20
20
  ##
21
21
  # @return [LLM::Function::Fork::Task]
22
22
  def spawn
23
- @ch = LLM::Object.from(control: xchan(:marshal), result: xchan(:marshal))
23
+ @span = @tracer&.on_tool_start(
24
+ id: @function.id, name: @function.name,
25
+ arguments: @function.arguments, model: @function.model
26
+ )
27
+ @ch = LLM::Object.from(
28
+ control: xchan(:marshal),
29
+ result: xchan(:marshal, sock: Socket::SOCK_STREAM)
30
+ )
24
31
  @pid = Kernel.fork { Fork::Job.new(@function, @ch).call }
32
+ @spawned = true
25
33
  self
26
34
  end
27
35
 
28
36
  ##
29
37
  # @return [Boolean]
30
38
  def alive?
31
- return false if @waited
39
+ return false if @waited || !@pid
32
40
  result = ::Process.waitpid(@pid, ::Process::WNOHANG)
33
41
  @waited = !result.nil?
34
42
  !@waited
@@ -51,6 +59,7 @@ class LLM::Function
51
59
  ##
52
60
  # @return [LLM::Function::Return]
53
61
  def wait
62
+ spawn unless @spawned
54
63
  kind, data = @ch.result.recv
55
64
  raise LLM::Interrupt if kind == :interrupt
56
65
  raise ArgumentError, "Unknown fork message: #{kind.inspect}" unless kind == :result
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::Function
4
+ ##
5
+ # Abstract base class for all concurrency strategy groups.
6
+ # Each strategy (sequential, thread, fiber, async, fork, ractor)
7
+ # subclasses this and implements {#alive?}, {#interrupt!}, and
8
+ # {#wait}.
9
+ class Group
10
+ ##
11
+ # @abstract
12
+ # @return [nil]
13
+ def spawn
14
+ raise NotImplementedError
15
+ end
16
+
17
+ ##
18
+ # @abstract
19
+ # @return [Boolean]
20
+ def alive?
21
+ raise NotImplementedError
22
+ end
23
+
24
+ ##
25
+ # @abstract
26
+ # @return [nil]
27
+ def interrupt!
28
+ raise NotImplementedError
29
+ end
30
+ alias_method :cancel!, :interrupt!
31
+
32
+ ##
33
+ # @abstract
34
+ # @return [Array<LLM::Function::Return>]
35
+ def wait
36
+ raise NotImplementedError
37
+ end
38
+ alias_method :value, :wait
39
+ end
40
+ end
@@ -1,18 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class LLM::Function
3
+ module LLM::Function::Ractor
4
4
  ##
5
- # The {LLM::Function::Ractor::Group} class wraps an array of
6
- # {LLM::Function::Ractor::Task} objects that are running
5
+ # Wraps an array of {Ractor::Task} objects that are running
7
6
  # {LLM::Function} calls concurrently.
8
- class Ractor::Group
7
+ class Group < LLM::Function::Group
9
8
  ##
10
9
  # @param [Array<LLM::Function::Task>] tasks
11
- # @return [LLM::Function::Ractor::Group]
12
10
  def initialize(tasks)
13
11
  @tasks = tasks
14
12
  end
15
13
 
14
+ ##
15
+ # @return [nil]
16
+ def spawn
17
+ @tasks.each(&:spawn)
18
+ nil
19
+ ensure
20
+ @spawned = true
21
+ end
22
+
16
23
  ##
17
24
  # @return [Boolean]
18
25
  def alive?
@@ -30,6 +37,7 @@ class LLM::Function
30
37
  ##
31
38
  # @return [Array<LLM::Function::Return>]
32
39
  def wait
40
+ spawn unless @spawned
33
41
  @tasks.map(&:wait)
34
42
  end
35
43
  alias_method :value, :wait
@@ -39,11 +39,17 @@ class LLM::Function
39
39
  result ||= data
40
40
  done = true
41
41
  waiters.each { _1.send(result) }
42
+ break unless waiters.empty?
42
43
  waiters.clear
43
44
  in [:alive?, reply]
44
45
  reply.send(!done)
45
46
  in [:wait, reply]
46
- done ? reply.send(result) : (waiters << reply)
47
+ if done
48
+ reply.send(result)
49
+ break
50
+ else
51
+ waiters << reply
52
+ end
47
53
  in [:interrupt]
48
54
  @tool&.send(:interrupt)
49
55
  end
@@ -52,9 +58,9 @@ class LLM::Function
52
58
 
53
59
  def spawn
54
60
  @tool = ::Ractor.new(@mailbox, @runner_class, @id, @name, @arguments) do |mailbox, runner_class, id, name, arguments|
55
- Thread.new do
61
+ ::Thread.new do
56
62
  ::Ractor.receive == :interrupt or next
57
- Thread.main.raise(LLM::Interrupt)
63
+ ::Thread.main.raise(LLM::Interrupt)
58
64
  rescue ::Ractor::Error
59
65
  end
60
66
  kwargs = Hash === arguments ? arguments.transform_keys(&:to_sym) : arguments
@@ -20,6 +20,8 @@ class LLM::Function
20
20
  # @return [Boolean]
21
21
  def alive?
22
22
  request(:alive?)
23
+ rescue ::Ractor::ClosedError
24
+ false
23
25
  end
24
26
 
25
27
  ##
@@ -5,31 +5,38 @@ class LLM::Function
5
5
  # The {LLM::Function::Ractor::Task} class wraps a ractor-backed function
6
6
  # call and delegates mailbox coordination to
7
7
  # {LLM::Function::Ractor::Mailbox}.
8
- class Ractor::Task
8
+ class Ractor::Task < LLM::Function::Task
9
9
  ##
10
10
  # @return [LLM::Function::Ractor::Mailbox]
11
11
  attr_reader :mailbox
12
12
 
13
13
  ##
14
- # @param [Class] runner_class
15
- # @param [String, nil] id
16
- # @param [String] name
17
- # @param [Hash, Array, nil] arguments
18
- # @param [LLM::Tracer, nil] tracer
19
- # @param [Object, nil] span
14
+ # @param [LLM::Function] fn
15
+ # @param [Hash] options
16
+ # @option options [LLM::Tracer, nil] :tracer
17
+ # @option options [Class] :runner_class
18
+ # @option options [String, nil] :id
19
+ # @option options [String] :name
20
+ # @option options [Hash, Array, nil] :arguments
21
+ # @option options [String, nil] :model
20
22
  # @return [LLM::Function::Ractor::Task]
21
- def initialize(runner_class, id, name, arguments, tracer: nil, span: nil)
22
- @runner_class = runner_class
23
- @id = id
24
- @name = name
25
- @arguments = arguments
26
- @tracer = tracer
27
- @span = span
23
+ def initialize(fn, options = {})
24
+ super
25
+ @runner_class = options.fetch(:runner_class)
26
+ @id = options.fetch(:id)
27
+ @name = options.fetch(:name)
28
+ @arguments = options.fetch(:arguments)
29
+ @model = options.fetch(:model, nil)
30
+ @tracer = options.fetch(:tracer, nil)
28
31
  end
29
32
 
30
33
  ##
31
34
  # @return [LLM::Function::Ractor::Task]
32
35
  def spawn
36
+ @span = @tracer&.on_tool_start(
37
+ id: @id, name: @name,
38
+ arguments: @arguments, model: @model
39
+ )
33
40
  @mailbox = Ractor::Mailbox.new(build_task)
34
41
  self
35
42
  end
@@ -37,7 +44,7 @@ class LLM::Function
37
44
  ##
38
45
  # @return [Boolean]
39
46
  def alive?
40
- mailbox.alive?
47
+ @mailbox&.alive? || false
41
48
  end
42
49
 
43
50
  ##
@@ -51,6 +58,7 @@ class LLM::Function
51
58
  ##
52
59
  # @return [LLM::Function::Return]
53
60
  def wait
61
+ spawn unless @mailbox
54
62
  id, name, value = mailbox.wait
55
63
  result = Return.new(id, name, value)
56
64
  @tracer&.on_tool_finish(result:, span: @span)
@@ -1,22 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class LLM::Function
3
+ module LLM::Function::Sequential
4
4
  ##
5
- # The {LLM::Function::CallGroup} class wraps an array of
6
- # {LLM::Function} objects for sequential execution.
7
- #
8
- # It provides the same basic interface as the concurrent group
5
+ # Wraps an array of {LLM::Function} objects for sequential
6
+ # execution. Provides the same interface as concurrent group
9
7
  # wrappers so callers can flow through `spawn(strategy).wait`
10
- # uniformly, even when the selected strategy is direct calls.
11
- class CallGroup
8
+ # uniformly.
9
+ class Group < LLM::Function::Group
12
10
  ##
13
11
  # @param [Array<LLM::Function>] functions
14
- # @return [LLM::Function::CallGroup]
15
12
  def initialize(functions)
16
13
  @functions = functions
17
14
  @owner = nil
18
15
  end
19
16
 
17
+ ##
18
+ # @return [nil]
19
+ def spawn
20
+ # no-op — execution happens in wait
21
+ nil
22
+ end
23
+
20
24
  ##
21
25
  # @return [Boolean]
22
26
  def alive?
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLM::Function::Sequential
4
+ ##
5
+ # The {LLM::Function::Sequential::Task} class wraps a single
6
+ # direct function call behind the same task-like interface
7
+ # used by spawned concurrency modes.
8
+ class Task < LLM::Function::Task
9
+ ##
10
+ # @param [LLM::Function] fn
11
+ # @param [Hash] options
12
+ def initialize(fn, options = {})
13
+ @function = fn
14
+ end
15
+
16
+ ##
17
+ # @return [nil]
18
+ def spawn
19
+ # no-op — execution happens in wait
20
+ end
21
+
22
+ ##
23
+ # @return [Boolean]
24
+ def alive?
25
+ false
26
+ end
27
+
28
+ ##
29
+ # @return [nil]
30
+ def interrupt!
31
+ function.interrupt!
32
+ nil
33
+ end
34
+ alias_method :cancel!, :interrupt!
35
+
36
+ ##
37
+ # @return [LLM::Function::Return]
38
+ def wait
39
+ @result ||= function.call
40
+ end
41
+ alias_method :value, :wait
42
+
43
+ ##
44
+ # @return [Class]
45
+ def group_class
46
+ LLM::Function::Sequential::Group
47
+ end
48
+ end
49
+ end
@@ -2,82 +2,59 @@
2
2
 
3
3
  class LLM::Function
4
4
  ##
5
- # The {LLM::Function::Task} class wraps a single concurrent function call and
6
- # provides a small, uniform interface across threads, scheduler-backed fibers,
7
- # and async tasks.
5
+ # This class is the superclass that all concurrency
6
+ # strategies must subclass in order to implement their
7
+ # own Task class. It provides a common interface that
8
+ # is the same across all concurrency strategies.
8
9
  class Task
9
10
  ##
10
- # @return [Object]
11
- attr_reader :task
11
+ # @return [LLM::Function]
12
+ attr_reader :function
12
13
 
13
14
  ##
14
- # @return [LLM::Function, nil]
15
- attr_reader :function
15
+ # @param [LLM::Function] fn
16
+ # @param [Hash] options
17
+ # An optional set of options that are specific
18
+ # to a given concurrency strategy.
19
+ def initialize(fn, options = {})
20
+ @function = fn
21
+ end
16
22
 
17
23
  ##
18
- # @param [Thread, Fiber, Async::Task, Ractor, LLM::Function::Ractor::Task] task
19
- # @param [LLM::Function, nil] function
20
- # @return [LLM::Function::Task]
21
- def initialize(task, function = nil)
22
- @task = task
23
- @function = function
24
+ # @abstract
25
+ # @return [nil]
26
+ def spawn
27
+ raise NotImplementedError
24
28
  end
25
29
 
26
30
  ##
31
+ # @abstract
27
32
  # @return [Boolean]
28
33
  def alive?
29
- return task.alive? if task.respond_to?(:alive?)
30
- false
34
+ raise NotImplementedError
31
35
  end
32
36
 
33
37
  ##
38
+ # @abstract
34
39
  # @return [nil]
35
40
  def interrupt!
36
- case task
37
- when Thread
38
- task.raise(LLM::Interrupt)
39
- when Fiber
40
- task.raise(LLM::Interrupt) if task.alive?
41
- else
42
- if defined?(::Async::Task) and ::Async::Task === task
43
- task.fiber&.raise(LLM::Interrupt) if task.alive?
44
- elsif task.respond_to?(:interrupt!)
45
- task.interrupt!
46
- end
47
- end
48
- function&.interrupt!
49
- nil
41
+ raise NotImplementedError
50
42
  end
51
43
  alias_method :cancel!, :interrupt!
52
44
 
53
45
  ##
46
+ # @abstract
54
47
  # @return [LLM::Function::Return]
55
48
  def wait
56
- if Thread === task
57
- task.value
58
- elsif Fiber === task
59
- fiber.alive? ? scheduler.run : nil
60
- task.value
61
- else
62
- task.wait
63
- end
49
+ raise NotImplementedError
64
50
  end
65
51
  alias_method :value, :wait
66
52
 
67
53
  ##
54
+ # @abstract
68
55
  # @return [Class]
69
56
  def group_class
70
- case task
71
- when Thread then LLM::Function::ThreadGroup
72
- when Fiber then LLM::Function::FiberGroup
73
- else LLM::Function::TaskGroup
74
- end
75
- end
76
-
77
- private
78
-
79
- def scheduler
80
- Fiber.scheduler
57
+ raise NotImplementedError
81
58
  end
82
59
  end
83
60
  end