desiru 0.1.0 → 0.1.1

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +34 -0
  3. data/.rubocop.yml +7 -4
  4. data/.ruby-version +1 -0
  5. data/CLAUDE.md +4 -0
  6. data/Gemfile +21 -2
  7. data/Gemfile.lock +87 -12
  8. data/README.md +295 -2
  9. data/Rakefile +1 -0
  10. data/db/migrations/001_create_initial_tables.rb +96 -0
  11. data/db/migrations/002_create_job_results.rb +39 -0
  12. data/desiru.db +0 -0
  13. data/desiru.gemspec +2 -5
  14. data/docs/background_processing_roadmap.md +87 -0
  15. data/docs/job_scheduling.md +167 -0
  16. data/dspy-analysis-swarm.yml +60 -0
  17. data/dspy-feature-analysis.md +121 -0
  18. data/examples/README.md +69 -0
  19. data/examples/api_with_persistence.rb +122 -0
  20. data/examples/assertions_example.rb +232 -0
  21. data/examples/async_processing.rb +2 -0
  22. data/examples/few_shot_learning.rb +1 -2
  23. data/examples/graphql_api.rb +4 -2
  24. data/examples/graphql_integration.rb +3 -3
  25. data/examples/graphql_optimization_summary.md +143 -0
  26. data/examples/graphql_performance_benchmark.rb +247 -0
  27. data/examples/persistence_example.rb +102 -0
  28. data/examples/react_agent.rb +203 -0
  29. data/examples/rest_api.rb +173 -0
  30. data/examples/rest_api_advanced.rb +333 -0
  31. data/examples/scheduled_job_example.rb +116 -0
  32. data/examples/simple_qa.rb +1 -2
  33. data/examples/sinatra_api.rb +109 -0
  34. data/examples/typed_signatures.rb +1 -2
  35. data/graphql_optimization_summary.md +53 -0
  36. data/lib/desiru/api/grape_integration.rb +284 -0
  37. data/lib/desiru/api/persistence_middleware.rb +148 -0
  38. data/lib/desiru/api/sinatra_integration.rb +217 -0
  39. data/lib/desiru/api.rb +42 -0
  40. data/lib/desiru/assertions.rb +74 -0
  41. data/lib/desiru/async_status.rb +65 -0
  42. data/lib/desiru/cache.rb +1 -1
  43. data/lib/desiru/configuration.rb +2 -1
  44. data/lib/desiru/errors.rb +160 -0
  45. data/lib/desiru/field.rb +17 -14
  46. data/lib/desiru/graphql/batch_loader.rb +85 -0
  47. data/lib/desiru/graphql/data_loader.rb +242 -75
  48. data/lib/desiru/graphql/enum_builder.rb +75 -0
  49. data/lib/desiru/graphql/executor.rb +37 -4
  50. data/lib/desiru/graphql/schema_generator.rb +62 -158
  51. data/lib/desiru/graphql/type_builder.rb +138 -0
  52. data/lib/desiru/graphql/type_cache_warmer.rb +91 -0
  53. data/lib/desiru/jobs/async_predict.rb +1 -1
  54. data/lib/desiru/jobs/base.rb +67 -0
  55. data/lib/desiru/jobs/batch_processor.rb +6 -6
  56. data/lib/desiru/jobs/retriable.rb +119 -0
  57. data/lib/desiru/jobs/retry_strategies.rb +169 -0
  58. data/lib/desiru/jobs/scheduler.rb +219 -0
  59. data/lib/desiru/jobs/webhook_notifier.rb +242 -0
  60. data/lib/desiru/models/anthropic.rb +164 -0
  61. data/lib/desiru/models/base.rb +37 -3
  62. data/lib/desiru/models/open_ai.rb +151 -0
  63. data/lib/desiru/models/open_router.rb +161 -0
  64. data/lib/desiru/module.rb +59 -9
  65. data/lib/desiru/modules/chain_of_thought.rb +3 -3
  66. data/lib/desiru/modules/majority.rb +51 -0
  67. data/lib/desiru/modules/multi_chain_comparison.rb +204 -0
  68. data/lib/desiru/modules/predict.rb +8 -1
  69. data/lib/desiru/modules/program_of_thought.rb +139 -0
  70. data/lib/desiru/modules/react.rb +273 -0
  71. data/lib/desiru/modules/retrieve.rb +4 -2
  72. data/lib/desiru/optimizers/base.rb +2 -4
  73. data/lib/desiru/optimizers/bootstrap_few_shot.rb +2 -2
  74. data/lib/desiru/optimizers/copro.rb +268 -0
  75. data/lib/desiru/optimizers/knn_few_shot.rb +185 -0
  76. data/lib/desiru/persistence/database.rb +71 -0
  77. data/lib/desiru/persistence/models/api_request.rb +38 -0
  78. data/lib/desiru/persistence/models/job_result.rb +138 -0
  79. data/lib/desiru/persistence/models/module_execution.rb +37 -0
  80. data/lib/desiru/persistence/models/optimization_result.rb +28 -0
  81. data/lib/desiru/persistence/models/training_example.rb +25 -0
  82. data/lib/desiru/persistence/models.rb +11 -0
  83. data/lib/desiru/persistence/repositories/api_request_repository.rb +98 -0
  84. data/lib/desiru/persistence/repositories/base_repository.rb +77 -0
  85. data/lib/desiru/persistence/repositories/job_result_repository.rb +116 -0
  86. data/lib/desiru/persistence/repositories/module_execution_repository.rb +85 -0
  87. data/lib/desiru/persistence/repositories/optimization_result_repository.rb +67 -0
  88. data/lib/desiru/persistence/repositories/training_example_repository.rb +102 -0
  89. data/lib/desiru/persistence/repository.rb +29 -0
  90. data/lib/desiru/persistence/setup.rb +77 -0
  91. data/lib/desiru/persistence.rb +49 -0
  92. data/lib/desiru/registry.rb +3 -5
  93. data/lib/desiru/signature.rb +91 -24
  94. data/lib/desiru/version.rb +1 -1
  95. data/lib/desiru.rb +23 -8
  96. data/missing-features-analysis.md +192 -0
  97. metadata +63 -45
  98. data/lib/desiru/models/raix_adapter.rb +0 -210
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: desiru
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Obie Fernandez
@@ -65,48 +65,6 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0.1'
68
- - !ruby/object:Gem::Dependency
69
- name: bundler
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '2.0'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '2.0'
82
- - !ruby/object:Gem::Dependency
83
- name: rake
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '13.0'
89
- type: :development
90
- prerelease: false
91
- version_requirements: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '13.0'
96
- - !ruby/object:Gem::Dependency
97
- name: rspec
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '3.0'
103
- type: :development
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '3.0'
110
68
  description: Desiru brings DSPy's declarative programming paradigm for language models
