dspy 0.34.2 → 0.34.3

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dspy/chain_of_thought.rb +3 -2
  3. data/lib/dspy/context.rb +17 -1
  4. data/lib/dspy/evals/version.rb +1 -1
  5. data/lib/dspy/evals.rb +42 -31
  6. data/lib/dspy/events.rb +2 -3
  7. data/lib/dspy/example.rb +1 -1
  8. data/lib/dspy/lm/adapter.rb +39 -0
  9. data/lib/dspy/lm/json_strategy.rb +37 -2
  10. data/lib/dspy/lm/message.rb +1 -1
  11. data/lib/dspy/lm/response.rb +1 -1
  12. data/lib/dspy/lm/usage.rb +4 -4
  13. data/lib/dspy/lm.rb +9 -49
  14. data/lib/dspy/mixins/type_coercion.rb +189 -30
  15. data/lib/dspy/module.rb +70 -25
  16. data/lib/dspy/predict.rb +32 -5
  17. data/lib/dspy/prediction.rb +15 -57
  18. data/lib/dspy/prompt.rb +50 -30
  19. data/lib/dspy/propose/dataset_summary_generator.rb +1 -1
  20. data/lib/dspy/propose/grounded_proposer.rb +3 -3
  21. data/lib/dspy/re_act.rb +0 -162
  22. data/lib/dspy/registry/signature_registry.rb +3 -3
  23. data/lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb +1 -27
  24. data/lib/dspy/schema/sorbet_json_schema.rb +7 -6
  25. data/lib/dspy/schema/version.rb +1 -1
  26. data/lib/dspy/schema_adapters.rb +1 -1
  27. data/lib/dspy/storage/program_storage.rb +2 -2
  28. data/lib/dspy/structured_outputs_prompt.rb +3 -3
  29. data/lib/dspy/teleprompt/utils.rb +2 -2
  30. data/lib/dspy/tools/github_cli_toolset.rb +7 -7
  31. data/lib/dspy/tools/text_processing_toolset.rb +2 -2
  32. data/lib/dspy/tools/toolset.rb +1 -1
  33. data/lib/dspy/version.rb +1 -1
  34. data/lib/dspy.rb +1 -4
  35. metadata +1 -26
  36. data/lib/dspy/events/subscriber_mixin.rb +0 -79
  37. data/lib/dspy/events/subscribers.rb +0 -43
  38. data/lib/dspy/memory/embedding_engine.rb +0 -68
  39. data/lib/dspy/memory/in_memory_store.rb +0 -216
  40. data/lib/dspy/memory/local_embedding_engine.rb +0 -244
  41. data/lib/dspy/memory/memory_compactor.rb +0 -298
  42. data/lib/dspy/memory/memory_manager.rb +0 -266
  43. data/lib/dspy/memory/memory_record.rb +0 -163
  44. data/lib/dspy/memory/memory_store.rb +0 -90
  45. data/lib/dspy/memory.rb +0 -30
  46. data/lib/dspy/tools/memory_toolset.rb +0 -117
