dspy 0.17.0 → 0.18.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.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/dspy/chain_of_thought.rb +13 -16
- data/lib/dspy/code_act.rb +31 -37
- data/lib/dspy/context.rb +67 -0
- data/lib/dspy/evaluate.rb +20 -17
- data/lib/dspy/lm.rb +72 -37
- data/lib/dspy/memory/memory_compactor.rb +5 -6
- data/lib/dspy/memory/memory_manager.rb +5 -4
- data/lib/dspy/observability.rb +109 -0
- data/lib/dspy/predict.rb +18 -6
- data/lib/dspy/propose/grounded_proposer.rb +13 -12
- data/lib/dspy/re_act.rb +34 -41
- data/lib/dspy/registry/registry_manager.rb +8 -10
- data/lib/dspy/registry/signature_registry.rb +40 -52
- data/lib/dspy/storage/program_storage.rb +28 -37
- data/lib/dspy/storage/storage_manager.rb +3 -4
- data/lib/dspy/teleprompt/teleprompter.rb +11 -12
- data/lib/dspy/teleprompt/utils.rb +24 -22
- data/lib/dspy/version.rb +1 -1
- data/lib/dspy.rb +42 -82
- metadata +31 -24
- data/lib/dspy/instrumentation/event_payload_factory.rb +0 -282
- data/lib/dspy/instrumentation/event_payloads.rb +0 -476
- data/lib/dspy/instrumentation/token_tracker.rb +0 -70
- data/lib/dspy/instrumentation.rb +0 -341
- data/lib/dspy/mixins/instrumentation_helpers.rb +0 -120
- data/lib/dspy/subscribers/langfuse_subscriber.rb +0 -669
- data/lib/dspy/subscribers/logger_subscriber.rb +0 -480
- data/lib/dspy/subscribers/newrelic_subscriber.rb +0 -686
- data/lib/dspy/subscribers/otel_subscriber.rb +0 -537
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module DSPy
|
6
|
+
class Observability
|
7
|
+
class << self
|
8
|
+
attr_reader :enabled, :tracer, :endpoint
|
9
|
+
|
10
|
+
def configure!
|
11
|
+
@enabled = false
|
12
|
+
|
13
|
+
# Check for required Langfuse environment variables
|
14
|
+
public_key = ENV['LANGFUSE_PUBLIC_KEY']
|
15
|
+
secret_key = ENV['LANGFUSE_SECRET_KEY']
|
16
|
+
|
17
|
+
unless public_key && secret_key
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
# Determine endpoint based on host
|
22
|
+
host = ENV['LANGFUSE_HOST'] || 'https://cloud.langfuse.com'
|
23
|
+
@endpoint = "#{host}/api/public/otel"
|
24
|
+
|
25
|
+
begin
|
26
|
+
# Load OpenTelemetry gems
|
27
|
+
require 'opentelemetry/sdk'
|
28
|
+
require 'opentelemetry/exporter/otlp'
|
29
|
+
|
30
|
+
# Generate Basic Auth header
|
31
|
+
auth_string = Base64.strict_encode64("#{public_key}:#{secret_key}")
|
32
|
+
|
33
|
+
# Configure OpenTelemetry SDK
|
34
|
+
OpenTelemetry::SDK.configure do |config|
|
35
|
+
config.service_name = 'dspy-ruby'
|
36
|
+
config.service_version = DSPy::VERSION
|
37
|
+
|
38
|
+
# Add OTLP exporter for Langfuse
|
39
|
+
config.add_span_processor(
|
40
|
+
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
41
|
+
OpenTelemetry::Exporter::OTLP::Exporter.new(
|
42
|
+
endpoint: @endpoint,
|
43
|
+
headers: {
|
44
|
+
'Authorization' => "Basic #{auth_string}",
|
45
|
+
'Content-Type' => 'application/x-protobuf'
|
46
|
+
},
|
47
|
+
compression: 'gzip'
|
48
|
+
)
|
49
|
+
)
|
50
|
+
)
|
51
|
+
|
52
|
+
# Add resource attributes
|
53
|
+
config.resource = OpenTelemetry::SDK::Resources::Resource.create({
|
54
|
+
'service.name' => 'dspy-ruby',
|
55
|
+
'service.version' => DSPy::VERSION,
|
56
|
+
'telemetry.sdk.name' => 'opentelemetry',
|
57
|
+
'telemetry.sdk.language' => 'ruby'
|
58
|
+
})
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create tracer
|
62
|
+
@tracer = OpenTelemetry.tracer_provider.tracer('dspy', DSPy::VERSION)
|
63
|
+
@enabled = true
|
64
|
+
|
65
|
+
rescue LoadError => e
|
66
|
+
DSPy.log('observability.disabled', reason: 'OpenTelemetry gems not available')
|
67
|
+
rescue StandardError => e
|
68
|
+
DSPy.log('observability.error', error: e.message, class: e.class.name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def enabled?
|
73
|
+
@enabled == true
|
74
|
+
end
|
75
|
+
|
76
|
+
def start_span(operation_name, attributes = {})
|
77
|
+
return nil unless enabled? && tracer
|
78
|
+
|
79
|
+
# Convert attribute keys to strings and filter out nil values
|
80
|
+
string_attributes = attributes.transform_keys(&:to_s)
|
81
|
+
.reject { |k, v| v.nil? }
|
82
|
+
string_attributes['operation.name'] = operation_name
|
83
|
+
|
84
|
+
tracer.start_span(
|
85
|
+
operation_name,
|
86
|
+
kind: :internal,
|
87
|
+
attributes: string_attributes
|
88
|
+
)
|
89
|
+
rescue StandardError => e
|
90
|
+
DSPy.log('observability.span_error', error: e.message, operation: operation_name)
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def finish_span(span)
|
95
|
+
return unless span
|
96
|
+
|
97
|
+
span.finish
|
98
|
+
rescue StandardError => e
|
99
|
+
DSPy.log('observability.span_finish_error', error: e.message)
|
100
|
+
end
|
101
|
+
|
102
|
+
def reset!
|
103
|
+
@enabled = false
|
104
|
+
@tracer = nil
|
105
|
+
@endpoint = nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/dspy/predict.rb
CHANGED
@@ -2,11 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'sorbet-runtime'
|
4
4
|
require_relative 'module'
|
5
|
-
require_relative 'instrumentation'
|
6
5
|
require_relative 'prompt'
|
7
6
|
require_relative 'mixins/struct_builder'
|
8
7
|
require_relative 'mixins/type_coercion'
|
9
|
-
require_relative 'mixins/instrumentation_helpers'
|
10
8
|
|
11
9
|
module DSPy
|
12
10
|
# Exception raised when prediction fails validation
|
@@ -27,7 +25,6 @@ module DSPy
|
|
27
25
|
extend T::Sig
|
28
26
|
include Mixins::StructBuilder
|
29
27
|
include Mixins::TypeCoercion
|
30
|
-
include Mixins::InstrumentationHelpers
|
31
28
|
|
32
29
|
sig { returns(T.class_of(Signature)) }
|
33
30
|
attr_reader :signature_class
|
@@ -85,12 +82,23 @@ module DSPy
|
|
85
82
|
|
86
83
|
sig { params(input_values: T.untyped).returns(T.untyped) }
|
87
84
|
def forward_untyped(**input_values)
|
88
|
-
|
85
|
+
# Wrap prediction in span tracking
|
86
|
+
DSPy::Context.with_span(
|
87
|
+
operation: "#{self.class.name}.forward",
|
88
|
+
'dspy.module' => self.class.name,
|
89
|
+
'dspy.signature' => @signature_class.name
|
90
|
+
) do
|
89
91
|
# Validate input
|
90
92
|
validate_input_struct(input_values)
|
91
93
|
|
94
|
+
# Check if LM is configured
|
95
|
+
current_lm = lm
|
96
|
+
if current_lm.nil?
|
97
|
+
raise DSPy::ConfigurationError.missing_lm(self.class.name)
|
98
|
+
end
|
99
|
+
|
92
100
|
# Call LM and process response
|
93
|
-
output_attributes =
|
101
|
+
output_attributes = current_lm.chat(self, input_values)
|
94
102
|
processed_output = process_lm_output(output_attributes)
|
95
103
|
|
96
104
|
# Create combined result struct
|
@@ -105,7 +113,11 @@ module DSPy
|
|
105
113
|
def validate_input_struct(input_values)
|
106
114
|
@signature_class.input_struct_class.new(**input_values)
|
107
115
|
rescue ArgumentError => e
|
108
|
-
|
116
|
+
DSPy.log('prediction.validation_error', **{
|
117
|
+
'dspy.signature' => @signature_class.name,
|
118
|
+
'prediction.validation_type' => 'input',
|
119
|
+
'prediction.validation_errors' => { input: e.message }
|
120
|
+
})
|
109
121
|
raise PredictionInvalidError.new({ input: e.message })
|
110
122
|
end
|
111
123
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'sorbet-runtime'
|
4
|
-
require_relative '../instrumentation'
|
5
4
|
require_relative '../signature'
|
6
5
|
require_relative '../predict'
|
7
6
|
|
@@ -104,12 +103,14 @@ module DSPy
|
|
104
103
|
).returns(ProposalResult)
|
105
104
|
end
|
106
105
|
def propose_instructions(signature_class, examples, few_shot_examples: nil, current_instruction: nil)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
106
|
+
DSPy::Context.with_span(
|
107
|
+
operation: 'optimization.instruction_proposal',
|
108
|
+
'dspy.module' => 'GroundedProposer',
|
109
|
+
'proposal.signature' => signature_class.name,
|
110
|
+
'proposal.num_examples' => examples.size,
|
111
|
+
'proposal.has_few_shot' => !few_shot_examples.nil?,
|
112
|
+
'proposal.has_current_instruction' => !current_instruction.nil?
|
113
|
+
) do
|
113
114
|
# Analyze the task and training data
|
114
115
|
analysis = analyze_task(signature_class, examples, few_shot_examples)
|
115
116
|
|
@@ -548,11 +549,11 @@ module DSPy
|
|
548
549
|
# Emit instruction proposal completion event
|
549
550
|
sig { params(result: ProposalResult).void }
|
550
551
|
def emit_proposal_complete_event(result)
|
551
|
-
|
552
|
-
num_candidates
|
553
|
-
best_instruction_length
|
554
|
-
analysis_themes
|
555
|
-
model_used
|
552
|
+
DSPy.log('optimization.instruction_proposal_complete', **{
|
553
|
+
'proposal.num_candidates' => result.num_candidates,
|
554
|
+
'proposal.best_instruction_length' => result.best_instruction.length,
|
555
|
+
'proposal.analysis_themes' => result.analysis[:common_themes] || [],
|
556
|
+
'proposal.model_used' => @config.proposal_model
|
556
557
|
})
|
557
558
|
end
|
558
559
|
end
|
data/lib/dspy/re_act.rb
CHANGED
@@ -6,9 +6,7 @@ require_relative 'predict'
|
|
6
6
|
require_relative 'signature'
|
7
7
|
require_relative 'chain_of_thought'
|
8
8
|
require 'json'
|
9
|
-
require_relative 'instrumentation'
|
10
9
|
require_relative 'mixins/struct_builder'
|
11
|
-
require_relative 'mixins/instrumentation_helpers'
|
12
10
|
|
13
11
|
module DSPy
|
14
12
|
# Define a simple struct for history entries with proper type annotations
|
@@ -67,7 +65,6 @@ module DSPy
|
|
67
65
|
class ReAct < Predict
|
68
66
|
extend T::Sig
|
69
67
|
include Mixins::StructBuilder
|
70
|
-
include Mixins::InstrumentationHelpers
|
71
68
|
|
72
69
|
FINISH_ACTION = "finish"
|
73
70
|
sig { returns(T.class_of(DSPy::Signature)) }
|
@@ -129,22 +126,14 @@ module DSPy
|
|
129
126
|
lm = config.lm || DSPy.config.lm
|
130
127
|
available_tools = @tools.keys
|
131
128
|
|
132
|
-
#
|
133
|
-
|
134
|
-
max_iterations: @max_iterations,
|
135
|
-
available_tools: available_tools
|
136
|
-
}) do
|
137
|
-
# Validate input
|
138
|
-
input_struct = @original_signature_class.input_struct_class.new(**kwargs)
|
129
|
+
# Validate input
|
130
|
+
input_struct = @original_signature_class.input_struct_class.new(**kwargs)
|
139
131
|
|
140
|
-
|
141
|
-
|
132
|
+
# Execute ReAct reasoning loop
|
133
|
+
reasoning_result = execute_react_reasoning_loop(input_struct)
|
142
134
|
|
143
|
-
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
result
|
135
|
+
# Create enhanced output with all ReAct data
|
136
|
+
create_enhanced_result(kwargs, reasoning_result)
|
148
137
|
end
|
149
138
|
|
150
139
|
private
|
@@ -247,13 +236,15 @@ module DSPy
|
|
247
236
|
# Executes a single iteration of the ReAct loop
|
248
237
|
sig { params(input_struct: T.untyped, history: T::Array[HistoryEntry], available_tools_desc: T::Array[T::Hash[String, T.untyped]], iteration: Integer, tools_used: T::Array[String], last_observation: T.nilable(String)).returns(T::Hash[Symbol, T.untyped]) }
|
249
238
|
def execute_single_iteration(input_struct, history, available_tools_desc, iteration, tools_used, last_observation)
|
250
|
-
#
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
239
|
+
# Track each iteration with span
|
240
|
+
DSPy::Context.with_span(
|
241
|
+
operation: 'react.iteration',
|
242
|
+
'dspy.module' => 'ReAct',
|
243
|
+
'react.iteration' => iteration,
|
244
|
+
'react.max_iterations' => @max_iterations,
|
245
|
+
'react.history_length' => history.length,
|
246
|
+
'react.tools_used' => tools_used.uniq
|
247
|
+
) do
|
257
248
|
# Generate thought and action
|
258
249
|
thought_obj = @thought_generator.forward(
|
259
250
|
input_context: DSPy::TypeSerializer.serialize(input_struct).to_json,
|
@@ -357,11 +348,13 @@ module DSPy
|
|
357
348
|
sig { params(action: T.nilable(String), action_input: T.untyped, iteration: Integer).returns(String) }
|
358
349
|
def execute_tool_with_instrumentation(action, action_input, iteration)
|
359
350
|
if action && @tools[action.downcase]
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
351
|
+
DSPy::Context.with_span(
|
352
|
+
operation: 'react.tool_call',
|
353
|
+
'dspy.module' => 'ReAct',
|
354
|
+
'react.iteration' => iteration,
|
355
|
+
'tool.name' => action.downcase,
|
356
|
+
'tool.input' => action_input
|
357
|
+
) do
|
365
358
|
execute_action(action, action_input)
|
366
359
|
end
|
367
360
|
else
|
@@ -421,24 +414,24 @@ module DSPy
|
|
421
414
|
|
422
415
|
sig { params(iteration: Integer, thought: String, action: String, action_input: T.untyped, observation: String, tools_used: T::Array[String]).void }
|
423
416
|
def emit_iteration_complete_event(iteration, thought, action, action_input, observation, tools_used)
|
424
|
-
|
425
|
-
iteration
|
426
|
-
thought
|
427
|
-
action
|
428
|
-
action_input
|
429
|
-
observation
|
430
|
-
tools_used
|
417
|
+
DSPy.log('react.iteration_complete', **{
|
418
|
+
'react.iteration' => iteration,
|
419
|
+
'react.thought' => thought,
|
420
|
+
'react.action' => action,
|
421
|
+
'react.action_input' => action_input,
|
422
|
+
'react.observation' => observation,
|
423
|
+
'react.tools_used' => tools_used.uniq
|
431
424
|
})
|
432
425
|
end
|
433
426
|
|
434
427
|
sig { params(iterations_count: Integer, final_answer: T.nilable(String), tools_used: T::Array[String], history: T::Array[HistoryEntry]).void }
|
435
428
|
def handle_max_iterations_if_needed(iterations_count, final_answer, tools_used, history)
|
436
429
|
if iterations_count >= @max_iterations && final_answer.nil?
|
437
|
-
|
438
|
-
iteration_count
|
439
|
-
max_iterations
|
440
|
-
tools_used
|
441
|
-
final_history_length
|
430
|
+
DSPy.log('react.max_iterations', **{
|
431
|
+
'react.iteration_count' => iterations_count,
|
432
|
+
'react.max_iterations' => @max_iterations,
|
433
|
+
'react.tools_used' => tools_used.uniq,
|
434
|
+
'react.final_history_length' => history.length
|
442
435
|
})
|
443
436
|
end
|
444
437
|
end
|
@@ -482,21 +482,19 @@ module DSPy
|
|
482
482
|
|
483
483
|
sig { params(signature_name: String, version: String).void }
|
484
484
|
def emit_auto_deployment_event(signature_name, version)
|
485
|
-
DSPy
|
486
|
-
signature_name
|
487
|
-
version
|
488
|
-
timestamp: Time.now.iso8601
|
485
|
+
DSPy.log('registry.auto_deployment', **{
|
486
|
+
'registry.signature_name' => signature_name,
|
487
|
+
'registry.version' => version
|
489
488
|
})
|
490
489
|
end
|
491
490
|
|
492
491
|
sig { params(signature_name: String, current_score: Float, previous_score: Float).void }
|
493
492
|
def emit_automatic_rollback_event(signature_name, current_score, previous_score)
|
494
|
-
DSPy
|
495
|
-
signature_name
|
496
|
-
current_score
|
497
|
-
previous_score
|
498
|
-
performance_drop
|
499
|
-
timestamp: Time.now.iso8601
|
493
|
+
DSPy.log('registry.automatic_rollback', **{
|
494
|
+
'registry.signature_name' => signature_name,
|
495
|
+
'registry.current_score' => current_score,
|
496
|
+
'registry.previous_score' => previous_score,
|
497
|
+
'registry.performance_drop' => previous_score - current_score
|
500
498
|
})
|
501
499
|
end
|
502
500
|
end
|
@@ -611,113 +611,101 @@ module DSPy
|
|
611
611
|
# Event emission methods
|
612
612
|
sig { params(signature_name: String, version: T.nilable(String)).void }
|
613
613
|
def emit_register_start_event(signature_name, version)
|
614
|
-
DSPy
|
615
|
-
signature_name
|
616
|
-
version
|
617
|
-
timestamp: Time.now.iso8601
|
614
|
+
DSPy.log('registry.register_start', **{
|
615
|
+
'registry.signature_name' => signature_name,
|
616
|
+
'registry.version' => version
|
618
617
|
})
|
619
618
|
end
|
620
619
|
|
621
620
|
sig { params(version: SignatureVersion).void }
|
622
621
|
def emit_register_complete_event(version)
|
623
|
-
DSPy
|
624
|
-
signature_name
|
625
|
-
version
|
626
|
-
version_hash
|
627
|
-
timestamp: Time.now.iso8601
|
622
|
+
DSPy.log('registry.register_complete', **{
|
623
|
+
'registry.signature_name' => version.signature_name,
|
624
|
+
'registry.version' => version.version,
|
625
|
+
'registry.version_hash' => version.version_hash
|
628
626
|
})
|
629
627
|
end
|
630
628
|
|
631
629
|
sig { params(signature_name: String, version: T.nilable(String), error: Exception).void }
|
632
630
|
def emit_register_error_event(signature_name, version, error)
|
633
|
-
DSPy
|
634
|
-
signature_name
|
635
|
-
version
|
636
|
-
error
|
637
|
-
timestamp: Time.now.iso8601
|
631
|
+
DSPy.log('registry.register_error', **{
|
632
|
+
'registry.signature_name' => signature_name,
|
633
|
+
'registry.version' => version,
|
634
|
+
'registry.error' => error.message
|
638
635
|
})
|
639
636
|
end
|
640
637
|
|
641
638
|
sig { params(signature_name: String, version: String).void }
|
642
639
|
def emit_deploy_start_event(signature_name, version)
|
643
|
-
DSPy
|
644
|
-
signature_name
|
645
|
-
version
|
646
|
-
timestamp: Time.now.iso8601
|
640
|
+
DSPy.log('registry.deploy_start', **{
|
641
|
+
'registry.signature_name' => signature_name,
|
642
|
+
'registry.version' => version
|
647
643
|
})
|
648
644
|
end
|
649
645
|
|
650
646
|
sig { params(version: SignatureVersion).void }
|
651
647
|
def emit_deploy_complete_event(version)
|
652
|
-
DSPy
|
653
|
-
signature_name
|
654
|
-
version
|
655
|
-
performance_score
|
656
|
-
timestamp: Time.now.iso8601
|
648
|
+
DSPy.log('registry.deploy_complete', **{
|
649
|
+
'registry.signature_name' => version.signature_name,
|
650
|
+
'registry.version' => version.version,
|
651
|
+
'registry.performance_score' => version.performance_score
|
657
652
|
})
|
658
653
|
end
|
659
654
|
|
660
655
|
sig { params(signature_name: String, version: String, error: Exception).void }
|
661
656
|
def emit_deploy_error_event(signature_name, version, error)
|
662
|
-
DSPy
|
663
|
-
signature_name
|
664
|
-
version
|
665
|
-
error
|
666
|
-
timestamp: Time.now.iso8601
|
657
|
+
DSPy.log('registry.deploy_error', **{
|
658
|
+
'registry.signature_name' => signature_name,
|
659
|
+
'registry.version' => version,
|
660
|
+
'registry.error' => error.message
|
667
661
|
})
|
668
662
|
end
|
669
663
|
|
670
664
|
sig { params(signature_name: String).void }
|
671
665
|
def emit_rollback_start_event(signature_name)
|
672
|
-
DSPy
|
673
|
-
signature_name
|
674
|
-
timestamp: Time.now.iso8601
|
666
|
+
DSPy.log('registry.rollback_start', **{
|
667
|
+
'registry.signature_name' => signature_name
|
675
668
|
})
|
676
669
|
end
|
677
670
|
|
678
671
|
sig { params(version: SignatureVersion).void }
|
679
672
|
def emit_rollback_complete_event(version)
|
680
|
-
DSPy
|
681
|
-
signature_name
|
682
|
-
version
|
683
|
-
timestamp: Time.now.iso8601
|
673
|
+
DSPy.log('registry.rollback_complete', **{
|
674
|
+
'registry.signature_name' => version.signature_name,
|
675
|
+
'registry.version' => version.version
|
684
676
|
})
|
685
677
|
end
|
686
678
|
|
687
679
|
sig { params(signature_name: String, error_message: String).void }
|
688
680
|
def emit_rollback_error_event(signature_name, error_message)
|
689
|
-
DSPy
|
690
|
-
signature_name
|
691
|
-
error
|
692
|
-
timestamp: Time.now.iso8601
|
681
|
+
DSPy.log('registry.rollback_error', **{
|
682
|
+
'registry.signature_name' => signature_name,
|
683
|
+
'registry.error' => error_message
|
693
684
|
})
|
694
685
|
end
|
695
686
|
|
696
687
|
sig { params(version: SignatureVersion).void }
|
697
688
|
def emit_performance_update_event(version)
|
698
|
-
DSPy
|
699
|
-
signature_name
|
700
|
-
version
|
701
|
-
performance_score
|
702
|
-
timestamp: Time.now.iso8601
|
689
|
+
DSPy.log('registry.performance_update', **{
|
690
|
+
'registry.signature_name' => version.signature_name,
|
691
|
+
'registry.version' => version.version,
|
692
|
+
'registry.performance_score' => version.performance_score
|
703
693
|
})
|
704
694
|
end
|
705
695
|
|
706
696
|
sig { params(export_path: String, signature_count: Integer).void }
|
707
697
|
def emit_export_event(export_path, signature_count)
|
708
|
-
DSPy
|
709
|
-
export_path
|
710
|
-
signature_count
|
711
|
-
timestamp: Time.now.iso8601
|
698
|
+
DSPy.log('registry.export', **{
|
699
|
+
'registry.export_path' => export_path,
|
700
|
+
'registry.signature_count' => signature_count
|
712
701
|
})
|
713
702
|
end
|
714
703
|
|
715
704
|
sig { params(import_path: String, signature_count: Integer).void }
|
716
705
|
def emit_import_event(import_path, signature_count)
|
717
|
-
DSPy
|
718
|
-
import_path
|
719
|
-
signature_count
|
720
|
-
timestamp: Time.now.iso8601
|
706
|
+
DSPy.log('registry.import', **{
|
707
|
+
'registry.import_path' => import_path,
|
708
|
+
'registry.signature_count' => signature_count
|
721
709
|
})
|
722
710
|
end
|
723
711
|
end
|
@@ -357,84 +357,75 @@ module DSPy
|
|
357
357
|
# Event emission methods
|
358
358
|
sig { params(program_id: T.nilable(String)).void }
|
359
359
|
def emit_save_start_event(program_id)
|
360
|
-
DSPy
|
361
|
-
program_id
|
362
|
-
|
363
|
-
timestamp: Time.now.iso8601
|
360
|
+
DSPy.log('storage.save_start', **{
|
361
|
+
'storage.program_id' => program_id,
|
362
|
+
'storage.path' => @storage_path
|
364
363
|
})
|
365
364
|
end
|
366
365
|
|
367
366
|
sig { params(saved_program: SavedProgram).void }
|
368
367
|
def emit_save_complete_event(saved_program)
|
369
|
-
DSPy
|
370
|
-
program_id
|
371
|
-
best_score
|
372
|
-
|
373
|
-
timestamp: Time.now.iso8601
|
368
|
+
DSPy.log('storage.save_complete', **{
|
369
|
+
'storage.program_id' => saved_program.program_id,
|
370
|
+
'storage.best_score' => saved_program.optimization_result[:best_score_value],
|
371
|
+
'storage.file_size' => File.size(program_file_path(saved_program.program_id))
|
374
372
|
})
|
375
373
|
end
|
376
374
|
|
377
375
|
sig { params(program_id: T.nilable(String), error: Exception).void }
|
378
376
|
def emit_save_error_event(program_id, error)
|
379
|
-
DSPy
|
380
|
-
program_id
|
381
|
-
error
|
382
|
-
error_class
|
383
|
-
timestamp: Time.now.iso8601
|
377
|
+
DSPy.log('storage.save_error', **{
|
378
|
+
'storage.program_id' => program_id,
|
379
|
+
'storage.error' => error.message,
|
380
|
+
'storage.error_class' => error.class.name
|
384
381
|
})
|
385
382
|
end
|
386
383
|
|
387
384
|
sig { params(program_id: String).void }
|
388
385
|
def emit_load_start_event(program_id)
|
389
|
-
DSPy
|
390
|
-
program_id
|
391
|
-
timestamp: Time.now.iso8601
|
386
|
+
DSPy.log('storage.load_start', **{
|
387
|
+
'storage.program_id' => program_id
|
392
388
|
})
|
393
389
|
end
|
394
390
|
|
395
391
|
sig { params(saved_program: SavedProgram).void }
|
396
392
|
def emit_load_complete_event(saved_program)
|
397
|
-
DSPy
|
398
|
-
program_id
|
399
|
-
saved_at
|
400
|
-
age_hours
|
401
|
-
timestamp: Time.now.iso8601
|
393
|
+
DSPy.log('storage.load_complete', **{
|
394
|
+
'storage.program_id' => saved_program.program_id,
|
395
|
+
'storage.saved_at' => saved_program.saved_at.iso8601,
|
396
|
+
'storage.age_hours' => ((Time.now - saved_program.saved_at) / 3600).round(2)
|
402
397
|
})
|
403
398
|
end
|
404
399
|
|
405
400
|
sig { params(program_id: String, error: T.any(String, Exception)).void }
|
406
401
|
def emit_load_error_event(program_id, error)
|
407
402
|
error_message = error.is_a?(Exception) ? error.message : error.to_s
|
408
|
-
DSPy
|
409
|
-
program_id
|
410
|
-
error
|
411
|
-
timestamp: Time.now.iso8601
|
403
|
+
DSPy.log('storage.load_error', **{
|
404
|
+
'storage.program_id' => program_id,
|
405
|
+
'storage.error' => error_message
|
412
406
|
})
|
413
407
|
end
|
414
408
|
|
415
409
|
sig { params(program_id: String).void }
|
416
410
|
def emit_delete_event(program_id)
|
417
|
-
DSPy
|
418
|
-
program_id
|
419
|
-
timestamp: Time.now.iso8601
|
411
|
+
DSPy.log('storage.delete', **{
|
412
|
+
'storage.program_id' => program_id
|
420
413
|
})
|
421
414
|
end
|
422
415
|
|
423
416
|
sig { params(export_path: String, program_count: Integer).void }
|
424
417
|
def emit_export_event(export_path, program_count)
|
425
|
-
DSPy
|
426
|
-
export_path
|
427
|
-
program_count
|
428
|
-
timestamp: Time.now.iso8601
|
418
|
+
DSPy.log('storage.export', **{
|
419
|
+
'storage.export_path' => export_path,
|
420
|
+
'storage.program_count' => program_count
|
429
421
|
})
|
430
422
|
end
|
431
423
|
|
432
424
|
sig { params(import_path: String, program_count: Integer).void }
|
433
425
|
def emit_import_event(import_path, program_count)
|
434
|
-
DSPy
|
435
|
-
import_path
|
436
|
-
program_count
|
437
|
-
timestamp: Time.now.iso8601
|
426
|
+
DSPy.log('storage.import', **{
|
427
|
+
'storage.import_path' => import_path,
|
428
|
+
'storage.program_count' => program_count
|
438
429
|
})
|
439
430
|
end
|
440
431
|
end
|
@@ -250,10 +250,9 @@ module DSPy
|
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
253
|
-
DSPy
|
254
|
-
deleted_count
|
255
|
-
remaining_count
|
256
|
-
timestamp: Time.now.iso8601
|
253
|
+
DSPy.log('storage.cleanup', **{
|
254
|
+
'storage.deleted_count' => deleted_count,
|
255
|
+
'storage.remaining_count' => @config.max_stored_programs
|
257
256
|
})
|
258
257
|
|
259
258
|
deleted_count
|