111
69
  to Ruby, enabling reliable, maintainable, and portable AI programming.
112
70
  email:
@@ -115,47 +73,107 @@ executables: []
115
73
  extensions: []
116
74
  extra_rdoc_files: []
117
75
  files:
76
+ - ".env.example"
118
77
  - ".rspec"
119
78
  - ".rubocop.yml"
79
+ - ".ruby-version"
120
80
  - CLAUDE.md
121
81
  - Gemfile
122
82
  - Gemfile.lock
123
83
  - LICENSE
124
84
  - README.md
125
85
  - Rakefile
86
+ - db/migrations/001_create_initial_tables.rb
87
+ - db/migrations/002_create_job_results.rb
88
+ - desiru.db
126
89
  - desiru.gemspec
90
+ - docs/background_processing_roadmap.md
91
+ - docs/job_scheduling.md
92
+ - dspy-analysis-swarm.yml
93
+ - dspy-feature-analysis.md
127
94
  - examples/README.md
95
+ - examples/api_with_persistence.rb
96
+ - examples/assertions_example.rb
128
97
  - examples/async_processing.rb
129
98
  - examples/few_shot_learning.rb
130
99
  - examples/graphql_api.rb
131
100
  - examples/graphql_integration.rb
101
+ - examples/graphql_optimization_summary.md
102
+ - examples/graphql_performance_benchmark.rb
103
+ - examples/persistence_example.rb
132
104
  - examples/rag_retrieval.rb
