phronomy 0.1.2 → 0.1.4

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/phronomy/install/templates/create_phronomy_messages.rb.tt +1 -1
  3. data/lib/phronomy/agent/base.rb +68 -35
  4. data/lib/phronomy/agent/handoff.rb +6 -2
  5. data/lib/phronomy/agent/react_agent.rb +57 -31
  6. data/lib/phronomy/agent/runner.rb +6 -4
  7. data/lib/phronomy/configuration.rb +6 -0
  8. data/lib/phronomy/context/assembler.rb +11 -3
  9. data/lib/phronomy/context/compaction_context.rb +1 -3
  10. data/lib/phronomy/context/context_version_cache.rb +22 -8
  11. data/lib/phronomy/context/token_estimator.rb +19 -2
  12. data/lib/phronomy/eval/eval_result.rb +15 -5
  13. data/lib/phronomy/eval/runner.rb +46 -11
  14. data/lib/phronomy/eval/scorer/llm_judge.rb +7 -2
  15. data/lib/phronomy/graph/compiled_graph.rb +9 -1
  16. data/lib/phronomy/graph/parallel_node.rb +53 -18
  17. data/lib/phronomy/graph/state_graph.rb +7 -1
  18. data/lib/phronomy/guardrail/builtin/pii_pattern_detector.rb +47 -3
  19. data/lib/phronomy/guardrail/builtin/prompt_injection_detector.rb +15 -1
  20. data/lib/phronomy/memory/compression/summary.rb +4 -3
  21. data/lib/phronomy/memory/compression/tool_output_pruner.rb +11 -6
  22. data/lib/phronomy/memory/conversation_manager.rb +59 -14
  23. data/lib/phronomy/memory/retrieval/base.rb +4 -3
  24. data/lib/phronomy/memory/retrieval/composite.rb +5 -4
  25. data/lib/phronomy/memory/retrieval/recent.rb +4 -3
  26. data/lib/phronomy/memory/retrieval/semantic.rb +50 -17
  27. data/lib/phronomy/memory/storage/active_record.rb +18 -13
  28. data/lib/phronomy/memory/storage/in_memory.rb +25 -16
  29. data/lib/phronomy/rails/agent_job.rb +20 -3
  30. data/lib/phronomy/runnable.rb +4 -1
  31. data/lib/phronomy/state_store/active_record.rb +7 -3
  32. data/lib/phronomy/state_store/base.rb +16 -2
  33. data/lib/phronomy/state_store/in_memory.rb +5 -4
  34. data/lib/phronomy/tool/base.rb +19 -3
  35. data/lib/phronomy/tool/mcp_tool.rb +67 -9
  36. data/lib/phronomy/tracing/base.rb +0 -2
  37. data/lib/phronomy/tracing/langfuse_tracer.rb +24 -4
  38. data/lib/phronomy/tracing/null_tracer.rb +6 -3
  39. data/lib/phronomy/trust_pipeline.rb +32 -4
  40. data/lib/phronomy/vector_store/in_memory.rb +7 -5
  41. data/lib/phronomy/vector_store/redis_search.rb +30 -23
  42. data/lib/phronomy/version.rb +1 -1
  43. data/lib/phronomy.rb +39 -0
  44. metadata +2 -2
@@ -14,13 +14,14 @@ module Phronomy
14
14
  class InMemory < Base
15
15
  def initialize
16
16
  @documents = {}
17
+ @mutex = Mutex.new
17
18
  end
18
19
 
19
20
  # @param id [String]
20
21
  # @param embedding [Array<Float>]
21
22
  # @param metadata [Hash]
22
23
  def add(id:, embedding:, metadata: {})
23
- @documents[id] = {embedding: embedding, metadata: metadata}
24
+ @mutex.synchronize { @documents[id] = {embedding: embedding, metadata: metadata} }
24
25
  self
25
26
  end
26
27
 
@@ -28,7 +29,8 @@ module Phronomy
28
29
  # @param k [Integer]
29
30
  # @return [Array<Hash>] sorted by descending score
30
31
  def search(query_embedding:, k: 5)
31
- results = @documents.map do |id, doc|
32
+ snapshot = @mutex.synchronize { @documents.dup }
33
+ results = snapshot.map do |id, doc|
32
34
  score = cosine_similarity(query_embedding, doc[:embedding])
33
35
  {id: id, score: score, metadata: doc[:metadata]}
34
36
  end
@@ -36,18 +38,18 @@ module Phronomy
36
38
  end
37
39
 
38
40
  def remove(id:)
39
- @documents.delete(id)
41
+ @mutex.synchronize { @documents.delete(id) }
40
42
  self
41
43
  end
42
44
 
43
45
  def clear
44
- @documents.clear
46
+ @mutex.synchronize { @documents.clear }
45
47
  self
46
48
  end
47
49
 
48
50
  # @return [Integer] number of documents stored
