dspy 0.3.1 → 0.4.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 +69 -382
- data/lib/dspy/chain_of_thought.rb +57 -0
- data/lib/dspy/evaluate.rb +554 -0
- data/lib/dspy/example.rb +203 -0
- data/lib/dspy/few_shot_example.rb +81 -0
- data/lib/dspy/instrumentation.rb +97 -8
- data/lib/dspy/lm/adapter_factory.rb +6 -8
- data/lib/dspy/lm.rb +5 -7
- data/lib/dspy/predict.rb +32 -34
- data/lib/dspy/prompt.rb +222 -0
- data/lib/dspy/propose/grounded_proposer.rb +560 -0
- data/lib/dspy/registry/registry_manager.rb +504 -0
- data/lib/dspy/registry/signature_registry.rb +725 -0
- data/lib/dspy/storage/program_storage.rb +442 -0
- data/lib/dspy/storage/storage_manager.rb +331 -0
- data/lib/dspy/subscribers/langfuse_subscriber.rb +669 -0
- data/lib/dspy/subscribers/logger_subscriber.rb +120 -0
- data/lib/dspy/subscribers/newrelic_subscriber.rb +686 -0
- data/lib/dspy/subscribers/otel_subscriber.rb +538 -0
- data/lib/dspy/teleprompt/data_handler.rb +107 -0
- data/lib/dspy/teleprompt/mipro_v2.rb +790 -0
- data/lib/dspy/teleprompt/simple_optimizer.rb +497 -0
- data/lib/dspy/teleprompt/teleprompter.rb +336 -0
- data/lib/dspy/teleprompt/utils.rb +380 -0
- data/lib/dspy/version.rb +5 -0
- data/lib/dspy.rb +16 -0
- metadata +29 -12
- data/lib/dspy/lm/adapters/ruby_llm_adapter.rb +0 -81
@@ -0,0 +1,504 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sorbet-runtime'
|
4
|
+
require_relative 'signature_registry'
|
5
|
+
|
6
|
+
module DSPy
|
7
|
+
module Registry
|
8
|
+
# High-level registry manager that integrates with the DSPy ecosystem
|
9
|
+
# Provides automatic version management and integration with optimization results
|
10
|
+
class RegistryManager
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
# Configuration for automatic registry integration
|
14
|
+
class RegistryIntegrationConfig
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { returns(T::Boolean) }
|
18
|
+
attr_accessor :auto_register_optimizations
|
19
|
+
|
20
|
+
sig { returns(T::Boolean) }
|
21
|
+
attr_accessor :auto_deploy_best_versions
|
22
|
+
|
23
|
+
sig { returns(Float) }
|
24
|
+
attr_accessor :auto_deploy_threshold
|
25
|
+
|
26
|
+
sig { returns(T::Boolean) }
|
27
|
+
attr_accessor :rollback_on_performance_drop
|
28
|
+
|
29
|
+
sig { returns(Float) }
|
30
|
+
attr_accessor :rollback_threshold
|
31
|
+
|
32
|
+
sig { returns(String) }
|
33
|
+
attr_accessor :deployment_strategy
|
34
|
+
|
35
|
+
sig { void }
|
36
|
+
def initialize
|
37
|
+
@auto_register_optimizations = true
|
38
|
+
@auto_deploy_best_versions = false
|
39
|
+
@auto_deploy_threshold = 0.1 # 10% improvement
|
40
|
+
@rollback_on_performance_drop = true
|
41
|
+
@rollback_threshold = 0.05 # 5% drop
|
42
|
+
@deployment_strategy = "conservative" # conservative, aggressive, manual
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
46
|
+
def to_h
|
47
|
+
{
|
48
|
+
auto_register_optimizations: @auto_register_optimizations,
|
49
|
+
auto_deploy_best_versions: @auto_deploy_best_versions,
|
50
|
+
auto_deploy_threshold: @auto_deploy_threshold,
|
51
|
+
rollback_on_performance_drop: @rollback_on_performance_drop,
|
52
|
+
rollback_threshold: @rollback_threshold,
|
53
|
+
deployment_strategy: @deployment_strategy
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { returns(SignatureRegistry) }
|
59
|
+
attr_reader :registry
|
60
|
+
|
61
|
+
sig { returns(RegistryIntegrationConfig) }
|
62
|
+
attr_reader :integration_config
|
63
|
+
|
64
|
+
sig do
|
65
|
+
params(
|
66
|
+
registry_config: T.nilable(SignatureRegistry::RegistryConfig),
|
67
|
+
integration_config: T.nilable(RegistryIntegrationConfig)
|
68
|
+
).void
|
69
|
+
end
|
70
|
+
def initialize(registry_config: nil, integration_config: nil)
|
71
|
+
@registry = SignatureRegistry.new(config: registry_config)
|
72
|
+
@integration_config = integration_config || RegistryIntegrationConfig.new
|
73
|
+
end
|
74
|
+
|
75
|
+
# Register an optimization result automatically
|
76
|
+
sig do
|
77
|
+
params(
|
78
|
+
optimization_result: T.untyped,
|
79
|
+
signature_name: T.nilable(String),
|
80
|
+
metadata: T::Hash[Symbol, T.untyped]
|
81
|
+
).returns(T.nilable(SignatureRegistry::SignatureVersion))
|
82
|
+
end
|
83
|
+
def register_optimization_result(optimization_result, signature_name: nil, metadata: {})
|
84
|
+
return nil unless @integration_config.auto_register_optimizations
|
85
|
+
|
86
|
+
# Extract signature name if not provided
|
87
|
+
signature_name ||= extract_signature_name(optimization_result)
|
88
|
+
return nil unless signature_name
|
89
|
+
|
90
|
+
# Extract configuration from optimization result
|
91
|
+
configuration = extract_configuration(optimization_result)
|
92
|
+
|
93
|
+
# Get performance score
|
94
|
+
performance_score = extract_performance_score(optimization_result)
|
95
|
+
|
96
|
+
# Enhanced metadata
|
97
|
+
enhanced_metadata = metadata.merge({
|
98
|
+
optimizer: extract_optimizer_name(optimization_result),
|
99
|
+
optimization_timestamp: extract_optimization_timestamp(optimization_result),
|
100
|
+
trials_count: extract_trials_count(optimization_result),
|
101
|
+
auto_registered: true
|
102
|
+
})
|
103
|
+
|
104
|
+
# Register the version
|
105
|
+
version = @registry.register_version(
|
106
|
+
signature_name,
|
107
|
+
configuration,
|
108
|
+
metadata: enhanced_metadata,
|
109
|
+
program_id: extract_program_id(optimization_result)
|
110
|
+
)
|
111
|
+
|
112
|
+
# Update performance score
|
113
|
+
if performance_score
|
114
|
+
@registry.update_performance_score(signature_name, version.version, performance_score)
|
115
|
+
version = version.with_performance_score(performance_score)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Check for auto-deployment
|
119
|
+
check_auto_deployment(signature_name, version)
|
120
|
+
|
121
|
+
version
|
122
|
+
end
|
123
|
+
|
124
|
+
# Create a deployment strategy
|
125
|
+
sig { params(signature_name: String, strategy: String).returns(T.nilable(SignatureRegistry::SignatureVersion)) }
|
126
|
+
def deploy_with_strategy(signature_name, strategy: nil)
|
127
|
+
strategy ||= @integration_config.deployment_strategy
|
128
|
+
|
129
|
+
case strategy
|
130
|
+
when "conservative"
|
131
|
+
deploy_conservative(signature_name)
|
132
|
+
when "aggressive"
|
133
|
+
deploy_aggressive(signature_name)
|
134
|
+
when "best_score"
|
135
|
+
deploy_best_score(signature_name)
|
136
|
+
else
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Monitor and rollback if needed
|
142
|
+
sig { params(signature_name: String, current_score: Float).returns(T::Boolean) }
|
143
|
+
def monitor_and_rollback(signature_name, current_score)
|
144
|
+
return false unless @integration_config.rollback_on_performance_drop
|
145
|
+
|
146
|
+
deployed_version = @registry.get_deployed_version(signature_name)
|
147
|
+
return false unless deployed_version&.performance_score
|
148
|
+
|
149
|
+
# Check if performance dropped significantly
|
150
|
+
performance_drop = deployed_version.performance_score - current_score
|
151
|
+
threshold_drop = deployed_version.performance_score * @integration_config.rollback_threshold
|
152
|
+
|
153
|
+
if performance_drop > threshold_drop
|
154
|
+
rollback_result = @registry.rollback(signature_name)
|
155
|
+
emit_automatic_rollback_event(signature_name, current_score, deployed_version.performance_score)
|
156
|
+
!rollback_result.nil?
|
157
|
+
else
|
158
|
+
false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Get deployment status and recommendations
|
163
|
+
sig { params(signature_name: String).returns(T::Hash[Symbol, T.untyped]) }
|
164
|
+
def get_deployment_status(signature_name)
|
165
|
+
deployed_version = @registry.get_deployed_version(signature_name)
|
166
|
+
all_versions = @registry.list_versions(signature_name)
|
167
|
+
performance_history = @registry.get_performance_history(signature_name)
|
168
|
+
|
169
|
+
recommendations = generate_deployment_recommendations(signature_name, all_versions, deployed_version)
|
170
|
+
|
171
|
+
{
|
172
|
+
deployed_version: deployed_version&.to_h,
|
173
|
+
total_versions: all_versions.size,
|
174
|
+
performance_history: performance_history,
|
175
|
+
recommendations: recommendations,
|
176
|
+
last_updated: all_versions.max_by(&:created_at)&.created_at&.iso8601
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
# Create a safe deployment plan
|
181
|
+
sig { params(signature_name: String, target_version: String).returns(T::Hash[Symbol, T.untyped]) }
|
182
|
+
def create_deployment_plan(signature_name, target_version)
|
183
|
+
current_deployed = @registry.get_deployed_version(signature_name)
|
184
|
+
target = @registry.list_versions(signature_name).find { |v| v.version == target_version }
|
185
|
+
|
186
|
+
return { error: "Target version not found" } unless target
|
187
|
+
|
188
|
+
plan = {
|
189
|
+
signature_name: signature_name,
|
190
|
+
current_version: current_deployed&.version,
|
191
|
+
target_version: target_version,
|
192
|
+
deployment_safe: true,
|
193
|
+
checks: [],
|
194
|
+
recommendations: []
|
195
|
+
}
|
196
|
+
|
197
|
+
# Performance check
|
198
|
+
if current_deployed&.performance_score && target.performance_score
|
199
|
+
performance_change = target.performance_score - current_deployed.performance_score
|
200
|
+
plan[:performance_change] = performance_change
|
201
|
+
|
202
|
+
if performance_change < 0
|
203
|
+
plan[:checks] << "Performance regression detected"
|
204
|
+
plan[:deployment_safe] = false
|
205
|
+
else
|
206
|
+
plan[:checks] << "Performance improvement expected"
|
207
|
+
end
|
208
|
+
else
|
209
|
+
plan[:checks] << "No performance data available"
|
210
|
+
plan[:recommendations] << "Run evaluation before deployment"
|
211
|
+
end
|
212
|
+
|
213
|
+
# Version age check
|
214
|
+
if target.created_at < (Time.now - 7 * 24 * 60 * 60) # 7 days old
|
215
|
+
plan[:recommendations] << "Target version is more than 7 days old"
|
216
|
+
end
|
217
|
+
|
218
|
+
# Configuration complexity check
|
219
|
+
config_complexity = estimate_configuration_complexity(target.configuration)
|
220
|
+
if config_complexity > 0.8
|
221
|
+
plan[:recommendations] << "Complex configuration detected - consider gradual rollout"
|
222
|
+
end
|
223
|
+
|
224
|
+
plan
|
225
|
+
end
|
226
|
+
|
227
|
+
# Bulk operations for managing multiple signatures
|
228
|
+
sig { params(signature_names: T::Array[String]).returns(T::Hash[String, T.untyped]) }
|
229
|
+
def bulk_deployment_status(signature_names)
|
230
|
+
results = {}
|
231
|
+
|
232
|
+
signature_names.each do |name|
|
233
|
+
results[name] = get_deployment_status(name)
|
234
|
+
end
|
235
|
+
|
236
|
+
results
|
237
|
+
end
|
238
|
+
|
239
|
+
# Clean up old versions across all signatures
|
240
|
+
sig { returns(T::Hash[Symbol, Integer]) }
|
241
|
+
def cleanup_old_versions
|
242
|
+
cleaned_signatures = 0
|
243
|
+
cleaned_versions = 0
|
244
|
+
|
245
|
+
@registry.list_signatures.each do |signature_name|
|
246
|
+
versions = @registry.list_versions(signature_name)
|
247
|
+
|
248
|
+
# Keep deployed version and recent versions
|
249
|
+
deployed_version = versions.find(&:is_deployed)
|
250
|
+
recent_versions = versions.sort_by(&:created_at).last(5)
|
251
|
+
|
252
|
+
keep_versions = [deployed_version, *recent_versions].compact.uniq
|
253
|
+
|
254
|
+
if keep_versions.size < versions.size
|
255
|
+
@registry.send(:save_signature_versions, signature_name, keep_versions)
|
256
|
+
cleaned_signatures += 1
|
257
|
+
cleaned_versions += (versions.size - keep_versions.size)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
{
|
262
|
+
cleaned_signatures: cleaned_signatures,
|
263
|
+
cleaned_versions: cleaned_versions
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
# Global registry instance
|
268
|
+
@@instance = T.let(nil, T.nilable(RegistryManager))
|
269
|
+
|
270
|
+
sig { returns(RegistryManager) }
|
271
|
+
def self.instance
|
272
|
+
@@instance ||= new
|
273
|
+
end
|
274
|
+
|
275
|
+
sig { params(registry_config: SignatureRegistry::RegistryConfig, integration_config: RegistryIntegrationConfig).void }
|
276
|
+
def self.configure(registry_config: nil, integration_config: nil)
|
277
|
+
@@instance = new(registry_config: registry_config, integration_config: integration_config)
|
278
|
+
end
|
279
|
+
|
280
|
+
private
|
281
|
+
|
282
|
+
sig { params(optimization_result: T.untyped).returns(T.nilable(String)) }
|
283
|
+
def extract_signature_name(optimization_result)
|
284
|
+
if optimization_result.respond_to?(:optimized_program)
|
285
|
+
program = optimization_result.optimized_program
|
286
|
+
if program.respond_to?(:signature_class)
|
287
|
+
program.signature_class&.name
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
sig { params(optimization_result: T.untyped).returns(T::Hash[Symbol, T.untyped]) }
|
293
|
+
def extract_configuration(optimization_result)
|
294
|
+
config = {}
|
295
|
+
|
296
|
+
if optimization_result.respond_to?(:optimized_program)
|
297
|
+
program = optimization_result.optimized_program
|
298
|
+
|
299
|
+
# Extract instruction
|
300
|
+
if program.respond_to?(:prompt) && program.prompt.respond_to?(:instruction)
|
301
|
+
config[:instruction] = program.prompt.instruction
|
302
|
+
end
|
303
|
+
|
304
|
+
# Extract few-shot examples
|
305
|
+
if program.respond_to?(:few_shot_examples)
|
306
|
+
config[:few_shot_examples_count] = program.few_shot_examples.size
|
307
|
+
config[:few_shot_examples] = program.few_shot_examples.map do |example|
|
308
|
+
{
|
309
|
+
input: example.respond_to?(:input) ? example.input : nil,
|
310
|
+
output: example.respond_to?(:output) ? example.output : nil
|
311
|
+
}
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Extract optimization metadata
|
317
|
+
if optimization_result.respond_to?(:metadata)
|
318
|
+
config[:optimization_metadata] = optimization_result.metadata
|
319
|
+
end
|
320
|
+
|
321
|
+
config
|
322
|
+
end
|
323
|
+
|
324
|
+
sig { params(optimization_result: T.untyped).returns(T.nilable(Float)) }
|
325
|
+
def extract_performance_score(optimization_result)
|
326
|
+
if optimization_result.respond_to?(:best_score_value)
|
327
|
+
optimization_result.best_score_value
|
328
|
+
elsif optimization_result.respond_to?(:scores) && optimization_result.scores.is_a?(Hash)
|
329
|
+
optimization_result.scores.values.first
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
sig { params(optimization_result: T.untyped).returns(T.nilable(String)) }
|
334
|
+
def extract_optimizer_name(optimization_result)
|
335
|
+
if optimization_result.respond_to?(:metadata) && optimization_result.metadata[:optimizer]
|
336
|
+
optimization_result.metadata[:optimizer]
|
337
|
+
else
|
338
|
+
optimization_result.class.name
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
sig { params(optimization_result: T.untyped).returns(T.nilable(String)) }
|
343
|
+
def extract_optimization_timestamp(optimization_result)
|
344
|
+
if optimization_result.respond_to?(:metadata)
|
345
|
+
optimization_result.metadata[:optimization_timestamp]
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
sig { params(optimization_result: T.untyped).returns(T.nilable(Integer)) }
|
350
|
+
def extract_trials_count(optimization_result)
|
351
|
+
if optimization_result.respond_to?(:history) && optimization_result.history[:total_trials]
|
352
|
+
optimization_result.history[:total_trials]
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
sig { params(optimization_result: T.untyped).returns(T.nilable(String)) }
|
357
|
+
def extract_program_id(optimization_result)
|
358
|
+
# This would typically come from storage system
|
359
|
+
if optimization_result.respond_to?(:metadata) && optimization_result.metadata[:program_id]
|
360
|
+
optimization_result.metadata[:program_id]
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
sig { params(signature_name: String, version: SignatureRegistry::SignatureVersion).void }
|
365
|
+
def check_auto_deployment(signature_name, version)
|
366
|
+
return unless @integration_config.auto_deploy_best_versions
|
367
|
+
return unless version.performance_score
|
368
|
+
|
369
|
+
deployed_version = @registry.get_deployed_version(signature_name)
|
370
|
+
|
371
|
+
should_deploy = if deployed_version&.performance_score
|
372
|
+
improvement = version.performance_score - deployed_version.performance_score
|
373
|
+
improvement >= @integration_config.auto_deploy_threshold
|
374
|
+
else
|
375
|
+
true # No current deployment, deploy this one
|
376
|
+
end
|
377
|
+
|
378
|
+
if should_deploy
|
379
|
+
@registry.deploy_version(signature_name, version.version)
|
380
|
+
emit_auto_deployment_event(signature_name, version.version)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
sig { params(signature_name: String).returns(T.nilable(SignatureRegistry::SignatureVersion)) }
|
385
|
+
def deploy_conservative(signature_name)
|
386
|
+
versions = @registry.list_versions(signature_name)
|
387
|
+
deployed = @registry.get_deployed_version(signature_name)
|
388
|
+
|
389
|
+
# Only deploy if significantly better than current
|
390
|
+
candidates = versions.select { |v| v.performance_score }
|
391
|
+
return nil if candidates.empty?
|
392
|
+
|
393
|
+
best_candidate = candidates.max_by(&:performance_score)
|
394
|
+
|
395
|
+
if deployed&.performance_score
|
396
|
+
improvement = best_candidate.performance_score - deployed.performance_score
|
397
|
+
threshold = deployed.performance_score * 0.1 # 10% improvement required
|
398
|
+
|
399
|
+
if improvement >= threshold
|
400
|
+
@registry.deploy_version(signature_name, best_candidate.version)
|
401
|
+
else
|
402
|
+
nil
|
403
|
+
end
|
404
|
+
else
|
405
|
+
@registry.deploy_version(signature_name, best_candidate.version)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
sig { params(signature_name: String).returns(T.nilable(SignatureRegistry::SignatureVersion)) }
|
410
|
+
def deploy_aggressive(signature_name)
|
411
|
+
versions = @registry.list_versions(signature_name)
|
412
|
+
candidates = versions.select { |v| v.performance_score }
|
413
|
+
return nil if candidates.empty?
|
414
|
+
|
415
|
+
# Deploy the best version regardless of current deployment
|
416
|
+
best_candidate = candidates.max_by(&:performance_score)
|
417
|
+
@registry.deploy_version(signature_name, best_candidate.version)
|
418
|
+
end
|
419
|
+
|
420
|
+
sig { params(signature_name: String).returns(T.nilable(SignatureRegistry::SignatureVersion)) }
|
421
|
+
def deploy_best_score(signature_name)
|
422
|
+
deploy_aggressive(signature_name) # Same as aggressive for now
|
423
|
+
end
|
424
|
+
|
425
|
+
sig do
|
426
|
+
params(
|
427
|
+
signature_name: String,
|
428
|
+
versions: T::Array[SignatureRegistry::SignatureVersion],
|
429
|
+
deployed: T.nilable(SignatureRegistry::SignatureVersion)
|
430
|
+
).returns(T::Array[String])
|
431
|
+
end
|
432
|
+
def generate_deployment_recommendations(signature_name, versions, deployed)
|
433
|
+
recommendations = []
|
434
|
+
|
435
|
+
if deployed.nil?
|
436
|
+
if versions.any? { |v| v.performance_score }
|
437
|
+
recommendations << "No version deployed - consider deploying best performing version"
|
438
|
+
else
|
439
|
+
recommendations << "No version deployed - run evaluation on versions before deployment"
|
440
|
+
end
|
441
|
+
else
|
442
|
+
# Check for better versions
|
443
|
+
better_versions = versions.select do |v|
|
444
|
+
v.performance_score &&
|
445
|
+
deployed.performance_score &&
|
446
|
+
v.performance_score > deployed.performance_score
|
447
|
+
end
|
448
|
+
|
449
|
+
if better_versions.any?
|
450
|
+
best = better_versions.max_by(&:performance_score)
|
451
|
+
improvement = ((best.performance_score - deployed.performance_score) / deployed.performance_score * 100).round(1)
|
452
|
+
recommendations << "Version #{best.version} shows #{improvement}% improvement"
|
453
|
+
end
|
454
|
+
|
455
|
+
# Check for old deployment
|
456
|
+
if deployed.created_at < (Time.now - 30 * 24 * 60 * 60) # 30 days
|
457
|
+
recommendations << "Current deployment is over 30 days old - consider updating"
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
recommendations
|
462
|
+
end
|
463
|
+
|
464
|
+
sig { params(configuration: T::Hash[Symbol, T.untyped]).returns(Float) }
|
465
|
+
def estimate_configuration_complexity(configuration)
|
466
|
+
complexity = 0.0
|
467
|
+
|
468
|
+
# Instruction complexity
|
469
|
+
if configuration[:instruction]
|
470
|
+
instruction_length = configuration[:instruction].length
|
471
|
+
complexity += [instruction_length / 1000.0, 0.5].min
|
472
|
+
end
|
473
|
+
|
474
|
+
# Few-shot examples complexity
|
475
|
+
if configuration[:few_shot_examples_count]
|
476
|
+
examples_complexity = [configuration[:few_shot_examples_count] / 10.0, 0.5].min
|
477
|
+
complexity += examples_complexity
|
478
|
+
end
|
479
|
+
|
480
|
+
[complexity, 1.0].min
|
481
|
+
end
|
482
|
+
|
483
|
+
sig { params(signature_name: String, version: String).void }
|
484
|
+
def emit_auto_deployment_event(signature_name, version)
|
485
|
+
DSPy::Instrumentation.emit('dspy.registry.auto_deployment', {
|
486
|
+
signature_name: signature_name,
|
487
|
+
version: version,
|
488
|
+
timestamp: Time.now.iso8601
|
489
|
+
})
|
490
|
+
end
|
491
|
+
|
492
|
+
sig { params(signature_name: String, current_score: Float, previous_score: Float).void }
|
493
|
+
def emit_automatic_rollback_event(signature_name, current_score, previous_score)
|
494
|
+
DSPy::Instrumentation.emit('dspy.registry.automatic_rollback', {
|
495
|
+
signature_name: signature_name,
|
496
|
+
current_score: current_score,
|
497
|
+
previous_score: previous_score,
|
498
|
+
performance_drop: previous_score - current_score,
|
499
|
+
timestamp: Time.now.iso8601
|
500
|
+
})
|
501
|
+
end
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|