105
+ - examples/react_agent.rb
106
+ - examples/rest_api.rb
107
+ - examples/rest_api_advanced.rb
108
+ - examples/scheduled_job_example.rb
133
109
  - examples/simple_qa.rb
110
+ - examples/sinatra_api.rb
134
111
  - examples/typed_signatures.rb
112
+ - graphql_optimization_summary.md
135
113
  - lib/desiru.rb
114
+ - lib/desiru/api.rb
115
+ - lib/desiru/api/grape_integration.rb
116
+ - lib/desiru/api/persistence_middleware.rb
117
+ - lib/desiru/api/sinatra_integration.rb
118
+ - lib/desiru/assertions.rb
136
119
  - lib/desiru/async_capable.rb
120
+ - lib/desiru/async_status.rb
137
121
  - lib/desiru/cache.rb
138
122
  - lib/desiru/configuration.rb
123
+ - lib/desiru/errors.rb
139
124
  - lib/desiru/field.rb
125
+ - lib/desiru/graphql/batch_loader.rb
140
126
  - lib/desiru/graphql/data_loader.rb
127
+ - lib/desiru/graphql/enum_builder.rb
141
128
  - lib/desiru/graphql/executor.rb
142
129
  - lib/desiru/graphql/schema_generator.rb
130
+ - lib/desiru/graphql/type_builder.rb
131
+ - lib/desiru/graphql/type_cache_warmer.rb
143
132
  - lib/desiru/jobs/async_predict.rb
144
133
  - lib/desiru/jobs/base.rb
145
134
  - lib/desiru/jobs/batch_processor.rb
146
135
  - lib/desiru/jobs/optimizer_job.rb
136
+ - lib/desiru/jobs/retriable.rb
137
+ - lib/desiru/jobs/retry_strategies.rb
138
+ - lib/desiru/jobs/scheduler.rb
139
+ - lib/desiru/jobs/webhook_notifier.rb
140
+ - lib/desiru/models/anthropic.rb
147
141
  - lib/desiru/models/base.rb
148
- - lib/desiru/models/raix_adapter.rb
142
+ - lib/desiru/models/open_ai.rb
143
+ - lib/desiru/models/open_router.rb
149
144
  - lib/desiru/module.rb
150
145
  - lib/desiru/modules/chain_of_thought.rb
146
+ - lib/desiru/modules/majority.rb
147
+ - lib/desiru/modules/multi_chain_comparison.rb
151
148
  - lib/desiru/modules/predict.rb
149
+ - lib/desiru/modules/program_of_thought.rb
150
+ - lib/desiru/modules/react.rb
152
151
  - lib/desiru/modules/retrieve.rb
153
152
  - lib/desiru/optimizers/base.rb
154
153
  - lib/desiru/optimizers/bootstrap_few_shot.rb
154
+ - lib/desiru/optimizers/copro.rb
155
+ - lib/desiru/optimizers/knn_few_shot.rb
156
+ - lib/desiru/persistence.rb
157
+ - lib/desiru/persistence/database.rb
158
+ - lib/desiru/persistence/models.rb
159
+ - lib/desiru/persistence/models/api_request.rb
160
+ - lib/desiru/persistence/models/job_result.rb
161
+ - lib/desiru/persistence/models/module_execution.rb
162
+ - lib/desiru/persistence/models/optimization_result.rb
163
+ - lib/desiru/persistence/models/training_example.rb
164
+ - lib/desiru/persistence/repositories/api_request_repository.rb
165
+ - lib/desiru/persistence/repositories/base_repository.rb
166
+ - lib/desiru/persistence/repositories/job_result_repository.rb
167
+ - lib/desiru/persistence/repositories/module_execution_repository.rb
168
+ - lib/desiru/persistence/repositories/optimization_result_repository.rb
169
+ - lib/desiru/persistence/repositories/training_example_repository.rb
170
+ - lib/desiru/persistence/repository.rb
171
+ - lib/desiru/persistence/setup.rb
155
172
  - lib/desiru/program.rb
