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.
- checksums.yaml +4 -4
- data/.env.example +34 -0
- data/.rubocop.yml +7 -4
- data/.ruby-version +1 -0
- data/CLAUDE.md +4 -0
- data/Gemfile +21 -2
- data/Gemfile.lock +87 -12
- data/README.md +295 -2
- data/Rakefile +1 -0
- data/db/migrations/001_create_initial_tables.rb +96 -0
- data/db/migrations/002_create_job_results.rb +39 -0
- data/desiru.db +0 -0
- data/desiru.gemspec +2 -5
- data/docs/background_processing_roadmap.md +87 -0
- data/docs/job_scheduling.md +167 -0
- data/dspy-analysis-swarm.yml +60 -0
- data/dspy-feature-analysis.md +121 -0
- data/examples/README.md +69 -0
- data/examples/api_with_persistence.rb +122 -0
- data/examples/assertions_example.rb +232 -0
- data/examples/async_processing.rb +2 -0
- data/examples/few_shot_learning.rb +1 -2
- data/examples/graphql_api.rb +4 -2
- data/examples/graphql_integration.rb +3 -3
- data/examples/graphql_optimization_summary.md +143 -0
- data/examples/graphql_performance_benchmark.rb +247 -0
- data/examples/persistence_example.rb +102 -0
- data/examples/react_agent.rb +203 -0
- data/examples/rest_api.rb +173 -0
- data/examples/rest_api_advanced.rb +333 -0
- data/examples/scheduled_job_example.rb +116 -0
- data/examples/simple_qa.rb +1 -2
- data/examples/sinatra_api.rb +109 -0
- data/examples/typed_signatures.rb +1 -2
- data/graphql_optimization_summary.md +53 -0
- data/lib/desiru/api/grape_integration.rb +284 -0
- data/lib/desiru/api/persistence_middleware.rb +148 -0
- data/lib/desiru/api/sinatra_integration.rb +217 -0
- data/lib/desiru/api.rb +42 -0
- data/lib/desiru/assertions.rb +74 -0
- data/lib/desiru/async_status.rb +65 -0
- data/lib/desiru/cache.rb +1 -1
- data/lib/desiru/configuration.rb +2 -1
- data/lib/desiru/errors.rb +160 -0
- data/lib/desiru/field.rb +17 -14
- data/lib/desiru/graphql/batch_loader.rb +85 -0
- data/lib/desiru/graphql/data_loader.rb +242 -75
- data/lib/desiru/graphql/enum_builder.rb +75 -0
- data/lib/desiru/graphql/executor.rb +37 -4
- data/lib/desiru/graphql/schema_generator.rb +62 -158
- data/lib/desiru/graphql/type_builder.rb +138 -0
- data/lib/desiru/graphql/type_cache_warmer.rb +91 -0
- data/lib/desiru/jobs/async_predict.rb +1 -1
- data/lib/desiru/jobs/base.rb +67 -0
- data/lib/desiru/jobs/batch_processor.rb +6 -6
- data/lib/desiru/jobs/retriable.rb +119 -0
- data/lib/desiru/jobs/retry_strategies.rb +169 -0
- data/lib/desiru/jobs/scheduler.rb +219 -0
- data/lib/desiru/jobs/webhook_notifier.rb +242 -0
- data/lib/desiru/models/anthropic.rb +164 -0
- data/lib/desiru/models/base.rb +37 -3
- data/lib/desiru/models/open_ai.rb +151 -0
- data/lib/desiru/models/open_router.rb +161 -0
- data/lib/desiru/module.rb +59 -9
- data/lib/desiru/modules/chain_of_thought.rb +3 -3
- data/lib/desiru/modules/majority.rb +51 -0
- data/lib/desiru/modules/multi_chain_comparison.rb +204 -0
- data/lib/desiru/modules/predict.rb +8 -1
- data/lib/desiru/modules/program_of_thought.rb +139 -0
- data/lib/desiru/modules/react.rb +273 -0
- data/lib/desiru/modules/retrieve.rb +4 -2
- data/lib/desiru/optimizers/base.rb +2 -4
- data/lib/desiru/optimizers/bootstrap_few_shot.rb +2 -2
- data/lib/desiru/optimizers/copro.rb +268 -0
- data/lib/desiru/optimizers/knn_few_shot.rb +185 -0
- data/lib/desiru/persistence/database.rb +71 -0
- data/lib/desiru/persistence/models/api_request.rb +38 -0
- data/lib/desiru/persistence/models/job_result.rb +138 -0
- data/lib/desiru/persistence/models/module_execution.rb +37 -0
- data/lib/desiru/persistence/models/optimization_result.rb +28 -0
- data/lib/desiru/persistence/models/training_example.rb +25 -0
- data/lib/desiru/persistence/models.rb +11 -0
- data/lib/desiru/persistence/repositories/api_request_repository.rb +98 -0
- data/lib/desiru/persistence/repositories/base_repository.rb +77 -0
- data/lib/desiru/persistence/repositories/job_result_repository.rb +116 -0
- data/lib/desiru/persistence/repositories/module_execution_repository.rb +85 -0
- data/lib/desiru/persistence/repositories/optimization_result_repository.rb +67 -0
- data/lib/desiru/persistence/repositories/training_example_repository.rb +102 -0
- data/lib/desiru/persistence/repository.rb +29 -0
- data/lib/desiru/persistence/setup.rb +77 -0
- data/lib/desiru/persistence.rb +49 -0
- data/lib/desiru/registry.rb +3 -5
- data/lib/desiru/signature.rb +91 -24
- data/lib/desiru/version.rb +1 -1
- data/lib/desiru.rb +23 -8
- data/missing-features-analysis.md +192 -0
- metadata +63 -45
- data/lib/desiru/models/raix_adapter.rb +0 -210
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d4dc11edfa1084914d29616c396cb2bb163c4ed74bf2e13a07f23edd4b0fefc
|
4
|
+
data.tar.gz: 86a62bd76bf86d7aedff994dcf93e110a64a1d135cad0249c89658f22e93bc78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bee932d2d4adb82e40e80feee6a07156f1f8c6f753660e11c492b2ff0ccc6fa8711587bdc05c2961b9f80b32b84394c226b8408fb7467d0d5bd5d2063254c6c0
|
7
|
+
data.tar.gz: f59cb824b72814078b1646c997f001b6aae47fa0ffc2ad7c06070b2a2a9cb0e3fe6068574624c9f508ac2f87326c9d76c2040f28ada3fc91b5e938113ac81186
|
data/.env.example
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Desiru Configuration Example
|
2
|
+
# Copy this file to .env and add your actual API keys
|
3
|
+
|
4
|
+
# Choose one of the following LLM providers:
|
5
|
+
|
6
|
+
# Anthropic Claude
|
7
|
+
# Get your API key from: https://console.anthropic.com/
|
8
|
+
# ANTHROPIC_API_KEY=sk-ant-api03-...
|
9
|
+
|
10
|
+
# OpenAI
|
11
|
+
# Get your API key from: https://platform.openai.com/api-keys
|
12
|
+
# OPENAI_API_KEY=sk-...
|
13
|
+
|
14
|
+
# Optional: Redis configuration for async/caching features
|
15
|
+
# Default: redis://localhost:6379/0
|
16
|
+
# REDIS_URL=redis://localhost:6379/0
|
17
|
+
|
18
|
+
# Optional: Database URL for persistence features
|
19
|
+
# Default: sqlite://db/desiru_development.db
|
20
|
+
# DATABASE_URL=sqlite://db/desiru_development.db
|
21
|
+
|
22
|
+
# Optional: OpenRouter API
|
23
|
+
# Get your API key from: https://openrouter.ai/keys
|
24
|
+
# OPENROUTER_API_KEY=sk-or-v1-...
|
25
|
+
|
26
|
+
# Optional: Default model selection
|
27
|
+
# For Anthropic: claude-3-haiku-20240307, claude-3-sonnet-20240229, claude-3-opus-20240229
|
28
|
+
# For OpenAI: gpt-4o-mini, gpt-4o, gpt-4-turbo
|
29
|
+
# For OpenRouter: anthropic/claude-3-haiku, openai/gpt-4o-mini, google/gemini-pro
|
30
|
+
# DESIRU_DEFAULT_MODEL=claude-3-haiku-20240307
|
31
|
+
|
32
|
+
# Optional: Logging level
|
33
|
+
# Options: debug, info, warn, error, fatal
|
34
|
+
# DESIRU_LOG_LEVEL=info
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
-
TargetRubyVersion: 3.
|
2
|
+
TargetRubyVersion: 3.3
|
3
3
|
NewCops: enable
|
4
4
|
SuggestExtensions: false
|
5
5
|
Exclude:
|
@@ -8,9 +8,9 @@ AllCops:
|
|
8
8
|
- 'bin/**/*'
|
9
9
|
- 'spec/fixtures/**/*'
|
10
10
|
|
11
|
-
|
11
|
+
plugins:
|
12
12
|
- rubocop-rake
|
13
|
-
|
13
|
+
|
14
14
|
|
15
15
|
# Relaxed metrics for DSL-heavy library code
|
16
16
|
Metrics/MethodLength:
|
@@ -52,4 +52,7 @@ Layout/LineLength:
|
|
52
52
|
- 'spec/**/*'
|
53
53
|
|
54
54
|
Gemspec/RequireMFA:
|
55
|
-
Enabled: false
|
55
|
+
Enabled: false
|
56
|
+
|
57
|
+
Metrics:
|
58
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.4.2
|
data/CLAUDE.md
CHANGED
@@ -17,6 +17,10 @@ This project is in its initial setup phase. When implementing features:
|
|
17
17
|
- Follow Ruby community conventions for project organization
|
18
18
|
- Set up bundler with a Gemfile when adding dependencies
|
19
19
|
|
20
|
+
## Testing Framework
|
21
|
+
|
22
|
+
**IMPORTANT**: This project uses RSpec exclusively for testing. NEVER use Minitest or any other testing framework. Do not add minitest gems, create test/ directories, or write any Minitest-style tests. All tests must be written in RSpec format and placed in the spec/ directory.
|
23
|
+
|
20
24
|
## Workflow Guidance
|
21
25
|
|
22
26
|
- For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
data/Gemfile
CHANGED
@@ -2,35 +2,54 @@
|
|
2
2
|
|
3
3
|
source 'https://rubygems.org'
|
4
4
|
|
5
|
-
ruby '
|
5
|
+
ruby '>= 3.2.0'
|
6
6
|
|
7
7
|
# Specify your gem's dependencies in desiru.gemspec
|
8
8
|
gemspec
|
9
9
|
|
10
10
|
group :development, :test do
|
11
|
+
gem 'dotenv', '~> 3.0'
|
12
|
+
gem 'mock_redis', '~> 0.40'
|
11
13
|
gem 'pry', '~> 0.14'
|
12
14
|
gem 'pry-byebug', '~> 3.10'
|
15
|
+
gem 'rack-test', '~> 2.0'
|
13
16
|
gem 'rake', '~> 13.0'
|
17
|
+
gem 'rdoc'
|
14
18
|
gem 'rspec', '~> 3.0'
|
15
19
|
gem 'rubocop', '~> 1.21'
|
16
20
|
gem 'rubocop-rake', '~> 0.6'
|
17
21
|
gem 'rubocop-rspec', '~> 2.0'
|
18
22
|
gem 'simplecov', '~> 0.22', require: false
|
23
|
+
gem 'webmock', '~> 3.0'
|
19
24
|
gem 'yard', '~> 0.9'
|
20
25
|
end
|
21
26
|
|
22
27
|
# LLM interaction dependencies
|
28
|
+
gem 'anthropic', '~> 0.3'
|
23
29
|
gem 'faraday', '~> 2.0'
|
24
30
|
gem 'faraday-retry', '~> 2.0'
|
25
|
-
gem '
|
31
|
+
gem 'open_router', '~> 0.3'
|
32
|
+
gem 'ruby-openai', '~> 7.0'
|
26
33
|
|
27
34
|
# GraphQL support
|
28
35
|
gem 'graphql', '~> 2.0'
|
29
36
|
|
37
|
+
# REST API support
|
38
|
+
gem 'grape', '~> 2.0'
|
39
|
+
gem 'rack-cors', '~> 2.0'
|
40
|
+
gem 'sinatra', '~> 3.0'
|
41
|
+
gem 'sinatra-contrib', '~> 3.0'
|
42
|
+
|
43
|
+
# Database support
|
44
|
+
gem 'sequel', '~> 5.0'
|
45
|
+
gem 'sqlite3', '~> 1.6' # For development/testing
|
46
|
+
|
30
47
|
# Optional dependencies for enhanced functionality
|
31
48
|
group :optional do
|
32
49
|
gem 'async', '~> 2.0' # For concurrent operations
|
33
50
|
gem 'dry-struct', '~> 1.0' # For type safety
|
34
51
|
gem 'dry-validation', '~> 1.0' # For validation
|
52
|
+
gem 'jwt', '~> 2.0' # For API authentication
|
53
|
+
gem 'rack-throttle', '~> 0.7' # For rate limiting
|
35
54
|
gem 'redis', '~> 5.0' # For caching
|
36
55
|
end
|
data/Gemfile.lock
CHANGED
@@ -23,6 +23,12 @@ GEM
|
|
23
23
|
securerandom (>= 0.3)
|
24
24
|
tzinfo (~> 2.0, >= 2.0.5)
|
25
25
|
uri (>= 0.13.1)
|
26
|
+
addressable (2.8.7)
|
27
|
+
public_suffix (>= 2.0.2, < 7.0)
|
28
|
+
anthropic (0.4.1)
|
29
|
+
event_stream_parser (>= 0.3.0, < 2.0.0)
|
30
|
+
faraday (>= 1)
|
31
|
+
faraday-multipart (>= 1)
|
26
32
|
ast (2.4.3)
|
27
33
|
async (2.25.0)
|
28
34
|
console (~> 1.29)
|
@@ -41,6 +47,10 @@ GEM
|
|
41
47
|
fiber-annotation
|
42
48
|
fiber-local (~> 1.1)
|
43
49
|
json
|
50
|
+
crack (1.0.0)
|
51
|
+
bigdecimal
|
52
|
+
rexml
|
53
|
+
date (3.4.1)
|
44
54
|
diff-lcs (1.6.2)
|
45
55
|
docile (1.4.1)
|
46
56
|
dotenv (3.1.8)
|
@@ -85,38 +95,56 @@ GEM
|
|
85
95
|
dry-initializer (~> 3.2)
|
86
96
|
dry-schema (~> 1.14)
|
87
97
|
zeitwerk (~> 2.6)
|
98
|
+
erb (5.0.1)
|
88
99
|
event_stream_parser (1.0.0)
|
89
100
|
faraday (2.13.1)
|
90
101
|
faraday-net_http (>= 2.0, < 3.5)
|
91
102
|
json
|
92
103
|
logger
|
93
|
-
faraday-multipart (1.1.
|
104
|
+
faraday-multipart (1.1.1)
|
94
105
|
multipart-post (~> 2.0)
|
95
106
|
faraday-net_http (3.4.1)
|
96
107
|
net-http (>= 0.5.0)
|
97
|
-
faraday-retry (2.3.
|
108
|
+
faraday-retry (2.3.2)
|
98
109
|
faraday (~> 2.0)
|
99
110
|
fiber-annotation (0.2.0)
|
100
111
|
fiber-local (1.1.0)
|
101
112
|
fiber-storage
|
102
113
|
fiber-storage (1.0.1)
|
103
114
|
forwardable (1.3.3)
|
115
|
+
grape (2.4.0)
|
116
|
+
activesupport (>= 6.1)
|
117
|
+
dry-types (>= 1.1)
|
118
|
+
mustermann-grape (~> 1.1.0)
|
119
|
+
rack (>= 2)
|
120
|
+
zeitwerk
|
104
121
|
graphql (2.5.9)
|
105
122
|
base64
|
106
123
|
fiber-storage
|
107
124
|
logger
|
125
|
+
hashdiff (1.2.0)
|
108
126
|
i18n (1.14.7)
|
109
127
|
concurrent-ruby (~> 1.0)
|
110
128
|
ice_nine (0.11.2)
|
111
129
|
io-event (1.11.0)
|
112
130
|
json (2.12.2)
|
131
|
+
jwt (2.10.1)
|
132
|
+
base64
|
113
133
|
language_server-protocol (3.17.0.5)
|
114
134
|
lint_roller (1.1.0)
|
115
135
|
logger (1.7.0)
|
116
136
|
method_source (1.1.0)
|
117
137
|
metrics (0.12.2)
|
138
|
+
mini_portile2 (2.8.9)
|
118
139
|
minitest (5.25.5)
|
140
|
+
mock_redis (0.50.0)
|
141
|
+
redis (~> 5)
|
142
|
+
multi_json (1.15.0)
|
119
143
|
multipart-post (2.4.1)
|
144
|
+
mustermann (3.0.3)
|
145
|
+
ruby2_keywords (~> 0.0.1)
|
146
|
+
mustermann-grape (1.1.0)
|
147
|
+
mustermann (>= 1.0.0)
|
120
148
|
net-http (0.6.0)
|
121
149
|
uri
|
122
150
|
open_router (0.3.3)
|
@@ -124,7 +152,6 @@ GEM
|
|
124
152
|
dotenv (>= 2)
|
125
153
|
faraday (>= 1)
|
126
154
|
faraday-multipart (>= 1)
|
127
|
-
ostruct (0.6.1)
|
128
155
|
parallel (1.27.0)
|
129
156
|
parser (3.3.8.0)
|
130
157
|
ast (~> 2.4.1)
|
@@ -136,21 +163,33 @@ GEM
|
|
136
163
|
pry-byebug (3.11.0)
|
137
164
|
byebug (~> 12.0)
|
138
165
|
pry (>= 0.13, < 0.16)
|
166
|
+
psych (5.2.6)
|
167
|
+
date
|
168
|
+
stringio
|
169
|
+
public_suffix (6.0.2)
|
139
170
|
racc (1.8.1)
|
140
|
-
rack (
|
171
|
+
rack (2.2.17)
|
172
|
+
rack-cors (2.0.2)
|
173
|
+
rack (>= 2.0.0)
|
174
|
+
rack-protection (3.2.0)
|
175
|
+
base64 (>= 0.1.0)
|
176
|
+
rack (~> 2.2, >= 2.2.4)
|
177
|
+
rack-test (2.2.0)
|
178
|
+
rack (>= 1.3)
|
179
|
+
rack-throttle (0.7.1)
|
180
|
+
bundler (>= 1.0.0)
|
181
|
+
rack (>= 1.0.0)
|
141
182
|
rainbow (3.1.1)
|
142
|
-
raix (0.9.2)
|
143
|
-
activesupport (>= 6.0)
|
144
|
-
faraday-retry (~> 2.0)
|
145
|
-
open_router (~> 0.2)
|
146
|
-
ostruct
|
147
|
-
ruby-openai (~> 7)
|
148
183
|
rake (13.3.0)
|
184
|
+
rdoc (6.14.1)
|
185
|
+
erb
|
186
|
+
psych (>= 4.0.0)
|
149
187
|
redis (5.4.0)
|
150
188
|
redis-client (>= 0.22.0)
|
151
189
|
redis-client (0.25.0)
|
152
190
|
connection_pool
|
153
191
|
regexp_parser (2.10.0)
|
192
|
+
rexml (3.4.1)
|
154
193
|
rspec (3.13.1)
|
155
194
|
rspec-core (~> 3.13.0)
|
156
195
|
rspec-expectations (~> 3.13.0)
|
@@ -199,7 +238,10 @@ GEM
|
|
199
238
|
faraday (>= 1)
|
200
239
|
faraday-multipart (>= 1)
|
201
240
|
ruby-progressbar (1.13.0)
|
241
|
+
ruby2_keywords (0.0.5)
|
202
242
|
securerandom (0.4.1)
|
243
|
+
sequel (5.93.0)
|
244
|
+
bigdecimal
|
203
245
|
sidekiq (7.3.9)
|
204
246
|
base64
|
205
247
|
connection_pool (>= 2.3.0)
|
@@ -212,7 +254,22 @@ GEM
|
|
212
254
|
simplecov_json_formatter (~> 0.1)
|
213
255
|
simplecov-html (0.13.1)
|
214
256
|
simplecov_json_formatter (0.1.4)
|
257
|
+
sinatra (3.2.0)
|
258
|
+
mustermann (~> 3.0)
|
259
|
+
rack (~> 2.2, >= 2.2.4)
|
260
|
+
rack-protection (= 3.2.0)
|
261
|
+
tilt (~> 2.0)
|
262
|
+
sinatra-contrib (3.2.0)
|
263
|
+
multi_json (>= 0.0.2)
|
264
|
+
mustermann (~> 3.0)
|
265
|
+
rack-protection (= 3.2.0)
|
266
|
+
sinatra (= 3.2.0)
|
267
|
+
tilt (~> 2.0)
|
215
268
|
singleton (0.3.0)
|
269
|
+
sqlite3 (1.7.3)
|
270
|
+
mini_portile2 (~> 2.8.0)
|
271
|
+
stringio (3.1.7)
|
272
|
+
tilt (2.6.0)
|
216
273
|
traces (0.15.2)
|
217
274
|
tzinfo (2.0.6)
|
218
275
|
concurrent-ruby (~> 1.0)
|
@@ -220,6 +277,10 @@ GEM
|
|
220
277
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
221
278
|
unicode-emoji (4.0.4)
|
222
279
|
uri (1.0.3)
|
280
|
+
webmock (3.25.1)
|
281
|
+
addressable (>= 2.8.0)
|
282
|
+
crack (>= 0.3.2)
|
283
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
223
284
|
yard (0.9.37)
|
224
285
|
zeitwerk (2.7.3)
|
225
286
|
|
@@ -228,24 +289,38 @@ PLATFORMS
|
|
228
289
|
ruby
|
229
290
|
|
230
291
|
DEPENDENCIES
|
292
|
+
anthropic (~> 0.3)
|
231
293
|
async (~> 2.0)
|
232
|
-
bundler (~> 2.0)
|
233
294
|
desiru!
|
295
|
+
dotenv (~> 3.0)
|
234
296
|
dry-struct (~> 1.0)
|
235
297
|
dry-validation (~> 1.0)
|
236
298
|
faraday (~> 2.0)
|
237
299
|
faraday-retry (~> 2.0)
|
300
|
+
grape (~> 2.0)
|
238
301
|
graphql (~> 2.0)
|
302
|
+
jwt (~> 2.0)
|
303
|
+
mock_redis (~> 0.40)
|
304
|
+
open_router (~> 0.3)
|
239
305
|
pry (~> 0.14)
|
240
306
|
pry-byebug (~> 3.10)
|
241
|
-
|
307
|
+
rack-cors (~> 2.0)
|
308
|
+
rack-test (~> 2.0)
|
309
|
+
rack-throttle (~> 0.7)
|
242
310
|
rake (~> 13.0)
|
311
|
+
rdoc
|
243
312
|
redis (~> 5.0)
|
244
313
|
rspec (~> 3.0)
|
245
314
|
rubocop (~> 1.21)
|
246
315
|
rubocop-rake (~> 0.6)
|
247
316
|
rubocop-rspec (~> 2.0)
|
317
|
+
ruby-openai (~> 7.0)
|
318
|
+
sequel (~> 5.0)
|
248
319
|
simplecov (~> 0.22)
|
320
|
+
sinatra (~> 3.0)
|
321
|
+
sinatra-contrib (~> 3.0)
|
322
|
+
sqlite3 (~> 1.6)
|
323
|
+
webmock (~> 3.0)
|
249
324
|
yard (~> 0.9)
|
250
325
|
|
251
326
|
RUBY VERSION
|
data/README.md
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
A Ruby implementation of [DSPy](https://dspy.ai/), the framework for programming—not prompting—language models. Build sophisticated AI systems with modular, composable code instead of brittle prompt strings.
|
4
4
|
|
5
|
+
Note: This project is in its earliest stages of development and experimental. Expect many bugs and breaking changes.
|
6
|
+
|
7
|
+
|
5
8
|
## Overview
|
6
9
|
|
7
10
|
Desiru brings the power of DSPy to the Ruby ecosystem, enabling developers to:
|
@@ -10,7 +13,7 @@ Desiru brings the power of DSPy to the Ruby ecosystem, enabling developers to:
|
|
10
13
|
- Build portable AI systems that work across different language models
|
11
14
|
- Create maintainable, testable AI applications
|
12
15
|
|
13
|
-
Desiru
|
16
|
+
Desiru provides direct integrations with multiple language model providers including OpenAI, Anthropic, and OpenRouter, with features like streaming, function calling, and prompt caching.
|
14
17
|
|
15
18
|
## Installation
|
16
19
|
|
@@ -187,6 +190,128 @@ model = Desiru::Models::Ollama.new(
|
|
187
190
|
cot = Desiru::ChainOfThought.new("question -> answer", model: model)
|
188
191
|
```
|
189
192
|
|
193
|
+
### Assertions and Validation
|
194
|
+
|
195
|
+
Desiru provides an assertions system for validating module outputs and enforcing constraints:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
# Configure assertions
|
199
|
+
Desiru::Assertions.configure do |config|
|
200
|
+
config.max_assertion_retries = 3 # Retry failed assertions up to 3 times
|
201
|
+
config.assertion_retry_delay = 0.5 # Wait 0.5s between retries
|
202
|
+
end
|
203
|
+
|
204
|
+
# Use assertions in your modules
|
205
|
+
class FactChecker < Desiru::Module
|
206
|
+
def forward(statement:)
|
207
|
+
result = @model.complete(prompt: "Verify: #{statement}")
|
208
|
+
confidence = extract_confidence(result)
|
209
|
+
|
210
|
+
# Hard assertion - will retry if confidence is too low
|
211
|
+
Desiru.assert(confidence > 0.8, "Confidence too low: #{confidence}")
|
212
|
+
|
213
|
+
{ statement: statement, confidence: confidence, verified: true }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Use suggestions for soft constraints
|
218
|
+
class CodeReviewer < Desiru::Module
|
219
|
+
def forward(code:)
|
220
|
+
review = analyze_code(code)
|
221
|
+
|
222
|
+
# Soft suggestion - logs warning but continues
|
223
|
+
Desiru.suggest(review[:test_coverage] > 0.7, "Test coverage below 70%")
|
224
|
+
Desiru.suggest(review[:complexity] < 10, "Code complexity too high")
|
225
|
+
|
226
|
+
review
|
227
|
+
end
|
228
|
+
end
|
229
|
+
```
|
230
|
+
|
231
|
+
Key features:
|
232
|
+
- **Assertions** (`Desiru.assert`) - Enforce hard constraints with automatic retries
|
233
|
+
- **Suggestions** (`Desiru.suggest`) - Log warnings for soft constraints
|
234
|
+
- **Configurable retries** - Control retry behavior for failed assertions
|
235
|
+
- **Module integration** - Assertions are fully integrated with the module retry system
|
236
|
+
|
237
|
+
See `examples/assertions_example.rb` for more detailed examples.
|
238
|
+
|
239
|
+
### REST API with Grape
|
240
|
+
|
241
|
+
Desiru provides Grape integration for building REST APIs:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
require 'desiru/api'
|
245
|
+
|
246
|
+
# Create API with your modules
|
247
|
+
api = Desiru::API::GrapeIntegration.new
|
248
|
+
api.register_module('/qa', qa_module, description: 'Question answering')
|
249
|
+
api.register_module('/summarize', summarizer, description: 'Text summarization')
|
250
|
+
|
251
|
+
# Mount as Rack app
|
252
|
+
run api.to_rack_app
|
253
|
+
```
|
254
|
+
|
255
|
+
Features:
|
256
|
+
- **Automatic endpoint generation** from Desiru signatures
|
257
|
+
- **Parameter validation** based on signature types
|
258
|
+
- **CORS support** built-in
|
259
|
+
- **Async support** (when enabled in modules)
|
260
|
+
- **Streaming endpoints** for real-time responses
|
261
|
+
|
262
|
+
Example endpoints:
|
263
|
+
```bash
|
264
|
+
# Synchronous request
|
265
|
+
curl -X POST http://localhost:9292/api/v1/qa \
|
266
|
+
-H "Content-Type: application/json" \
|
267
|
+
-d '{"question": "What is Ruby?"}'
|
268
|
+
|
269
|
+
# Async request
|
270
|
+
curl -X POST http://localhost:9292/api/v1/summarize \
|
271
|
+
-H "Content-Type: application/json" \
|
272
|
+
-d '{"text": "Long text...", "max_words": 100, "async": true}'
|
273
|
+
|
274
|
+
# Check job status
|
275
|
+
curl http://localhost:9292/api/v1/jobs/JOB_ID
|
276
|
+
|
277
|
+
# Check API health
|
278
|
+
curl http://localhost:9292/api/v1/health
|
279
|
+
```
|
280
|
+
|
281
|
+
See `examples/rest_api.rb` and `examples/rest_api_advanced.rb` for complete examples.
|
282
|
+
|
283
|
+
### REST API with Sinatra
|
284
|
+
|
285
|
+
Desiru also supports Sinatra for lightweight REST APIs:
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
require 'desiru/api'
|
289
|
+
|
290
|
+
# Create API with Sinatra (lightweight alternative to Grape)
|
291
|
+
api = Desiru::API.sinatra do
|
292
|
+
register_module '/qa', qa_module, description: 'Question answering'
|
293
|
+
register_module '/summarize', summarizer, description: 'Text summarization'
|
294
|
+
end
|
295
|
+
|
296
|
+
# Or explicitly specify the framework
|
297
|
+
api = Desiru::API.create(framework: :sinatra) do
|
298
|
+
register_module '/process', processor
|
299
|
+
end
|
300
|
+
|
301
|
+
# Mount as Rack app
|
302
|
+
run api.to_rack_app
|
303
|
+
```
|
304
|
+
|
305
|
+
Features:
|
306
|
+
- **Lightweight** - Minimal dependencies with Sinatra
|
307
|
+
- **Same interface** as Grape integration
|
308
|
+
- **Full compatibility** with all Desiru module features
|
309
|
+
- **CORS support** built-in
|
310
|
+
- **Async support** for background processing
|
311
|
+
- **Streaming endpoints** for real-time responses
|
312
|
+
|
313
|
+
See `examples/sinatra_api.rb` for a complete example.
|
314
|
+
|
190
315
|
### Background Processing
|
191
316
|
|
192
317
|
Desiru includes built-in support for asynchronous processing using Sidekiq:
|
@@ -201,8 +326,10 @@ end
|
|
201
326
|
module = Desiru::Predict.new("question -> answer")
|
202
327
|
result = module.call_async(question: "What is 2+2?")
|
203
328
|
|
204
|
-
# Check status
|
329
|
+
# Check status and progress
|
205
330
|
result.ready? # => false (still processing)
|
331
|
+
result.status # => "running", "completed", "failed", etc.
|
332
|
+
result.progress # => 0-100 (percentage complete)
|
206
333
|
result.success? # => true/false (when ready)
|
207
334
|
|
208
335
|
# Wait for result
|
@@ -267,6 +394,172 @@ This approach makes Desiru particularly well-suited for:
|
|
267
394
|
- Systems requiring job persistence and reliability
|
268
395
|
- Deployments that need to scale horizontally
|
269
396
|
|
397
|
+
### Database Persistence with Sequel
|
398
|
+
|
399
|
+
Desiru includes a comprehensive persistence layer using Sequel for tracking:
|
400
|
+
- Module execution history and performance metrics
|
401
|
+
- API request/response data for analytics
|
402
|
+
- Training examples and optimization results
|
403
|
+
- Model performance over time
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
# Configure persistence
|
407
|
+
require 'desiru/persistence'
|
408
|
+
|
409
|
+
Desiru::Persistence.database_url = 'postgres://localhost/desiru'
|
410
|
+
Desiru::Persistence.connect!
|
411
|
+
Desiru::Persistence.migrate!
|
412
|
+
|
413
|
+
# Track module executions
|
414
|
+
execution = Desiru::Persistence[:module_executions].create_for_module(
|
415
|
+
'TextSummarizer',
|
416
|
+
{ text: 'Long article...' }
|
417
|
+
)
|
418
|
+
|
419
|
+
# Complete with results
|
420
|
+
Desiru::Persistence[:module_executions].complete(
|
421
|
+
execution.id,
|
422
|
+
{ summary: 'Short summary' },
|
423
|
+
{ model: 'gpt-3.5-turbo', tokens: 150 }
|
424
|
+
)
|
425
|
+
|
426
|
+
# Query performance metrics
|
427
|
+
repo = Desiru::Persistence[:module_executions]
|
428
|
+
puts "Success rate: #{repo.success_rate('TextSummarizer')}%"
|
429
|
+
puts "Average duration: #{repo.average_duration('TextSummarizer')}s"
|
430
|
+
|
431
|
+
# Store training examples
|
432
|
+
examples = [
|
433
|
+
{ inputs: { text: 'Example 1' }, outputs: { summary: 'Summary 1' } },
|
434
|
+
{ inputs: { text: 'Example 2' }, outputs: { summary: 'Summary 2' } }
|
435
|
+
]
|
436
|
+
|
437
|
+
Desiru::Persistence[:training_examples].bulk_create('TextSummarizer', examples)
|
438
|
+
|
439
|
+
# Export for training
|
440
|
+
data = Desiru::Persistence[:training_examples].export_for_training(
|
441
|
+
'TextSummarizer',
|
442
|
+
format: :dspy
|
443
|
+
)
|
444
|
+
```
|
445
|
+
|
446
|
+
#### API Request Tracking
|
447
|
+
|
448
|
+
Automatically track all API requests with the persistence middleware:
|
449
|
+
|
450
|
+
```ruby
|
451
|
+
# Add persistence to your API
|
452
|
+
api = Desiru::API.create do
|
453
|
+
register_module '/summarize', summarizer
|
454
|
+
end
|
455
|
+
|
456
|
+
# Enable automatic request tracking
|
457
|
+
app = api.with_persistence(enabled: true)
|
458
|
+
|
459
|
+
# Query API metrics
|
460
|
+
requests = Desiru::Persistence[:api_requests]
|
461
|
+
puts "Requests per minute: #{requests.requests_per_minute}"
|
462
|
+
puts "Average response time: #{requests.average_response_time}s"
|
463
|
+
puts "Top endpoints: #{requests.top_paths(5)}"
|
464
|
+
```
|
465
|
+
|
466
|
+
Features:
|
467
|
+
- **Automatic tracking** of all API requests and module executions
|
468
|
+
- **Performance analytics** including success rates and response times
|
469
|
+
- **Training data management** with dataset splitting and export
|
470
|
+
- **Optimization tracking** to measure improvements over time
|
471
|
+
- **Multiple database support** via Sequel (PostgreSQL, MySQL, SQLite)
|
472
|
+
|
473
|
+
### ReAct Module (Tool-Using Agents)
|
474
|
+
|
475
|
+
The ReAct module enables building AI agents that can reason about tasks and use tools to gather information:
|
476
|
+
|
477
|
+
```ruby
|
478
|
+
# Define tools for your agent
|
479
|
+
class WeatherTool
|
480
|
+
def self.name
|
481
|
+
"get_weather"
|
482
|
+
end
|
483
|
+
|
484
|
+
def self.description
|
485
|
+
"Get current weather for a city. Args: city (string)"
|
486
|
+
end
|
487
|
+
|
488
|
+
def self.call(city:)
|
489
|
+
# Your weather API integration
|
490
|
+
"Current weather in #{city}: sunny, 72°F"
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
# Create a ReAct agent with tools
|
495
|
+
tools = [WeatherTool, CalculatorTool]
|
496
|
+
agent = Desiru::Modules::ReAct.new(
|
497
|
+
'question: string -> answer: string',
|
498
|
+
tools: tools,
|
499
|
+
max_iterations: 5
|
500
|
+
)
|
501
|
+
|
502
|
+
# The agent will reason and use tools to answer
|
503
|
+
result = agent.call(
|
504
|
+
question: "What's the weather in Tokyo and is 72°F warm in Celsius?"
|
505
|
+
)
|
506
|
+
# The agent will:
|
507
|
+
# 1. Call get_weather tool for Tokyo
|
508
|
+
# 2. Use calculator to convert 72°F to Celsius
|
509
|
+
# 3. Synthesize the final answer
|
510
|
+
```
|
511
|
+
|
512
|
+
Key features:
|
513
|
+
- **Flexible tool format**: Pass tools as classes, hashes, or callables
|
514
|
+
- **Automatic reasoning**: The agent decides which tools to use and when
|
515
|
+
- **Trajectory management**: Automatically handles long conversations
|
516
|
+
- **Error handling**: Gracefully handles tool execution failures
|
517
|
+
- **Iteration limits**: Prevents infinite loops
|
518
|
+
|
519
|
+
### GraphQL Integration
|
520
|
+
|
521
|
+
Desiru provides GraphQL integration with automatic schema generation and efficient batch loading:
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
require 'desiru/graphql'
|
525
|
+
|
526
|
+
# Register your Desiru modules
|
527
|
+
generator = Desiru::GraphQL::SchemaGenerator.new
|
528
|
+
generator.register_signature('questionAnswer', qa_module)
|
529
|
+
generator.register_signature('summarize', summarizer_module)
|
530
|
+
|
531
|
+
# Generate GraphQL schema
|
532
|
+
schema = generator.generate_schema
|
533
|
+
|
534
|
+
# Use with your GraphQL server
|
535
|
+
result = schema.execute(
|
536
|
+
query,
|
537
|
+
context: { current_user: user },
|
538
|
+
variables: variables
|
539
|
+
)
|
540
|
+
```
|
541
|
+
|
542
|
+
Features include:
|
543
|
+
- **Automatic schema generation** from Desiru signatures
|
544
|
+
- **DataLoader pattern** for N+1 query prevention
|
545
|
+
- **Batch execution** for multiple queries
|
546
|
+
- **Type mapping** including support for Literal types as GraphQL enums
|
547
|
+
- **Thread-safe** promise-based lazy loading
|
548
|
+
|
549
|
+
#### GraphQL Batch Loading Example
|
550
|
+
|
551
|
+
```ruby
|
552
|
+
# The executor automatically batches multiple field requests
|
553
|
+
executor = Desiru::GraphQL::Executor.new(schema)
|
554
|
+
|
555
|
+
# Execute multiple queries efficiently in a single batch
|
556
|
+
results = executor.execute_batch([
|
557
|
+
{ query: query1, variables: vars1 },
|
558
|
+
{ query: query2, variables: vars2 }
|
559
|
+
])
|
560
|
+
```
|
561
|
+
|
562
|
+
|
270
563
|
## Examples
|
271
564
|
|
272
565
|
### Retrieval-Augmented Generation (RAG)
|