49
51
  def size
50
- @documents.size
52
+ @mutex.synchronize { @documents.size }
51
53
  end
52
54
 
53
55
  private
@@ -38,6 +38,7 @@ module Phronomy
38
38
  @index_name = index_name
39
39
  @dimension = dimension
40
40
  @index_created = false
41
+ @mutex = Mutex.new
41
42
  end
42
43
 
43
44
  # @param id [String]
@@ -79,37 +80,43 @@ module Phronomy
79
80
  end
80
81
 
81
82
  def clear
82
- begin
83
- @redis.call("FT.DROPINDEX", @index_name, "DD")
84
- rescue => e
85
- raise unless e.message.to_s.include?("Unknown Index name")
83
+ @mutex.synchronize do
84
+ begin
85
+ @redis.call("FT.DROPINDEX", @index_name, "DD")
86
+ rescue => e
87
+ raise unless e.message.to_s.include?("Unknown Index name")
88
+ end
89
+ @index_created = false
86
90
  end
87
- @index_created = false
88
91
  self
89
92
  end
90
93
 
91
94
  private
92
95
 
93
96
  def ensure_index!(dim)
94
- return if @index_created
95
-
96
- @dimension ||= dim
97
- begin
98
- @redis.call(
99
- "FT.CREATE", @index_name,
100
- "ON", "HASH",
101
- "PREFIX", 1, DOC_PREFIX,
102
- "SCHEMA",
103
- "embedding", "VECTOR", "FLAT", 6,
104
- "TYPE", "FLOAT32",
105
- "DIM", @dimension,
106
- "DISTANCE_METRIC", "COSINE",
107
- "metadata", "TEXT"
108
- )
109
- rescue => e
110
- raise unless e.message.to_s.include?("Index already exists")
97
+ return if @index_created # fast path outside lock
98
+
99
+ @mutex.synchronize do
100
+ return if @index_created # re-check inside lock
101
+
102
+ @dimension ||= dim
103
+ begin
104
+ @redis.call(
105
+ "FT.CREATE", @index_name,
106
+ "ON", "HASH",
107
+ "PREFIX", 1, DOC_PREFIX,
108
+ "SCHEMA",
109
+ "embedding", "VECTOR", "FLAT", 6,
110
+ "TYPE", "FLOAT32",
111
+ "DIM", @dimension,
112
+ "DISTANCE_METRIC", "COSINE",
113
+ "metadata", "TEXT"
114
+ )
115
+ rescue => e
116
+ raise unless e.message.to_s.include?("Index already exists")
117
+ end
118
+ @index_created = true
111
119
  end
112
- @index_created = true
113
120
  end
114
121
 
115
122
  # Pack a Float array as a FLOAT32 binary string for RediSearch.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.4"
5
5
  end
data/lib/phronomy.rb CHANGED
@@ -35,6 +35,45 @@ module Phronomy
35
35
  end
36
36
  end
37
37
 
38
+ # Namespace for graph-related classes (StateGraph, State, ParallelNode, …).
39
+ # Also serves as the registry for State classes that may be serialized to
40
+ # external stores (Redis, DB). Call +register_state_class+ at application
41
+ # startup so that only known classes can be deserialized.
42
+ module Graph
43
+ @state_class_registry = nil
44
+ @registry_mutex = Mutex.new
45
+
46
+ class << self
47
+ # Register one or more State classes that are allowed to be deserialized
48
+ # by StateStore backends. When at least one class is registered, only
49
+ # registered classes will be accepted by +StateStore::Base#safe_state_class+.
50
+ #
51
+ # Call this once at application startup (e.g. in a Rails initializer).
52
+ #
53
+ # @param classes [Array<Class>] classes including Phronomy::Graph::State
54
+ # @example
55
+ # Phronomy::Graph.register_state_class(MyWorkflowState, OtherState)
56
+ def register_state_class(*classes)
57
+ @registry_mutex.synchronize do
58
+ @state_class_registry ||= {}
59
+ classes.each do |klass|
60
+ raise ArgumentError, "#{klass.inspect} is not a Class" unless klass.is_a?(Class)
61
+ @state_class_registry[klass.name] = klass
62
+ end
63
+ end
64
+ end
65
+
66
+ # Returns the current registry Hash, or nil when no class has been registered.
67
+ # @return [Hash{String => Class}, nil]
68
+ attr_reader :state_class_registry
69
+
70
+ # Clears the registry. Primarily used in tests.
71
+ def reset_state_class_registry!
72
+ @registry_mutex.synchronize { @state_class_registry = nil }
73
+ end
74
+ end
75
+ end
76
+
38
77
  class << self
39
78
  def configuration
40
79
  @configuration ||= Configuration.new
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.1.2
4
+ version: 0.1.4
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-10 00:00:00.000000000 Z
11
+ date: 2026-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby_llm