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.
- checksums.yaml +4 -4
- data/lib/dspy/chain_of_thought.rb +3 -2
- data/lib/dspy/context.rb +17 -1
- data/lib/dspy/evals/version.rb +1 -1
- data/lib/dspy/evals.rb +42 -31
- data/lib/dspy/events.rb +2 -3
- data/lib/dspy/example.rb +1 -1
- data/lib/dspy/lm/adapter.rb +39 -0
- data/lib/dspy/lm/json_strategy.rb +37 -2
- data/lib/dspy/lm/message.rb +1 -1
- data/lib/dspy/lm/response.rb +1 -1
- data/lib/dspy/lm/usage.rb +4 -4
- data/lib/dspy/lm.rb +9 -49
- data/lib/dspy/mixins/type_coercion.rb +189 -30
- data/lib/dspy/module.rb +70 -25
- data/lib/dspy/predict.rb +32 -5
- data/lib/dspy/prediction.rb +15 -57
- data/lib/dspy/prompt.rb +50 -30
- data/lib/dspy/propose/dataset_summary_generator.rb +1 -1
- data/lib/dspy/propose/grounded_proposer.rb +3 -3
- data/lib/dspy/re_act.rb +0 -162
- data/lib/dspy/registry/signature_registry.rb +3 -3
- data/lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb +1 -27
- data/lib/dspy/schema/sorbet_json_schema.rb +7 -6
- data/lib/dspy/schema/version.rb +1 -1
- data/lib/dspy/schema_adapters.rb +1 -1
- data/lib/dspy/storage/program_storage.rb +2 -2
- data/lib/dspy/structured_outputs_prompt.rb +3 -3
- data/lib/dspy/teleprompt/utils.rb +2 -2
- data/lib/dspy/tools/github_cli_toolset.rb +7 -7
- data/lib/dspy/tools/text_processing_toolset.rb +2 -2
- data/lib/dspy/tools/toolset.rb +1 -1
- data/lib/dspy/version.rb +1 -1
- data/lib/dspy.rb +1 -4
- metadata +1 -26
- data/lib/dspy/events/subscriber_mixin.rb +0 -79
- data/lib/dspy/events/subscribers.rb +0 -43
- data/lib/dspy/memory/embedding_engine.rb +0 -68
- data/lib/dspy/memory/in_memory_store.rb +0 -216
- data/lib/dspy/memory/local_embedding_engine.rb +0 -244
- data/lib/dspy/memory/memory_compactor.rb +0 -298
- data/lib/dspy/memory/memory_manager.rb +0 -266
- data/lib/dspy/memory/memory_record.rb +0 -163
- data/lib/dspy/memory/memory_store.rb +0 -90
- data/lib/dspy/memory.rb +0 -30
- 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
|