156
173
  - lib/desiru/registry.rb
157
174
  - lib/desiru/signature.rb
158
175
  - lib/desiru/version.rb
176
+ - missing-features-analysis.md
159
177
  homepage: https://github.com/obie/desiru
160
178
  licenses:
161
179
  - MIT
@@ -171,7 +189,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
171
189
  requirements:
172
190
  - - ">="
173
191
  - !ruby/object:Gem::Version
174
- version: 3.4.2
192
+ version: 3.3.0
175
193
  required_rubygems_version: !ruby/object:Gem::Requirement
176
194
  requirements:
177
195
  - - ">="
@@ -1,210 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'raix'
4
- require 'faraday'
5
- require 'faraday/retry'
6
- require 'openai'
7
-
8
- module Desiru
9
- module Models
10
- # Adapter for Raix gem integration
11
- # Provides unified interface to OpenAI, Anthropic, and OpenRouter via Raix
12
- # Uses modern Raix patterns with direct OpenAI::Client configuration
13
- class RaixAdapter < Base
14
- def initialize(api_key: nil, provider: :openai, uri_base: nil, **config)
15
- @api_key = api_key || fetch_api_key(provider)
16
- @provider = provider
17
- @uri_base = uri_base || fetch_uri_base(provider)
18
-
19
- super(config)
20
- configure_raix!
21
- end
22
-
23
- def complete(prompt, **options)
24
- opts = build_completion_options(prompt, options)
25
-
26
- response = with_retry do
27
- client.completions.create(**opts)
28
- end
29
-
30
- process_response(response)
31
- end
32
-
33
- def stream_complete(prompt, **options)
34
- opts = build_completion_options(prompt, options).merge(stream: true)
35
-
36
- with_retry do
37
- client.completions.create(**opts) do |chunk|
38
- yield process_stream_chunk(chunk)
39
- end
40
- end
41
- end
42
-
43
- def models
44
- case provider
45
- when :openai
46
- %w[gpt-4-turbo gpt-4 gpt-3.5-turbo gpt-4o gpt-4o-mini]
47
- when :anthropic
48
- %w[claude-3-opus-20240229 claude-3-sonnet-20240229 claude-3-haiku-20240307]
49
- when :openrouter
50
- # OpenRouter supports many models with provider prefixes
51
- %w[anthropic/claude-3-opus openai/gpt-4-turbo google/gemini-pro meta-llama/llama-3-70b]
52
- else
53
- []
54
- end
55
- end
56
-
57
- protected
58
-
59
- def default_config
60
- super.merge(
61
- model: 'gpt-4-turbo-preview',
62
- response_format: nil,
63
- tools: nil,
64
- tool_choice: nil
65
- )
66
- end
67
-
68
- def build_client
69
- # Modern Raix uses direct configuration, not separate client instances
70
- # The client is accessed through Raix after configuration
71
- ::Raix
72
- end
73
-
74
- def configure_raix!
75
- ::Raix.configure do |raix_config|
76
- raix_config.openai_client = build_openai_client
77
- end
78
- end
79
-
80
- def build_openai_client
81
- ::OpenAI::Client.new(
82
- access_token: @api_key,
83
- uri_base: @uri_base
84
- ) do |f|
85
- # Add retry middleware
86
- f.request(:retry, {
87
- max: config[:max_retries] || 3,
88
- interval: 0.05,
89
- interval_randomness: 0.5,
90
- backoff_factor: 2
91
- })
92
-
93
- # Add logging in debug mode
94
- if ENV['DEBUG'] || config[:debug]
95
- f.response(:logger, config[:logger] || Logger.new($stdout), {
96
- headers: false,
97
- bodies: true,
98
- errors: true
99
- }) do |logger|
100
- logger.filter(/(Bearer) (\S+)/, '\1[REDACTED]')
101
- end
102
- end
103
- end
104
- end
105
-
106
- def fetch_api_key(provider)
107
- case provider
108
- when :openai
109
- ENV.fetch('OPENAI_API_KEY', nil)
110
- when :anthropic
111
- ENV.fetch('ANTHROPIC_API_KEY', nil)
112
- when :openrouter
113
- ENV.fetch('OPENROUTER_API_KEY', nil)
114
- else
115
- ENV.fetch("#{provider.to_s.upcase}_API_KEY", nil)
116
- end
117
- end
118
-
119
- def fetch_uri_base(provider)
120
- case provider
121
- when :openai
122
- ENV['OPENAI_API_BASE'] || 'https://api.openai.com/v1'
123
- when :anthropic
124
- ENV['ANTHROPIC_API_BASE'] || 'https://api.anthropic.com/v1'
125
- when :openrouter
126
- ENV['OPENROUTER_API_BASE'] || 'https://openrouter.ai/api/v1'
127
- else
128
- ENV.fetch("#{provider.to_s.upcase}_API_BASE", nil)
129
- end
130
- end
131
-
132
- def validate_config!
133
- raise ConfigurationError, 'API key is required' if @api_key.nil? || @api_key.empty?
134
- raise ConfigurationError, 'Model must be specified' if config[:model].nil?
135
- end
136
-
137
- private
138
-
139
- attr_reader :provider
140
-
141
- def build_completion_options(prompt, options)
142
- messages = build_messages(prompt, options[:demos] || [])
143
-
144
- {
145
- model: options[:model] || config[:model],
146
- messages: messages,
147
- temperature: options[:temperature] || config[:temperature],
148
- max_tokens: options[:max_tokens] || config[:max_tokens],
149
- response_format: options[:response_format] || config[:response_format],
150
- tools: options[:tools] || config[:tools],
151
- tool_choice: options[:tool_choice] || config[:tool_choice]
152
- }.compact
153
- end
154
-
155
- def build_messages(prompt, demos)
156
- messages = []
157
-
158
- # Add system message if provided
159
- messages << { role: 'system', content: prompt[:system] } if prompt[:system]
160
-
161
- # Add demonstrations
162
- demos.each do |demo|
163
- messages << { role: 'user', content: demo[:input] }
164
- messages << { role: 'assistant', content: demo[:output] }
165
- end
166
-
167
- # Add current prompt
168
- messages << { role: 'user', content: prompt[:user] || prompt[:content] || prompt }
169
-
170
- messages
171
- end
172
-
173
- def process_response(response)
174
- content = response.dig('choices', 0, 'message', 'content')
175
- usage = response['usage']
176
-
177
- increment_stats(usage['total_tokens']) if usage
178
-
179
- {
180
- content: content,
181
- raw: response,
182
- model: response['model'],
183
- usage: usage
184
- }
185
- end
186
-
187
- def process_stream_chunk(chunk)
188
- content = chunk.dig('choices', 0, 'delta', 'content')
189
-
190
- {
191
- content: content,
192
- finished: chunk.dig('choices', 0, 'finish_reason').present?
193
- }
194
- end
195
- end
196
-
197
- # Convenience classes for specific providers
198
- class OpenAI < RaixAdapter
199
- def initialize(api_key: nil, **config)
200
- super(api_key: api_key, provider: :openai, **config)
201
- end
202
- end
203
-
204
- class OpenRouter < RaixAdapter
205
- def initialize(api_key: nil, **config)
206
- super(api_key: api_key, provider: :openrouter, **config)
207
- end
208
- end
209
- end
210
- end