phronomy 0.7.1 → 0.9.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -45
  3. data/benchmark/baseline.json +1 -1
  4. data/benchmark/bench_agent_invoke.rb +1 -1
  5. data/benchmark/bench_context_assembler.rb +11 -3
  6. data/benchmark/bench_regression.rb +11 -11
  7. data/benchmark/bench_token_estimator.rb +5 -5
  8. data/benchmark/bench_tool_schema.rb +2 -2
  9. data/docs/decisions/011-build-context-as-single-llm-input-authority.md +224 -0
  10. data/lib/phronomy/agent/base.rb +268 -403
  11. data/lib/phronomy/agent/checkpoint.rb +118 -0
  12. data/lib/phronomy/agent/concerns/suspendable.rb +6 -6
  13. data/lib/phronomy/agent/context/capability/base.rb +689 -0
  14. data/lib/phronomy/agent/context/capability/scope_policy.rb +54 -0
  15. data/lib/phronomy/agent/context/instruction/prompt_template.rb +102 -0
  16. data/lib/phronomy/agent/context/knowledge/base.rb +58 -0
  17. data/lib/phronomy/agent/context/knowledge/entity_knowledge.rb +102 -0
  18. data/lib/phronomy/agent/context/knowledge/static_knowledge.rb +58 -0
  19. data/lib/phronomy/agent/fsm.rb +1 -1
  20. data/lib/phronomy/agent/invocation_pipeline.rb +108 -0
  21. data/lib/phronomy/agent/lifecycle/fsm_session.rb +251 -0
  22. data/lib/phronomy/agent/lifecycle/phase_machine_builder.rb +249 -0
  23. data/lib/phronomy/agent/react_agent.rb +43 -37
  24. data/lib/phronomy/agent/runner.rb +2 -2
  25. data/lib/phronomy/agent/shared_state.rb +2 -2
  26. data/lib/phronomy/agent/tool_executor.rb +108 -0
  27. data/lib/phronomy/concurrency/async_queue.rb +157 -0
  28. data/lib/phronomy/concurrency/blocking_adapter_pool.rb +443 -0
  29. data/lib/phronomy/concurrency/cancellation_scope.rb +125 -0
  30. data/lib/phronomy/concurrency/cancellation_token.rb +140 -0
  31. data/lib/phronomy/concurrency/concurrency_gate.rb +157 -0
  32. data/lib/phronomy/concurrency/deadline.rb +65 -0
  33. data/lib/phronomy/{runtime → concurrency}/gate_registry.rb +1 -2
  34. data/lib/phronomy/{runtime → concurrency}/pool_registry.rb +1 -1
  35. data/lib/phronomy/configuration.rb +0 -6
  36. data/lib/phronomy/context.rb +2 -8
  37. data/lib/phronomy/eval/runner.rb +4 -0
  38. data/lib/phronomy/eval/scorer/llm_judge.rb +12 -1
  39. data/lib/phronomy/event_loop.rb +7 -7
  40. data/lib/phronomy/invocation_context.rb +3 -3
  41. data/lib/phronomy/knowledge_source.rb +0 -5
  42. data/lib/phronomy/llm_adapter/ruby_llm.rb +17 -11
  43. data/lib/phronomy/llm_context_window/assembler.rb +191 -0
  44. data/lib/phronomy/{context → llm_context_window}/context_version_cache.rb +1 -1
  45. data/lib/phronomy/{context → llm_context_window}/token_budget.rb +7 -4
  46. data/lib/phronomy/{context → llm_context_window}/token_estimator.rb +3 -3
  47. data/lib/phronomy/{agent → multi_agent}/handoff.rb +6 -6
  48. data/lib/phronomy/{agent → multi_agent}/orchestrator.rb +7 -7
  49. data/lib/phronomy/{agent → multi_agent}/parallel_tool_chat.rb +4 -4
  50. data/lib/phronomy/{agent → multi_agent}/team_coordinator.rb +4 -4
  51. data/lib/phronomy/runtime/runtime_metrics.rb +0 -1
  52. data/lib/phronomy/runtime.rb +20 -6
  53. data/lib/phronomy/task_group.rb +1 -1
  54. data/lib/phronomy/tool.rb +3 -4
  55. data/lib/phronomy/{tool/agent_tool.rb → tools/agent.rb} +6 -6
  56. data/lib/phronomy/{tool/mcp_tool.rb → tools/mcp.rb} +9 -9
  57. data/lib/phronomy/tools/vector_search.rb +70 -0
  58. data/lib/phronomy/tracing/null_tracer.rb +3 -1
  59. data/lib/phronomy/vector_store/async_backend.rb +4 -4
  60. data/lib/phronomy/vector_store/base.rb +2 -2
  61. data/lib/phronomy/vector_store/embeddings/base.rb +41 -0
  62. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +47 -0
  63. data/lib/phronomy/vector_store/in_memory.rb +12 -2
  64. data/lib/phronomy/vector_store/loader/base.rb +27 -0
  65. data/lib/phronomy/vector_store/loader/csv_loader.rb +58 -0
  66. data/lib/phronomy/vector_store/loader/markdown_loader.rb +78 -0
  67. data/lib/phronomy/vector_store/loader/plain_text_loader.rb +24 -0
  68. data/lib/phronomy/vector_store/pgvector.rb +2 -2
  69. data/lib/phronomy/vector_store/redis_search.rb +2 -2
  70. data/lib/phronomy/vector_store/splitter/base.rb +49 -0
  71. data/lib/phronomy/vector_store/splitter/fixed_size_splitter.rb +53 -0
  72. data/lib/phronomy/vector_store/splitter/recursive_splitter.rb +107 -0
  73. data/lib/phronomy/vector_store.rb +14 -2
  74. data/lib/phronomy/version.rb +1 -1
  75. data/lib/phronomy/workflow_context.rb +8 -0
  76. data/lib/phronomy/workflow_runner.rb +11 -131
  77. data/lib/phronomy.rb +2 -0
  78. data/scripts/api_snapshot.rb +11 -9
  79. metadata +44 -46
  80. data/lib/phronomy/async_queue.rb +0 -155
  81. data/lib/phronomy/blocking_adapter_pool.rb +0 -435
  82. data/lib/phronomy/cancellation_scope.rb +0 -123
  83. data/lib/phronomy/cancellation_token.rb +0 -133
  84. data/lib/phronomy/concurrency_gate.rb +0 -155
  85. data/lib/phronomy/context/assembler.rb +0 -143
  86. data/lib/phronomy/context/compaction_context.rb +0 -111
  87. data/lib/phronomy/context/trigger_context.rb +0 -39
  88. data/lib/phronomy/context/trim_context.rb +0 -75
  89. data/lib/phronomy/deadline.rb +0 -63
  90. data/lib/phronomy/embeddings/base.rb +0 -39
  91. data/lib/phronomy/embeddings/ruby_llm_embeddings.rb +0 -45
  92. data/lib/phronomy/embeddings.rb +0 -11
  93. data/lib/phronomy/fsm_session.rb +0 -247
  94. data/lib/phronomy/knowledge_source/base.rb +0 -54
  95. data/lib/phronomy/knowledge_source/entity_knowledge.rb +0 -96
  96. data/lib/phronomy/knowledge_source/rag_knowledge.rb +0 -57
  97. data/lib/phronomy/knowledge_source/static_knowledge.rb +0 -52
  98. data/lib/phronomy/loader/base.rb +0 -25
  99. data/lib/phronomy/loader/csv_loader.rb +0 -56
  100. data/lib/phronomy/loader/markdown_loader.rb +0 -76
  101. data/lib/phronomy/loader/plain_text_loader.rb +0 -22
  102. data/lib/phronomy/loader.rb +0 -13
  103. data/lib/phronomy/prompt_template.rb +0 -96
  104. data/lib/phronomy/splitter/base.rb +0 -47
  105. data/lib/phronomy/splitter/fixed_size_splitter.rb +0 -51
  106. data/lib/phronomy/splitter/recursive_splitter.rb +0 -105
  107. data/lib/phronomy/splitter.rb +0 -12
  108. data/lib/phronomy/tool/base.rb +0 -644
  109. data/lib/phronomy/tool/scope_policy.rb +0 -50
  110. data/lib/phronomy/tool_executor.rb +0 -106
