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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 483d9107e403dc8c29d5b3621ca65876eaf4b4fb19fadd5b59bc39a12b005e5f
4
- data.tar.gz: 58b101dc3f69a6c215a6edda23b4895eaa92007dcf2fbe3710705349c8e48f78
3
+ metadata.gz: 6d4dc11edfa1084914d29616c396cb2bb163c4ed74bf2e13a07f23edd4b0fefc
4
+ data.tar.gz: 86a62bd76bf86d7aedff994dcf93e110a64a1d135cad0249c89658f22e93bc78
5
5
  SHA512:
6
- metadata.gz: d1c90f98e10e42dc1bd59631bb5498c35368a46c4db01bb62fafadeb8cb36f4977d2e58ef1af8b23242428047fc10ea1b9edb3cfe2f139173c0405e22040ee74
7
- data.tar.gz: b2060d56c756f30218ab977a0c45d59294d231c5d7b179cadb19483a2af144537e45b40d8f57821f045f23b0e5072ec2086e69bb8e2e04a03f13210d0c6e6494
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.4
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
- require:
11
+ plugins:
12
12
  - rubocop-rake
13
- - rubocop-rspec
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 '~> 3.4.2'
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 'raix', '~> 0.4'
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.0)
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.1)
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 (3.1.16)
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
- raix (~> 0.4)
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 leverages [Raix](https://github.com/OlympiaAI/raix) under the hood as its primary chat completion interface, providing seamless support for OpenAI and OpenRouter APIs with features like streaming, function calling, and prompt caching.
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)
data/Rakefile CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
5
 
6
+ # RSpec task
6
7
  RSpec::Core::RakeTask.new(:spec)
7
8
 
8
9
  require 'rubocop/rake_task'