@@ -1,163 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'sorbet-runtime'
4
- require 'securerandom'
5
-
6
- module DSPy
7
- module Memory
8
- # Represents a single memory entry with metadata and embeddings
9
- class MemoryRecord
10
- extend T::Sig
11
-
12
- sig { returns(String) }
13
- attr_reader :id
14
-
15
- sig { returns(String) }
16
- attr_accessor :content
17
-
18
- sig { returns(T.nilable(String)) }
19
- attr_accessor :user_id
20
-
21
- sig { returns(T::Array[String]) }
22
- attr_accessor :tags
23
-
24
- sig { returns(T.nilable(T::Array[Float])) }
25
- attr_accessor :embedding
26
-
27
- sig { returns(Time) }
28
- attr_reader :created_at
29
-
30
- sig { returns(Time) }
31
- attr_accessor :updated_at
32
-
33
- sig { returns(Integer) }
34
- attr_accessor :access_count
35
-
36
- sig { returns(T.nilable(Time)) }
37
- attr_accessor :last_accessed_at
38
-
39
- sig { returns(T::Hash[String, T.untyped]) }
40
- attr_accessor :metadata
41
-
42
- sig do
43
- params(
44
- content: String,
45
- user_id: T.nilable(String),
46
- tags: T::Array[String],
47
- embedding: T.nilable(T::Array[Float]),
48
- id: T.nilable(String),
49
- metadata: T::Hash[String, T.untyped]
50
- ).void
51
- end
52
- def initialize(content:, user_id: nil, tags: [], embedding: nil, id: nil, metadata: {})
53
- @id = id || SecureRandom.uuid
54
- @content = content
55
- @user_id = user_id
56
- @tags = tags
57
- @embedding = embedding
58
- @created_at = Time.now
59
- @updated_at = Time.now
60
- @access_count = 0
61
- @last_accessed_at = nil
62
- @metadata = metadata
63
- end
64
-
65
- # Record an access to this memory
66
- sig { void }
67
- def record_access!
68
- @access_count += 1
69
- @last_accessed_at = Time.now
70
- end
71
-
72
- # Update the content and timestamp
73
- sig { params(new_content: String).void }
74
- def update_content!(new_content)
75
- @content = new_content
76
- @updated_at = Time.now
77
- end
78
-
79
- # Calculate age in seconds
80
- sig { returns(Float) }
81
- def age_in_seconds
82
- Time.now - @created_at
83
- end
84
-
85
- # Calculate age in days
86
- sig { returns(Float) }
87
- def age_in_days
88
- age_in_seconds / 86400.0
89
- end
90
-
91
- # Check if memory has been accessed recently (within last N seconds)
92
- sig { params(seconds: Integer).returns(T::Boolean) }
93
- def accessed_recently?(seconds = 3600)
94
- return false if @last_accessed_at.nil?
95
- (Time.now - @last_accessed_at) <= seconds
96
- end
97
-
98
- # Check if memory matches a tag
99
- sig { params(tag: String).returns(T::Boolean) }
100
- def has_tag?(tag)
101
- @tags.include?(tag)
102
- end
103
-
104
- # Add a tag if not already present
105
- sig { params(tag: String).void }
106
- def add_tag(tag)
107
- @tags << tag unless @tags.include?(tag)
108
- end
109
-
110
- # Remove a tag
111
- sig { params(tag: String).void }
112
- def remove_tag(tag)
113
- @tags.delete(tag)
114
- end
115
-
116
- # Convert to hash for serialization
117
- sig { returns(T::Hash[String, T.untyped]) }
118
- def to_h
119
- {
120
- 'id' => @id,
121
- 'content' => @content,
122
- 'user_id' => @user_id,
123
- 'tags' => @tags,
124
- 'embedding' => @embedding,
125
- 'created_at' => @created_at.iso8601,
126
- 'updated_at' => @updated_at.iso8601,
127
- 'access_count' => @access_count,
128
- 'last_accessed_at' => @last_accessed_at&.iso8601,
129
- 'metadata' => @metadata
130
- }
131
- end
132
-
133
- # Create from hash (for deserialization)
134
- sig { params(hash: T::Hash[String, T.untyped]).returns(MemoryRecord) }
135
- def self.from_h(hash)
136
- record = allocate
137
- record.instance_variable_set(:@id, hash['id'])
138
- record.instance_variable_set(:@content, hash['content'])
139
- record.instance_variable_set(:@user_id, hash['user_id'])
140
- record.instance_variable_set(:@tags, hash['tags'] || [])
141
- record.instance_variable_set(:@embedding, hash['embedding'])
142
- record.instance_variable_set(:@created_at, Time.parse(hash['created_at']))
143
- record.instance_variable_set(:@updated_at, Time.parse(hash['updated_at']))
144
- record.instance_variable_set(:@access_count, hash['access_count'] || 0)
145
- record.instance_variable_set(:@last_accessed_at,
146
- hash['last_accessed_at'] ? Time.parse(hash['last_accessed_at']) : nil)
147
- record.instance_variable_set(:@metadata, hash['metadata'] || {})
148
- record
149
- end
150
-
151
- # String representation
152
- sig { returns(String) }
153
- def to_s
154
- "#<MemoryRecord id=#{@id[0..7]}... content=\"#{@content[0..50]}...\" tags=#{@tags}>"
155
- end
156
-
157
- sig { returns(String) }
158
- def inspect
159
- to_s
160
- end
161
- end
162
- end
163
- end
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'sorbet-runtime'
4
-
5
- module DSPy
6
- module Memory
7
- # Abstract base class for memory storage backends
8
- class MemoryStore
9
- extend T::Sig
10
- extend T::Helpers
11
- abstract!
12
-
13
- # Store a memory record
14
- sig { abstract.params(record: MemoryRecord).returns(T::Boolean) }
15
- def store(record); end
16
-
17
- # Retrieve a memory record by ID
18
- sig { abstract.params(id: String).returns(T.nilable(MemoryRecord)) }
19
- def retrieve(id); end
20
-
21
- # Update an existing memory record
22
- sig { abstract.params(record: MemoryRecord).returns(T::Boolean) }
23
- def update(record); end
24
-
25
- # Delete a memory record by ID
26
- sig { abstract.params(id: String).returns(T::Boolean) }
27
- def delete(id); end
28
-
29
- # List all memory records for a user
30
- sig { abstract.params(user_id: T.nilable(String), limit: T.nilable(Integer), offset: T.nilable(Integer)).returns(T::Array[MemoryRecord]) }
31
- def list(user_id: nil, limit: nil, offset: nil); end
32
-
33
- # Search memories by content (basic text search)
34
- sig { abstract.params(query: String, user_id: T.nilable(String), limit: T.nilable(Integer)).returns(T::Array[MemoryRecord]) }
35
- def search(query, user_id: nil, limit: nil); end
36
-
37
- # Search memories by tags
38
- sig { abstract.params(tags: T::Array[String], user_id: T.nilable(String), limit: T.nilable(Integer)).returns(T::Array[MemoryRecord]) }
39
- def search_by_tags(tags, user_id: nil, limit: nil); end
40
-
41
- # Vector similarity search (if supported by backend)
42
- sig { abstract.params(embedding: T::Array[Float], user_id: T.nilable(String), limit: T.nilable(Integer), threshold: T.nilable(Float)).returns(T::Array[MemoryRecord]) }
43
- def vector_search(embedding, user_id: nil, limit: nil, threshold: nil); end
44
-
45
- # Count total memories
46
- sig { abstract.params(user_id: T.nilable(String)).returns(Integer) }
47
- def count(user_id: nil); end
48
-
49
- # Clear all memories for a user (or all if user_id is nil)
50
- sig { abstract.params(user_id: T.nilable(String)).returns(Integer) }
51
- def clear(user_id: nil); end
52
-
53
- # Check if the store supports vector search
54
- sig { returns(T::Boolean) }
55
- def supports_vector_search?
56
- false
57
- end
58
-
59
- # Get store statistics
60
- sig { returns(T::Hash[Symbol, T.untyped]) }
61
- def stats
62
- {
63
- total_memories: count,
64
- supports_vector_search: supports_vector_search?
65
- }
66
- end
67
-
68
- # Batch operations
69
- sig { params(records: T::Array[MemoryRecord]).returns(T::Array[T::Boolean]) }
70
- def store_batch(records)
71
- records.map { |record| store(record) }
72
- end
73
-
74
- sig { params(ids: T::Array[String]).returns(T::Array[T.nilable(MemoryRecord)]) }
75
- def retrieve_batch(ids)
76
- ids.map { |id| retrieve(id) }
77
- end
78
-
79
- sig { params(records: T::Array[MemoryRecord]).returns(T::Array[T::Boolean]) }
80
- def update_batch(records)
81
- records.map { |record| update(record) }
82
- end
83
-
84
- sig { params(ids: T::Array[String]).returns(T::Array[T::Boolean]) }
85
- def delete_batch(ids)
86
- ids.map { |id| delete(id) }
87
- end
88
- end
89
- end
90
- end
data/lib/dspy/memory.rb DELETED
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'memory/memory_record'
4
- require_relative 'memory/memory_store'
5
- require_relative 'memory/in_memory_store'
6
- require_relative 'memory/embedding_engine'
7
- require_relative 'memory/local_embedding_engine'
8
- require_relative 'memory/memory_compactor'
9
- require_relative 'memory/memory_manager'
10
-
11
- module DSPy
12
- # Memory system for persistent, searchable agent memory
13
- module Memory
14
- class << self
15
- extend T::Sig
16
-
17
- # Configure the memory system
18
- sig { returns(MemoryManager) }
19
- def manager
20
- @manager ||= MemoryManager.new
21
- end
22
-
23
- # Reset the memory system (useful for testing)
24
- sig { void }
25
- def reset!
26
- @manager = nil
27
- end
28
- end
29
- end
30
- end
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'sorbet-runtime'
4
- require_relative 'toolset'
5
-
6
- module DSPy
7
- module Tools
8
- # Example implementation of a memory toolset for agents
9
- # Provides tools for storing, retrieving, and managing memory
10
- class MemoryToolset < Toolset
11
- extend T::Sig
12
-
13
- toolset_name "memory"
14
-
15
- # Expose methods as tools with descriptions
16
- tool :store, description: "Store a key-value pair in memory with optional tags"
17
- tool :retrieve, description: "Retrieve a value by key from memory"
18
- tool :search, description: "Search memories by pattern in keys and/or values"
19
- tool :list_keys, tool_name: "memory_list", description: "List all stored memory keys"
20
- tool :update, description: "Update an existing memory value"
21
- tool :delete, description: "Delete a memory by key"
22
- tool :clear, description: "Clear all stored memories"
23
- tool :count, description: "Get the count of stored memories"
24
- tool :get_metadata, description: "Get metadata for a specific memory"
25
-
26
- sig { void }
27
- def initialize
28
- @memory = T.let({}, T::Hash[String, T::Hash[Symbol, T.untyped]])
29
- end
30
-
31
- sig { params(key: String, value: String, tags: T.nilable(T::Array[String])).returns(String) }
32
- def store(key:, value:, tags: nil)
33
- @memory[key] = {
34
- value: value,
35
- tags: tags || [],
36
- created_at: Time.now,
37
- updated_at: Time.now,
38
- access_count: 0
39
- }
40
- "Stored memory '#{key}' successfully"
41
- end
42
-
43
- sig { params(key: String).returns(T.nilable(String)) }
44
- def retrieve(key:)
45
- entry = @memory[key]
46
- return nil unless entry
47
-
48
- # Track access
49
- entry[:access_count] += 1
50
- entry[:last_accessed_at] = Time.now
51
- entry[:value]
52
- end
53
-
54
- sig { params(pattern: String, in_keys: T::Boolean, in_values: T::Boolean).returns(T::Array[T::Hash[Symbol, String]]) }
55
- def search(pattern:, in_keys: true, in_values: true)
56
- results = []
57
- regex = Regexp.new(pattern, Regexp::IGNORECASE)
58
-
59
- @memory.each do |key, entry|
60
- match = (in_keys && key.match?(regex)) || (in_values && entry[:value].match?(regex))
61
- results << { key: key, value: entry[:value] } if match
62
- end
63
-
64
- results
65
- end
66
-
67
- sig { returns(T::Array[String]) }
68
- def list_keys
69
- @memory.keys.sort
70
- end
71
-
72
- sig { params(key: String, value: String).returns(String) }
73
- def update(key:, value:)
74
- return "Memory '#{key}' not found" unless @memory.key?(key)
75
-
76
- @memory[key][:value] = value
77
- @memory[key][:updated_at] = Time.now
78
- "Updated memory '#{key}' successfully"
79
- end
80
-
81
- sig { params(key: String).returns(String) }
82
- def delete(key:)
83
- return "Memory '#{key}' not found" unless @memory.key?(key)
84
-
85
- @memory.delete(key)
86
- "Deleted memory '#{key}' successfully"
87
- end
88
-
89
- sig { returns(String) }
90
- def clear
91
- count = @memory.size
92
- @memory.clear
93
- "Cleared #{count} memories"
94
- end
95
-
96
- sig { returns(Integer) }
97
- def count
98
- @memory.size
99
- end
100
-
101
- sig { params(key: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
102
- def get_metadata(key:)
103
- entry = @memory[key]
104
- return nil unless entry
105
-
106
- {
107
- created_at: entry[:created_at],
108
- updated_at: entry[:updated_at],
109
- access_count: entry[:access_count],
110
- last_accessed_at: entry[:last_accessed_at],
111
- tags: entry[:tags],
112
- value_length: entry[:value].length
113
- }
114
- end
115
- end
116
- end
117
- end