@@ -1,106 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- # Centralises tool execution routing based on {Tool::Base.execution_mode}.
5
- #
6
- # This is the single place in the framework that decides *how* a tool call is
7
- # dispatched:
8
- #
9
- # - +:cooperative+ — dispatched via +Runtime#spawn+ through the configured
10
- # scheduler. Under the +:fiber+ backend this avoids an
11
- # extra OS thread; under the +:thread+ backend it is
12
- # backed by +ThreadScheduler+ (one thread per task).
13
- # - +:blocking_io+ — submitted to +BlockingAdapterPool+ when the runtime
14
- # provides a pool; falls back to +Runtime#spawn+ otherwise.
15
- # - +:cpu_bound+ — emits a deprecation-style warning then falls back to
16
- # +:blocking_io+ routing (no process pool available yet).
17
- # - +:external_process+ — falls back to +:blocking_io+ routing (no process
18
- # manager available yet).
19
- #
20
- # All paths return an object that responds to +#await+ (+Phronomy::Task+ or
21
- # +BlockingAdapterPool::PendingOperation+), so callers can collect results
22
- # uniformly.
23
- #
24
- # @note Non-goals
25
- # ToolExecutor deliberately does NOT provide:
26
- # - A CPU-bound process pool. CPU-intensive tool work must be handled at the
27
- # application layer (e.g., fork, Sidekiq, separate OS processes). The
28
- # framework will not add a +ProcessPoolExecutor+ equivalent.
29
- # - An external process manager. Spawning or supervising subprocesses is
30
- # out of scope for this module.
31
- # - Additional core execution routes beyond scheduler-backed cooperative
32
- # execution and BlockingAdapterPool-backed blocking I/O isolation.
33
- # The +:cpu_bound+ and +:external_process+ modes are accepted for
34
- # compatibility but both fall back to +:blocking_io+ routing with a
35
- # one-time warning. If a genuinely new core execution route is needed,
36
- # a new ADR is required.
37
- # These non-goals follow from the cooperative-first, non-preemptive
38
- # concurrency model (ADR-010): framework components must not assume the
39
- # caller's concurrency model, and CPU/process management belongs to the
40
- # application layer.
41
- #
42
- # @api private
43
- module ToolExecutor
44
- # Tracks tool classes that have already emitted an execution_mode warning so
45
- # that the same warning is only logged once per process lifetime.
46
- WARNED_MODES = Set.new
47
- WARNED_MODES_MUTEX = Mutex.new
48
- private_constant :WARNED_MODES, :WARNED_MODES_MUTEX
49
-
50
- # Dispatches a single tool call asynchronously according to its
51
- # +execution_mode+ and returns an awaitable.
52
- #
53
- # @param tool [Phronomy::Tool::Base] the tool instance to invoke
54
- # @param args [Hash] argument hash to pass to {Tool::Base#call}
55
- # @param cancellation_token [Phronomy::CancellationToken, nil]
56
- # @param runtime [Phronomy::Runtime] runtime to use for spawning
57
- # (defaults to {Runtime.instance}; injectable for tests)
58
- # @return [#await] a {Phronomy::Task} or {BlockingAdapterPool::PendingOperation}
59
- # @api private
60
- def self.call_async(tool:, args:, cancellation_token: nil, runtime: Phronomy::Runtime.instance)
61
- ct = cancellation_token
62
- mode = tool.class.execution_mode
63
-
64
- # Warn and normalise unsupported modes to :blocking_io.
65
- # Each (tool class, mode) pair emits the warning at most once per process
66
- # lifetime to avoid log flooding in high-throughput scenarios.
67
- if mode == :cpu_bound || mode == :external_process
68
- warn_key = [tool.class.name, mode]
69
- newly_warned = WARNED_MODES_MUTEX.synchronize { WARNED_MODES.add?(warn_key) }
70
- if newly_warned
71
- msg = if mode == :cpu_bound
72
- "[Phronomy] Tool #{tool.class.name} declares execution_mode :cpu_bound, " \
73
- "which has no dedicated executor. " \
74
- "Falling back to blocking_io (BlockingAdapterPool). " \
75
- "Use :blocking_io explicitly to suppress this warning."
76
- else
77
- "[Phronomy] Tool #{tool.class.name} declares execution_mode :external_process, " \
78
- "which has no dedicated process manager. " \
79
- "Falling back to blocking_io (BlockingAdapterPool)."
80
- end
81
- if Phronomy.configuration.logger
82
- Phronomy.configuration.logger.warn(msg)
83
- else
84
- warn msg
85
- end
86
- end
87
- mode = :blocking_io
88
- end
89
-
90
- pool = begin
91
- runtime&.blocking_io
92
- rescue
93
- nil
94
- end
95
-
96
- if mode == :cooperative || pool.nil?
97
- runtime.spawn(name: "tool-#{tool.class.name.to_s.split("::").last}") do
98
- tool.call(args, cancellation_token: ct)
99
- end
100
- else
101
- # Submit directly to pool — no wrapping Task thread required.
102
- pool.submit(cancellation_token: ct) { tool.call(args, cancellation_token: ct) }
103
- end
104
- end
105
- end
106
- end