ruby_llm 2.0.0 → 2.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/README.md +67 -8
- data/lib/generators/ruby_llm/generator_helpers.rb +68 -6
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +1 -0
- data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +36 -3
- data/lib/generators/ruby_llm/provider/scaffold.rb +32 -22
- data/lib/generators/ruby_llm/provider/templates/gem/ci.yml.erb +1 -5
- data/lib/generators/ruby_llm/provider/templates/gem/gemfile.erb +1 -1
- data/lib/generators/ruby_llm/provider/templates/gem/gemspec.erb +1 -1
- data/lib/generators/ruby_llm/provider/templates/gem/rubocop.yml.erb +1 -1
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_ruby_llm_to_2_1.rb.tt +78 -0
- data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +6 -163
- data/lib/ruby_llm/accounting/usage.rb +86 -49
- data/lib/ruby_llm/active_record/acts_as.rb +3 -0
- data/lib/ruby_llm/active_record/attachment_helpers.rb +50 -25
- data/lib/ruby_llm/active_record/batch.rb +1 -1
- data/lib/ruby_llm/active_record/chat_methods.rb +143 -43
- data/lib/ruby_llm/active_record/mcp_credential.rb +41 -0
- data/lib/ruby_llm/active_record/message_methods.rb +32 -7
- data/lib/ruby_llm/active_record/model.rb +1 -1
- data/lib/ruby_llm/active_record/provider_file.rb +64 -0
- data/lib/ruby_llm/active_record/record.rb +19 -0
- data/lib/ruby_llm/active_record/tool_call.rb +1 -1
- data/lib/ruby_llm/active_record/usage.rb +53 -3
- data/lib/ruby_llm/agent.rb +198 -33
- data/lib/ruby_llm/aliases.json +66 -8
- data/lib/ruby_llm/attachment.rb +54 -10
- data/lib/ruby_llm/batch.rb +26 -3
- data/lib/ruby_llm/chat/tool_concurrency.rb +47 -23
- data/lib/ruby_llm/chat/tool_search.rb +26 -0
- data/lib/ruby_llm/chat.rb +492 -82
- data/lib/ruby_llm/choice.rb +38 -0
- data/lib/ruby_llm/configuration.rb +36 -11
- data/lib/ruby_llm/context.rb +45 -32
- data/lib/ruby_llm/cost.rb +42 -1
- data/lib/ruby_llm/embedding.rb +9 -2
- data/lib/ruby_llm/error.rb +34 -2
- data/lib/ruby_llm/evaluation/assertions.rb +40 -0
- data/lib/ruby_llm/evaluation/case.rb +52 -0
- data/lib/ruby_llm/evaluation/dataset.rb +65 -0
- data/lib/ruby_llm/evaluation/evaluator.rb +126 -0
- data/lib/ruby_llm/evaluation/evidence.rb +125 -0
- data/lib/ruby_llm/evaluation/minitest.rb +26 -0
- data/lib/ruby_llm/evaluation/report.rb +103 -0
- data/lib/ruby_llm/evaluation/result.rb +75 -0
- data/lib/ruby_llm/evaluation/reviewer.rb +20 -0
- data/lib/ruby_llm/evaluation/rspec.rb +23 -0
- data/lib/ruby_llm/evaluation/runner.rb +93 -0
- data/lib/ruby_llm/evaluation/trial.rb +101 -0
- data/lib/ruby_llm/evaluation.rb +289 -0
- data/lib/ruby_llm/image.rb +12 -4
- data/lib/ruby_llm/judge/builder.rb +51 -0
- data/lib/ruby_llm/judge/data.rb +43 -0
- data/lib/ruby_llm/judge/question.rb +101 -0
- data/lib/ruby_llm/judge.rb +179 -0
- data/lib/ruby_llm/judgment.rb +125 -0
- data/lib/ruby_llm/mcp/apps.rb +25 -0
- data/lib/ruby_llm/mcp/client.rb +226 -0
- data/lib/ruby_llm/mcp/collection.rb +56 -0
- data/lib/ruby_llm/mcp/content.rb +44 -0
- data/lib/ruby_llm/mcp/error.rb +27 -0
- data/lib/ruby_llm/mcp/http.rb +379 -0
- data/lib/ruby_llm/mcp/input_request.rb +118 -0
- data/lib/ruby_llm/mcp/input_required_error.rb +29 -0
- data/lib/ruby_llm/mcp/listener.rb +168 -0
- data/lib/ruby_llm/mcp/oauth/key.rb +78 -0
- data/lib/ruby_llm/mcp/oauth/memory_store.rb +28 -0
- data/lib/ruby_llm/mcp/oauth/proofs.rb +39 -0
- data/lib/ruby_llm/mcp/oauth.rb +653 -0
- data/lib/ruby_llm/mcp/param_headers.rb +83 -0
- data/lib/ruby_llm/mcp/prompt.rb +71 -0
- data/lib/ruby_llm/mcp/resource.rb +84 -0
- data/lib/ruby_llm/mcp/resource_template.rb +99 -0
- data/lib/ruby_llm/mcp/result.rb +76 -0
- data/lib/ruby_llm/mcp/session_expired.rb +10 -0
- data/lib/ruby_llm/mcp/stdio.rb +250 -0
- data/lib/ruby_llm/mcp/task.rb +151 -0
- data/lib/ruby_llm/mcp/tool.rb +130 -0
- data/lib/ruby_llm/mcp.rb +1101 -0
- data/lib/ruby_llm/message.rb +41 -6
- data/lib/ruby_llm/model.rb +2 -1
- data/lib/ruby_llm/models/aliases.rb +5 -2
- data/lib/ruby_llm/models/lookup.rb +80 -0
- data/lib/ruby_llm/models/registry.rb +2 -2
- data/lib/ruby_llm/models/schema.rb +1 -1
- data/lib/ruby_llm/models.json +23082 -10158
- data/lib/ruby_llm/models.rb +20 -69
- data/lib/ruby_llm/moderation.rb +7 -2
- data/lib/ruby_llm/ocr.rb +7 -2
- data/lib/ruby_llm/open_telemetry/attributes.rb +111 -0
- data/lib/ruby_llm/open_telemetry.rb +82 -0
- data/lib/ruby_llm/probability.rb +30 -0
- data/lib/ruby_llm/progress.rb +44 -0
- data/lib/ruby_llm/prompt/context.rb +55 -0
- data/lib/ruby_llm/prompt.rb +1 -1
- data/lib/ruby_llm/protocol/binary_streaming.rb +1 -1
- data/lib/ruby_llm/protocol/request_shapes/analysis.rb +190 -0
- data/lib/ruby_llm/protocol/request_shapes.rb +183 -0
- data/lib/ruby_llm/protocol/stored_uploads.rb +62 -0
- data/lib/ruby_llm/protocol/stream_accumulator.rb +9 -25
- data/lib/ruby_llm/protocol/streaming.rb +38 -31
- data/lib/ruby_llm/protocol.rb +196 -40
- data/lib/ruby_llm/protocols/anthropic/chat.rb +39 -32
- data/lib/ruby_llm/protocols/anthropic/media.rb +6 -2
- data/lib/ruby_llm/protocols/anthropic/models.rb +16 -1
- data/lib/ruby_llm/protocols/anthropic/request_shapes.rb +70 -0
- data/lib/ruby_llm/protocols/anthropic/streaming.rb +23 -9
- data/lib/ruby_llm/protocols/anthropic/tools.rb +43 -0
- data/lib/ruby_llm/protocols/anthropic.rb +20 -1
- data/lib/ruby_llm/protocols/bedrock/async_videos.rb +9 -17
- data/lib/ruby_llm/protocols/bedrock/guardrails.rb +8 -9
- data/lib/ruby_llm/protocols/bedrock/rerank.rb +1 -17
- data/lib/ruby_llm/protocols/chat_completions/chat.rb +5 -7
- data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +21 -0
- data/lib/ruby_llm/protocols/chat_completions/media.rb +34 -8
- data/lib/ruby_llm/protocols/chat_completions/request_shapes.rb +121 -0
- data/lib/ruby_llm/protocols/chat_completions/streaming.rb +3 -0
- data/lib/ruby_llm/protocols/chat_completions.rb +1 -0
- data/lib/ruby_llm/protocols/cohere/batch_requests.rb +0 -5
- data/lib/ruby_llm/protocols/cohere/batches.rb +3 -7
- data/lib/ruby_llm/protocols/cohere/chat.rb +8 -0
- data/lib/ruby_llm/protocols/cohere/embeddings.rb +7 -4
- data/lib/ruby_llm/protocols/cohere/media.rb +6 -2
- data/lib/ruby_llm/protocols/cohere/ocr.rb +1 -1
- data/lib/ruby_llm/protocols/cohere/streaming.rb +1 -0
- data/lib/ruby_llm/protocols/cohere/tools.rb +5 -1
- data/lib/ruby_llm/protocols/cohere.rb +1 -0
- data/lib/ruby_llm/protocols/converse/chat.rb +22 -131
- data/lib/ruby_llm/protocols/converse/media.rb +16 -2
- data/lib/ruby_llm/protocols/converse/reasoning.rb +194 -0
- data/lib/ruby_llm/protocols/converse/request_shapes.rb +99 -0
- data/lib/ruby_llm/protocols/converse/streaming.rb +2 -2
- data/lib/ruby_llm/protocols/converse/thinking_stream.rb +2 -1
- data/lib/ruby_llm/protocols/converse.rb +3 -5
- data/lib/ruby_llm/protocols/deepseek/files.rb +2 -9
- data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +1 -7
- data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +11 -17
- data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +1 -4
- data/lib/ruby_llm/protocols/gemini/chat.rb +50 -21
- data/lib/ruby_llm/protocols/gemini/embeddings.rb +2 -2
- data/lib/ruby_llm/protocols/gemini/files.rb +1 -1
- data/lib/ruby_llm/protocols/gemini/images.rb +5 -1
- data/lib/ruby_llm/protocols/gemini/media.rb +15 -3
- data/lib/ruby_llm/protocols/gemini/request_shapes.rb +111 -0
- data/lib/ruby_llm/protocols/gemini/streaming.rb +2 -1
- data/lib/ruby_llm/protocols/gemini/tools.rb +17 -11
- data/lib/ruby_llm/protocols/gemini/transcription.rb +13 -0
- data/lib/ruby_llm/protocols/gemini/videos.rb +7 -2
- data/lib/ruby_llm/protocols/gemini.rb +1 -0
- data/lib/ruby_llm/protocols/gpustack/videos.rb +7 -9
- data/lib/ruby_llm/protocols/interactions/chat.rb +46 -18
- data/lib/ruby_llm/protocols/interactions/content.rb +9 -2
- data/lib/ruby_llm/protocols/interactions/request_shapes.rb +62 -0
- data/lib/ruby_llm/protocols/interactions/streaming.rb +15 -0
- data/lib/ruby_llm/protocols/interactions/tools.rb +28 -4
- data/lib/ruby_llm/protocols/interactions/transcription.rb +1 -11
- data/lib/ruby_llm/protocols/interactions.rb +1 -0
- data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +2 -11
- data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +2 -2
- data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +2 -6
- data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +2 -8
- data/lib/ruby_llm/protocols/invoke_model.rb +1 -4
- data/lib/ruby_llm/protocols/mistral/content.rb +13 -0
- data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +11 -1
- data/lib/ruby_llm/protocols/mistral/conversations/images.rb +5 -1
- data/lib/ruby_llm/protocols/mistral/conversations/request_shapes.rb +45 -0
- data/lib/ruby_llm/protocols/mistral/conversations.rb +1 -0
- data/lib/ruby_llm/protocols/mistral/multi_completion.rb +11 -1
- data/lib/ruby_llm/protocols/openai/decisions.rb +135 -0
- data/lib/ruby_llm/protocols/openai/files.rb +0 -7
- data/lib/ruby_llm/protocols/openrouter/batches.rb +13 -8
- data/lib/ruby_llm/protocols/openrouter/responses.rb +5 -1
- data/lib/ruby_llm/protocols/perplexity/agent.rb +116 -0
- data/lib/ruby_llm/protocols/perplexity/router.rb +2 -43
- data/lib/ruby_llm/protocols/responses/chat.rb +56 -22
- data/lib/ruby_llm/protocols/responses/media.rb +11 -7
- data/lib/ruby_llm/protocols/responses/request_shapes.rb +70 -0
- data/lib/ruby_llm/protocols/responses/streaming.rb +23 -7
- data/lib/ruby_llm/protocols/responses/token_counting.rb +2 -2
- data/lib/ruby_llm/protocols/responses/tools.rb +27 -0
- data/lib/ruby_llm/protocols/responses.rb +1 -0
- data/lib/ruby_llm/protocols/system_one/judgments.rb +42 -0
- data/lib/ruby_llm/protocols/system_one/models.rb +27 -0
- data/lib/ruby_llm/protocols/system_one/responses.rb +55 -0
- data/lib/ruby_llm/protocols/system_one.rb +27 -0
- data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +2 -2
- data/lib/ruby_llm/protocols/vertexai/files.rb +34 -6
- data/lib/ruby_llm/protocols/vertexai/ranking.rb +9 -30
- data/lib/ruby_llm/protocols/vertexai/research.rb +9 -41
- data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +2 -5
- data/lib/ruby_llm/provider.rb +87 -15
- data/lib/ruby_llm/providers/anthropic.rb +23 -0
- data/lib/ruby_llm/providers/azure/chat.rb +4 -0
- data/lib/ruby_llm/providers/azure/images.rb +2 -2
- data/lib/ruby_llm/providers/azure/media.rb +8 -3
- data/lib/ruby_llm/providers/azure/videos.rb +7 -2
- data/lib/ruby_llm/providers/azure.rb +24 -4
- data/lib/ruby_llm/providers/bedrock/auth.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +1 -3
- data/lib/ruby_llm/providers/bedrock/mantle.rb +5 -9
- data/lib/ruby_llm/providers/bedrock/models.rb +1 -2
- data/lib/ruby_llm/providers/bedrock.rb +38 -6
- data/lib/ruby_llm/providers/deepseek/chat.rb +6 -1
- data/lib/ruby_llm/providers/deepseek/responses.rb +6 -0
- data/lib/ruby_llm/providers/deepseek.rb +4 -0
- data/lib/ruby_llm/providers/gemini.rb +4 -0
- data/lib/ruby_llm/providers/gpustack/media.rb +8 -5
- data/lib/ruby_llm/providers/hetzner/chat.rb +39 -0
- data/lib/ruby_llm/providers/hetzner.rb +41 -0
- data/lib/ruby_llm/providers/mistral/chat.rb +16 -12
- data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +17 -3
- data/lib/ruby_llm/providers/mistral/media.rb +8 -4
- data/lib/ruby_llm/providers/mistral/ocr.rb +1 -5
- data/lib/ruby_llm/providers/ollama/judgments.rb +26 -0
- data/lib/ruby_llm/providers/ollama/media.rb +6 -2
- data/lib/ruby_llm/providers/ollama/models.rb +2 -0
- data/lib/ruby_llm/providers/ollama.rb +11 -0
- data/lib/ruby_llm/providers/openai/capabilities.rb +3 -1
- data/lib/ruby_llm/providers/openai.rb +14 -0
- data/lib/ruby_llm/providers/openrouter/chat.rb +20 -24
- data/lib/ruby_llm/providers/openrouter/embeddings.rb +2 -0
- data/lib/ruby_llm/providers/openrouter/media.rb +5 -1
- data/lib/ruby_llm/providers/openrouter/streaming.rb +4 -1
- data/lib/ruby_llm/providers/openrouter/videos.rb +10 -5
- data/lib/ruby_llm/providers/openrouter.rb +4 -0
- data/lib/ruby_llm/providers/perplexity/chat.rb +4 -0
- data/lib/ruby_llm/providers/perplexity/media.rb +5 -11
- data/lib/ruby_llm/providers/perplexity/models.rb +8 -4
- data/lib/ruby_llm/providers/perplexity.rb +11 -0
- data/lib/ruby_llm/providers/typesafe.rb +32 -0
- data/lib/ruby_llm/providers/vertexai/credentials.rb +63 -0
- data/lib/ruby_llm/providers/vertexai/gemini.rb +2 -2
- data/lib/ruby_llm/providers/vertexai/transcription.rb +1 -0
- data/lib/ruby_llm/providers/vertexai.rb +27 -28
- data/lib/ruby_llm/providers/xai/chat.rb +6 -0
- data/lib/ruby_llm/providers/xai/images.rb +9 -10
- data/lib/ruby_llm/providers/xai/responses.rb +10 -8
- data/lib/ruby_llm/providers/xai/videos.rb +11 -9
- data/lib/ruby_llm/providers/xai.rb +4 -0
- data/lib/ruby_llm/railtie.rb +11 -0
- data/lib/ruby_llm/request_shape/part.rb +117 -0
- data/lib/ruby_llm/request_shape/problem.rb +63 -0
- data/lib/ruby_llm/request_shape/tool_round.rb +64 -0
- data/lib/ruby_llm/request_shape/turn.rb +54 -0
- data/lib/ruby_llm/request_shape.rb +160 -0
- data/lib/ruby_llm/rerank.rb +9 -2
- data/lib/ruby_llm/research_job.rb +21 -5
- data/lib/ruby_llm/score.rb +42 -0
- data/lib/ruby_llm/search_results.rb +2 -2
- data/lib/ruby_llm/server_tool_call.rb +10 -2
- data/lib/ruby_llm/speech.rb +8 -1
- data/lib/ruby_llm/support/cancellation.rb +39 -0
- data/lib/ruby_llm/support/inspectable.rb +3 -2
- data/lib/ruby_llm/support/instrumentation.rb +38 -1
- data/lib/ruby_llm/support/process_cache.rb +60 -0
- data/lib/ruby_llm/support/progress_reporter.rb +33 -0
- data/lib/ruby_llm/thinking.rb +2 -1
- data/lib/ruby_llm/tokens.rb +57 -14
- data/lib/ruby_llm/tool.rb +50 -3
- data/lib/ruby_llm/tool_call.rb +17 -2
- data/lib/ruby_llm/transcription.rb +10 -3
- data/lib/ruby_llm/transport/connection.rb +127 -44
- data/lib/ruby_llm/transport/error_middleware.rb +90 -24
- data/lib/ruby_llm/transport/event_stream_parser.rb +121 -0
- data/lib/ruby_llm/transport/json_response.rb +33 -0
- data/lib/ruby_llm/transport/signing_middleware.rb +29 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm/video.rb +5 -3
- data/lib/ruby_llm/video_job.rb +48 -5
- data/lib/ruby_llm.rb +106 -5
- data/lib/tasks/ruby_llm/evaluations.rb +43 -0
- data/lib/tasks/ruby_llm.rake +6 -9
- data/skills/rubyllm/SKILL.md +33 -12
- metadata +101 -37
- data/lib/generators/ruby_llm/upgrade/legacy_content_sql.rb +0 -34
- data/lib/generators/ruby_llm/upgrade/online_copy_migration/data.rb +0 -341
- data/lib/generators/ruby_llm/upgrade/online_copy_migration/journal.rb +0 -171
- data/lib/generators/ruby_llm/upgrade/online_copy_migration/verification.rb +0 -197
- data/lib/generators/ruby_llm/upgrade/online_copy_migration.rb +0 -174
- data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +0 -468
- data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +0 -101
- data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +0 -247
- data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +0 -662
- data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +0 -251
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +0 -13
- data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +0 -359
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8000141c827d6b8f899bcf2ec5a23153ecf83f8196fcd35cee7af8de2f9e58e4
|
|
4
|
+
data.tar.gz: '069b322ad8df77cd46ee2dd9b72ec02578281ff9e6c96b349e46aff911ee85b8'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6d651244192242fc5f06cf8126932659b46dec1228ed97e3a9876dd7ee581f8972d42fbaca209698b1c42e1704ebb766ca07b450cfd7651d9192f8d46d3f8c42
|
|
7
|
+
data.tar.gz: 9511e7a1c10a8b73cebbefb59a26be1fd7dcec10c369f57d60b7a348d180a8037700826400d9da41b630d190b940aa73ba7c5cbdb7fbd0f4b6cffbcbe563d732
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<p>RubyLLM is the Ruby-native AI framework. Work with models, tools, and agents through one consistent API, in plain Ruby or Rails.</p>
|
|
11
11
|
|
|
12
|
-
[Website](https://rubyllm.com/) · [Getting Started](https://rubyllm.com/getting-started/) · [What's New in 2.
|
|
12
|
+
[Website](https://rubyllm.com/) · [Getting Started](https://rubyllm.com/getting-started/) · [What's New in 2.1](https://rubyllm.com/whats-new-in-2-1/)
|
|
13
13
|
|
|
14
14
|
Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="https://chatwithwork.com/logotype-dark.svg"><img src="https://chatwithwork.com/logotype.svg" alt="Chat with Work" height="30" align="absmiddle"></picture>](https://chatwithwork.com) - *Fully private work AI*
|
|
15
15
|
|
|
@@ -21,12 +21,14 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
|
|
|
21
21
|
<a href="https://trendshift.io/repositories/13640" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13640" alt="crmne%2Fruby_llm | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
+
https://github.com/user-attachments/assets/519fcb20-ab8c-4af3-9185-0953b3ba7b81
|
|
25
|
+
|
|
24
26
|
> [!NOTE]
|
|
25
27
|
> Using RubyLLM? [Share your story](https://tally.so/r/3Na02p)! Takes 5 minutes.
|
|
26
28
|
|
|
27
29
|
---
|
|
28
30
|
|
|
29
|
-
##
|
|
31
|
+
## 19 providers. One Ruby API.
|
|
30
32
|
|
|
31
33
|
Build with the models you want. Move between hosted and local providers without rewriting your application, or connect an OpenAI-compatible endpoint.
|
|
32
34
|
|
|
@@ -34,7 +36,7 @@ Build with the models you want. Move between hosted and local providers without
|
|
|
34
36
|
|
|
35
37
|
## Start with one line. Add files, tools, and agents
|
|
36
38
|
|
|
37
|
-
These examples use **2.
|
|
39
|
+
These examples use **2.1.1**. Follow [Getting Started](https://rubyllm.com/getting-started/) to install it and configure the providers you want to try. For 1.x, use the [1.x docs](https://rubyllm.com/v1/).
|
|
38
40
|
|
|
39
41
|
```ruby
|
|
40
42
|
# Just ask
|
|
@@ -111,6 +113,15 @@ puts document.markdown
|
|
|
111
113
|
RubyLLM.moderate("Some user-generated content").flagged?
|
|
112
114
|
```
|
|
113
115
|
|
|
116
|
+
```ruby
|
|
117
|
+
# Ask a typed question about your data
|
|
118
|
+
class Urgency < RubyLLM::Judge
|
|
119
|
+
probability :urgent, "Does this need attention today?"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
Urgency.judge("Please refund my duplicate charge today.").urgent.probability
|
|
123
|
+
```
|
|
124
|
+
|
|
114
125
|
```ruby
|
|
115
126
|
# Let AI use your code
|
|
116
127
|
class Weather < RubyLLM::Tool
|
|
@@ -150,9 +161,55 @@ response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "pr
|
|
|
150
161
|
response.parsed
|
|
151
162
|
```
|
|
152
163
|
|
|
164
|
+
## Evaluate your agents
|
|
165
|
+
|
|
166
|
+
Keep questions and expected answers in a dataset. RubyLLM checks correctness by default. Run the same evaluation from Ruby, RSpec, Minitest, or Rake.
|
|
167
|
+
|
|
168
|
+
Evaluations are available on `main`. Follow the [development installation guide](https://rubyllm.com/next/getting-started/) to try them.
|
|
169
|
+
|
|
170
|
+
`app/agents/support_agent.rb`
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
class SupportAgent < RubyLLM::Agent
|
|
174
|
+
instructions "Accept returns of unopened items within 30 days."
|
|
175
|
+
end
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`app/evals/support_evaluation.rb`
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
class SupportEvaluation < RubyLLM::Evaluation
|
|
182
|
+
def perform(input)
|
|
183
|
+
SupportAgent.new.ask(input)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`app/evals/support_evaluation.yml`
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
cases:
|
|
192
|
+
- name: within_return_window
|
|
193
|
+
inputs: Can I return an unopened item after 14 days?
|
|
194
|
+
expected_output: Yes, unopened items can be returned within 30 days.
|
|
195
|
+
- name: past_return_window
|
|
196
|
+
inputs: Can I return an unopened item after 45 days?
|
|
197
|
+
expected_output: No, the 30-day return window has passed.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Run the evaluation:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
report = SupportEvaluation.run
|
|
204
|
+
report.passed?
|
|
205
|
+
report.cost.total
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The model evaluator checks meaning, so answers can use different wording. Declare your own criteria, add Ruby assertions, or choose a configured evaluator agent or judgment model. [Read the evaluation guides](https://rubyllm.com/next/evaluations/).
|
|
209
|
+
|
|
153
210
|
## A complete AI framework for Ruby
|
|
154
211
|
|
|
155
|
-
Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking and Rails integration to bring them into your app.
|
|
212
|
+
Agents, evaluations, workflows, RAG, images, audio, and video. Built in, with usage tracking and Rails integration to bring them into your app.
|
|
156
213
|
|
|
157
214
|
* **Chat:** Conversational AI with `RubyLLM.chat`
|
|
158
215
|
* **Vision:** Analyze images and videos
|
|
@@ -164,11 +221,13 @@ Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking
|
|
|
164
221
|
* **Embeddings:** Generate embeddings with `RubyLLM.embed`
|
|
165
222
|
* **Reranking:** Order retrieval candidates by relevance with `RubyLLM.rerank`
|
|
166
223
|
* **Moderation:** Content flags, categories, and scores with `RubyLLM.moderate`
|
|
224
|
+
* **Judgments:** Probabilities, choices, and scores with `RubyLLM::Judge` and `RubyLLM.judge`
|
|
167
225
|
* **Tools:** Let AI call your Ruby methods
|
|
168
226
|
* **Tool approval:** Park a run until a human approves with `requires_approval`
|
|
169
227
|
* **The agentic loop:** Drive it yourself with `ask_later`, `step`, and `complete?`
|
|
170
228
|
* **Provider tools:** Web search, code execution, and MCP connectors with `with_provider_tools`
|
|
171
229
|
* **Agents:** Reusable assistants with `RubyLLM::Agent`
|
|
230
|
+
* **Evaluations (main):** Reusable datasets, model criteria, and Ruby assertions with [`RubyLLM::Evaluation`](https://rubyllm.com/next/evaluations/)
|
|
172
231
|
* **Prompt templates:** ERB prompts in `app/prompts`, rendered with `RubyLLM.render_prompt`
|
|
173
232
|
* **Workflows:** Correlate multi-agent runs in your telemetry with `RubyLLM.workflow`
|
|
174
233
|
* **Structured output:** Define a Ruby schema and read the result with `response.parsed`
|
|
@@ -185,14 +244,14 @@ Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking
|
|
|
185
244
|
* **Batches:** Provider-side batch processing with provider-specific discounts via `RubyLLM.batch`
|
|
186
245
|
* **Compaction:** Let providers condense long conversations with `with_compaction`
|
|
187
246
|
* **Token counting:** Count a request before you send it with `count_tokens`
|
|
188
|
-
* **Providers:** OpenAI, Azure, xAI, Anthropic, Gemini, VertexAI, Bedrock, Cohere, DeepSeek, Mistral, Ollama, Ollama Cloud, OpenRouter, Perplexity, GPUStack, ElevenLabs, Deepgram, and any OpenAI-compatible API
|
|
247
|
+
* **Providers:** OpenAI, Azure, xAI, Anthropic, Gemini, VertexAI, Bedrock, Cohere, DeepSeek, Hetzner, Mistral, Ollama, Ollama Cloud, OpenRouter, Perplexity, GPUStack, ElevenLabs, Deepgram, TypeSafe, and any OpenAI-compatible API
|
|
189
248
|
|
|
190
249
|
## Installation
|
|
191
250
|
|
|
192
|
-
Install RubyLLM 2.
|
|
251
|
+
Install RubyLLM 2.1:
|
|
193
252
|
|
|
194
253
|
```bash
|
|
195
|
-
bundle add ruby_llm --version 2.
|
|
254
|
+
bundle add ruby_llm --version 2.1.1
|
|
196
255
|
```
|
|
197
256
|
|
|
198
257
|
Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
|
|
@@ -205,7 +264,7 @@ RubyLLM.configure do |config|
|
|
|
205
264
|
end
|
|
206
265
|
```
|
|
207
266
|
|
|
208
|
-
Configure the other providers used by the examples as needed: Gemini for files, xAI for video, Mistral for OCR, and Cohere for reranking. [Getting Started](https://rubyllm.com/getting-started/) shows each setup beside its example.
|
|
267
|
+
Configure the other providers used by the examples as needed: Gemini for files, xAI for video, Mistral for OCR, and Cohere for reranking. [Getting Started](https://rubyllm.com/getting-started/) shows each setup beside its example. Upgrading an existing app? Follow the [upgrade guide](https://rubyllm.com/upgrading/).
|
|
209
268
|
|
|
210
269
|
## Feels at home in Rails
|
|
211
270
|
|
|
@@ -149,15 +149,11 @@ module RubyLLM
|
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
def postgresql?
|
|
152
|
-
|
|
153
|
-
rescue StandardError
|
|
154
|
-
false
|
|
152
|
+
adapter_built_on?('ActiveRecord::ConnectionAdapters::PostgreSQLAdapter')
|
|
155
153
|
end
|
|
156
154
|
|
|
157
155
|
def mysql?
|
|
158
|
-
|
|
159
|
-
rescue StandardError
|
|
160
|
-
false
|
|
156
|
+
adapter_built_on?('ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter')
|
|
161
157
|
end
|
|
162
158
|
|
|
163
159
|
def table_exists?(table_name)
|
|
@@ -202,8 +198,74 @@ module RubyLLM
|
|
|
202
198
|
end
|
|
203
199
|
end
|
|
204
200
|
|
|
201
|
+
module ClassMethods # :nodoc:
|
|
202
|
+
def start(given_args = ARGV, config = {})
|
|
203
|
+
super(GeneratorHelpers.reorder_arguments(given_args, class_options), config)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def self.included(base)
|
|
208
|
+
base.extend(ClassMethods)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def self.reorder_arguments(args, options)
|
|
212
|
+
mappings = []
|
|
213
|
+
switches = []
|
|
214
|
+
index = 0
|
|
215
|
+
|
|
216
|
+
while index < args.length
|
|
217
|
+
argument = args[index]
|
|
218
|
+
return mappings + switches + args[index..] if argument == '--'
|
|
219
|
+
|
|
220
|
+
unless argument.is_a?(String) && argument.start_with?('-')
|
|
221
|
+
mappings << argument
|
|
222
|
+
index += 1
|
|
223
|
+
next
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
switches << argument
|
|
227
|
+
index += 1
|
|
228
|
+
next if argument.include?('=')
|
|
229
|
+
|
|
230
|
+
value = args[index]
|
|
231
|
+
next unless takes_value?(options, argument, value)
|
|
232
|
+
|
|
233
|
+
switches << value
|
|
234
|
+
index += 1
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
mappings + switches
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def self.takes_value?(options, argument, value)
|
|
241
|
+
return false if value.nil? || value.to_s.start_with?('-')
|
|
242
|
+
|
|
243
|
+
option = option_for(options, argument)
|
|
244
|
+
return false unless option
|
|
245
|
+
return %w[true TRUE t T false FALSE f F].include?(value) if option.type == :boolean
|
|
246
|
+
|
|
247
|
+
!argument.tr('_', '-').match?(/\A--(?:no|skip)-/)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def self.option_for(options, argument)
|
|
251
|
+
return option_for(options, "-#{argument[-1]}") if argument.match?(/\A-[a-z]{2,}\z/i)
|
|
252
|
+
|
|
253
|
+
name = argument.sub(/\A--?/, '').tr('-', '_')
|
|
254
|
+
options[name.to_sym] || options[name] ||
|
|
255
|
+
options.values.find { |candidate| candidate.aliases.include?(argument) } ||
|
|
256
|
+
options[name.sub(/\A(?:no|skip)_/, '').to_sym] || options[name.sub(/\A(?:no|skip)_/, '')]
|
|
257
|
+
end
|
|
258
|
+
|
|
205
259
|
private
|
|
206
260
|
|
|
261
|
+
# Compares class names because referencing an adapter constant loads
|
|
262
|
+
# its database driver, which the application may not have installed.
|
|
263
|
+
def adapter_built_on?(adapter_class_name)
|
|
264
|
+
::ActiveRecord::Base.connection.class.ancestors.any? { |ancestor| ancestor.name == adapter_class_name }
|
|
265
|
+
rescue StandardError
|
|
266
|
+
false
|
|
267
|
+
end
|
|
268
|
+
|
|
207
269
|
def add_association_params(params, default_assoc, table_name, model_name,
|
|
208
270
|
owner_table:, owner_model_name:, plural: false)
|
|
209
271
|
assoc = plural ? table_name.to_sym : table_name.singularize.to_sym
|
|
@@ -5,6 +5,7 @@ class <%= create_migration_class_name(message_table_name) %> < ActiveRecord::Mig
|
|
|
5
5
|
t.string :role, null: false
|
|
6
6
|
t.text :content
|
|
7
7
|
t.boolean :cache_until_here, null: false, default: false
|
|
8
|
+
t.string :cache_ttl
|
|
8
9
|
t.text :thinking_text
|
|
9
10
|
t.text :thinking_signature
|
|
10
11
|
<% if postgresql? %>
|
|
@@ -47,10 +47,16 @@ class <%= create_migration_class_name('ruby_llm_records') %> < ActiveRecord::Mig
|
|
|
47
47
|
t.boolean :remote, default: false, null: false
|
|
48
48
|
<% if postgresql? %>
|
|
49
49
|
t.jsonb :arguments, default: {}
|
|
50
|
+
t.jsonb :mcp_state
|
|
51
|
+
t.jsonb :mcp_result
|
|
50
52
|
<% elsif mysql? %>
|
|
51
53
|
t.json :arguments
|
|
54
|
+
t.json :mcp_state
|
|
55
|
+
t.json :mcp_result
|
|
52
56
|
<% else %>
|
|
53
57
|
t.json :arguments, default: {}
|
|
58
|
+
t.json :mcp_state
|
|
59
|
+
t.json :mcp_result
|
|
54
60
|
<% end %>
|
|
55
61
|
t.timestamps
|
|
56
62
|
|
|
@@ -60,9 +66,19 @@ class <%= create_migration_class_name('ruby_llm_records') %> < ActiveRecord::Mig
|
|
|
60
66
|
t.index :name
|
|
61
67
|
end
|
|
62
68
|
|
|
69
|
+
create_table :ruby_llm_mcp_credentials, id: :<%= reference_type %> do |t|
|
|
70
|
+
t.references :owner, polymorphic: true, type: :<%= reference_type %>
|
|
71
|
+
t.string :key, null: false
|
|
72
|
+
t.text :data
|
|
73
|
+
t.timestamps
|
|
74
|
+
|
|
75
|
+
t.index :key, unique: true
|
|
76
|
+
end
|
|
77
|
+
|
|
63
78
|
create_table :ruby_llm_usages, id: :<%= reference_type %> do |t|
|
|
64
|
-
t.references :chat, polymorphic: true,
|
|
79
|
+
t.references :chat, polymorphic: true, type: :<%= reference_type %>, index: false
|
|
65
80
|
t.references :message, polymorphic: true, type: :<%= reference_type %>, index: false
|
|
81
|
+
t.references :owner, polymorphic: true, type: :<%= reference_type %>, index: false
|
|
66
82
|
t.string :operation, null: false
|
|
67
83
|
t.string :provider, null: false
|
|
68
84
|
t.string :model, null: false
|
|
@@ -72,6 +88,11 @@ class <%= create_migration_class_name('ruby_llm_records') %> < ActiveRecord::Mig
|
|
|
72
88
|
t.integer :cache_read_tokens
|
|
73
89
|
t.integer :cache_write_tokens
|
|
74
90
|
t.integer :thinking_tokens
|
|
91
|
+
<% if postgresql? %>
|
|
92
|
+
t.jsonb :server_tool_use
|
|
93
|
+
<% else %>
|
|
94
|
+
t.json :server_tool_use
|
|
95
|
+
<% end %>
|
|
75
96
|
t.decimal :input_cost, precision: 16, scale: 10
|
|
76
97
|
t.decimal :output_cost, precision: 16, scale: 10
|
|
77
98
|
t.decimal :cache_read_cost, precision: 16, scale: 10
|
|
@@ -82,9 +103,10 @@ class <%= create_migration_class_name('ruby_llm_records') %> < ActiveRecord::Mig
|
|
|
82
103
|
|
|
83
104
|
t.index [ :chat_type, :chat_id ]
|
|
84
105
|
t.index [ :message_type, :message_id ]
|
|
106
|
+
t.index [ :owner_type, :owner_id ]
|
|
85
107
|
t.index :status
|
|
86
|
-
t.check_constraint "operation IN (<%= usage_operations_sql %>)"
|
|
87
|
-
t.check_constraint "status IN (<%= usage_statuses_sql %>)"
|
|
108
|
+
t.check_constraint "operation<%= '::text' if postgresql? %> IN (<%= usage_operations_sql %>)"
|
|
109
|
+
t.check_constraint "status<%= '::text' if postgresql? %> IN (<%= usage_statuses_sql %>)"
|
|
88
110
|
end
|
|
89
111
|
|
|
90
112
|
create_table :ruby_llm_batches, id: :<%= reference_type %> do |t|
|
|
@@ -113,5 +135,16 @@ class <%= create_migration_class_name('ruby_llm_records') %> < ActiveRecord::Mig
|
|
|
113
135
|
t.index [ :provider, :provider_batch_id ], unique: true
|
|
114
136
|
t.index :status
|
|
115
137
|
end
|
|
138
|
+
|
|
139
|
+
create_table :ruby_llm_provider_files, id: :<%= reference_type %> do |t|
|
|
140
|
+
t.string :blob_key, null: false
|
|
141
|
+
t.string :provider, null: false
|
|
142
|
+
t.string :account, null: false
|
|
143
|
+
t.text :file_id, null: false
|
|
144
|
+
t.datetime :expires_at
|
|
145
|
+
t.timestamps
|
|
146
|
+
|
|
147
|
+
t.index [ :blob_key, :provider, :account ], unique: true, name: "index_ruby_llm_provider_files_uniqueness"
|
|
148
|
+
end
|
|
116
149
|
end
|
|
117
150
|
end
|
|
@@ -11,6 +11,9 @@ module RubyLLM
|
|
|
11
11
|
# Generates first-party providers and standalone provider gems.
|
|
12
12
|
class Scaffold # :nodoc: all
|
|
13
13
|
SUPPORTED_MODES = %w[core gem].freeze
|
|
14
|
+
HASH_ENTRY_LINE = /^\s+'[^']+' => '[^']+',?$/
|
|
15
|
+
ENV_LINE = /^#?\s*[A-Z][A-Z0-9_]*=/
|
|
16
|
+
SPEC_CONFIG_LINE = /^ {6}config\.[a-z0-9_]+ = /
|
|
14
17
|
SUPPORTED_DIALECTS = %w[chat_completions responses anthropic gemini converse ollama].freeze
|
|
15
18
|
TEMPLATE_ROOT = File.expand_path('templates', __dir__)
|
|
16
19
|
|
|
@@ -157,7 +160,7 @@ module RubyLLM
|
|
|
157
160
|
path = file_path('lib/ruby_llm.rb')
|
|
158
161
|
return unless File.exist?(path)
|
|
159
162
|
|
|
160
|
-
|
|
163
|
+
insert_sorted_entry(path, " '#{slug}' => '#{class_name}'")
|
|
161
164
|
insert_sorted_line(
|
|
162
165
|
path,
|
|
163
166
|
"RubyLLM::Provider.register :#{slug}, RubyLLM::Providers::#{class_name}",
|
|
@@ -169,8 +172,8 @@ module RubyLLM
|
|
|
169
172
|
path = file_path('.env.example')
|
|
170
173
|
return unless File.exist?(path)
|
|
171
174
|
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
insert_sorted_line(path, "#{api_key_env}=$(op read \"op://RubyLLM/#{class_name}/credential\")", ENV_LINE)
|
|
176
|
+
insert_sorted_line(path, "#{api_base_env}=#{api_base}", ENV_LINE)
|
|
174
177
|
end
|
|
175
178
|
|
|
176
179
|
def update_core_spec_configuration
|
|
@@ -178,9 +181,9 @@ module RubyLLM
|
|
|
178
181
|
return unless File.exist?(path)
|
|
179
182
|
|
|
180
183
|
insert_sorted_line(path, " config.#{api_base_config} = ENV.fetch('#{api_base_env}', '#{api_base}')",
|
|
181
|
-
|
|
184
|
+
SPEC_CONFIG_LINE)
|
|
182
185
|
insert_sorted_line(path, " config.#{api_key_config} = ENV.fetch('#{api_key_env}', 'test')",
|
|
183
|
-
|
|
186
|
+
SPEC_CONFIG_LINE)
|
|
184
187
|
end
|
|
185
188
|
|
|
186
189
|
def update_core_vcr_configuration
|
|
@@ -196,7 +199,7 @@ module RubyLLM
|
|
|
196
199
|
path = file_path('lib/ruby_llm/models.rb')
|
|
197
200
|
return unless File.exist?(path)
|
|
198
201
|
|
|
199
|
-
|
|
202
|
+
insert_sorted_entry(path, " '#{models_dev_provider}' => '#{slug}'")
|
|
200
203
|
end
|
|
201
204
|
|
|
202
205
|
def write_template(template, relative_path, executable: false)
|
|
@@ -218,38 +221,45 @@ module RubyLLM
|
|
|
218
221
|
record(existed ? :updated : :written, relative_path)
|
|
219
222
|
end
|
|
220
223
|
|
|
221
|
-
def append_unique_line(path, line)
|
|
222
|
-
content = File.exist?(path) ? File.read(path) : ''
|
|
223
|
-
return record(:skipped, relative_path(path)) if content.include?(line)
|
|
224
|
-
|
|
225
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
226
|
-
File.write(path, "#{content.chomp}\n#{line}\n")
|
|
227
|
-
record(:updated, relative_path(path))
|
|
228
|
-
end
|
|
229
|
-
|
|
230
224
|
def insert_sorted_line(path, line, matcher)
|
|
231
225
|
content = File.read(path)
|
|
232
226
|
return record(:skipped, relative_path(path)) if content.include?(line)
|
|
233
227
|
|
|
234
228
|
lines = content.lines
|
|
235
|
-
|
|
236
|
-
insert_at = indexes.find { |index| line < lines[index].chomp } || indexes.last&.+(1) || lines.length
|
|
237
|
-
lines.insert(insert_at, "#{line}\n")
|
|
229
|
+
lines.insert(sorted_line_index(lines, line, matcher), "#{line}\n")
|
|
238
230
|
File.write(path, lines.join)
|
|
239
231
|
record(:updated, relative_path(path))
|
|
240
232
|
end
|
|
241
233
|
|
|
242
|
-
|
|
234
|
+
# Lands after the last smaller line, above any comment that describes the next one.
|
|
235
|
+
def sorted_line_index(lines, line, matcher)
|
|
236
|
+
indexes = lines.each_index.select { |index| lines[index].match?(matcher) }
|
|
237
|
+
index = indexes.find { |candidate| line < lines[candidate].chomp } || indexes.last&.+(1) || lines.length
|
|
238
|
+
index -= 1 while index.positive? && lines[index - 1].strip.start_with?('#')
|
|
239
|
+
index
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Inserts a `'key' => 'value'` entry into the first multiline hash literal of
|
|
243
|
+
# such entries, keeping it sorted and every entry but the last comma-terminated.
|
|
244
|
+
def insert_sorted_entry(path, entry)
|
|
243
245
|
content = File.read(path)
|
|
244
|
-
return record(:skipped, relative_path(path)) if content.
|
|
246
|
+
return record(:skipped, relative_path(path)) if content.match?(/^#{Regexp.escape(entry)},?$/)
|
|
245
247
|
|
|
246
248
|
lines = content.lines
|
|
247
|
-
|
|
248
|
-
lines.
|
|
249
|
+
block = hash_entry_block(lines)
|
|
250
|
+
insert_at = block.find { |index| entry < lines[index].chomp.delete_suffix(',') } || (block.last + 1)
|
|
251
|
+
lines.insert(insert_at, "#{entry}\n")
|
|
252
|
+
hash_entry_block(lines)[0...-1].each { |index| lines[index] = "#{lines[index].chomp.delete_suffix(',')},\n" }
|
|
249
253
|
File.write(path, lines.join)
|
|
250
254
|
record(:updated, relative_path(path))
|
|
251
255
|
end
|
|
252
256
|
|
|
257
|
+
def hash_entry_block(lines)
|
|
258
|
+
first = lines.index { |line| line.match?(HASH_ENTRY_LINE) }
|
|
259
|
+
last = (first...lines.length).take_while { |index| lines[index].match?(HASH_ENTRY_LINE) }.last
|
|
260
|
+
(first..last).to_a
|
|
261
|
+
end
|
|
262
|
+
|
|
253
263
|
def record(action, path)
|
|
254
264
|
@result.public_send(action) << path
|
|
255
265
|
@result.actions << [action, path]
|
|
@@ -16,7 +16,7 @@ jobs:
|
|
|
16
16
|
strategy:
|
|
17
17
|
fail-fast: false
|
|
18
18
|
matrix:
|
|
19
|
-
ruby-version: ["3.
|
|
19
|
+
ruby-version: ["3.2", "3.3", "3.4", "4.0"]
|
|
20
20
|
steps:
|
|
21
21
|
- uses: actions/checkout@v6
|
|
22
22
|
|
|
@@ -26,7 +26,3 @@ jobs:
|
|
|
26
26
|
bundler-cache: true
|
|
27
27
|
|
|
28
28
|
- run: bundle exec rake
|
|
29
|
-
if: matrix.ruby-version != '3.1'
|
|
30
|
-
|
|
31
|
-
- run: bundle exec rake rubocop flay spec
|
|
32
|
-
if: matrix.ruby-version == '3.1'
|
|
@@ -5,7 +5,7 @@ source 'https://rubygems.org'
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
7
|
group :development do
|
|
8
|
-
gem 'archspec' if RUBY_ENGINE == 'ruby'
|
|
8
|
+
gem 'archspec' if RUBY_ENGINE == 'ruby'
|
|
9
9
|
gem 'bundler', '>= 2.0'
|
|
10
10
|
gem 'dotenv'
|
|
11
11
|
gem 'flay'
|
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
spec.description = 'Adds <%= class_name %> provider support to RubyLLM.'
|
|
11
11
|
spec.homepage = 'https://github.com/<%= github_owner %>/<%= gem_name %>'
|
|
12
12
|
spec.license = 'MIT'
|
|
13
|
-
spec.required_ruby_version = '>= 3.
|
|
13
|
+
spec.required_ruby_version = '>= 3.2'
|
|
14
14
|
|
|
15
15
|
spec.metadata['homepage_uri'] = spec.homepage
|
|
16
16
|
spec.metadata['source_code_uri'] = spec.homepage
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
class <%= 'upgrade_ruby_llm_to_2_1'.camelize %> < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
+
def change
|
|
3
|
+
unless table_exists?(:ruby_llm_mcp_credentials)
|
|
4
|
+
create_table :ruby_llm_mcp_credentials, id: :<%= reference_type %> do |t|
|
|
5
|
+
t.references :owner, polymorphic: true, type: :<%= reference_type %>
|
|
6
|
+
t.string :key, null: false
|
|
7
|
+
t.text :data
|
|
8
|
+
t.timestamps
|
|
9
|
+
|
|
10
|
+
t.index :key, unique: true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
unless column_exists?(:ruby_llm_tool_calls, :mcp_state)
|
|
15
|
+
if column_exists?(:ruby_llm_tool_calls, :pending_input)
|
|
16
|
+
rename_column :ruby_llm_tool_calls, :pending_input, :mcp_state
|
|
17
|
+
else
|
|
18
|
+
<% if postgresql? -%>
|
|
19
|
+
add_column :ruby_llm_tool_calls, :mcp_state, :jsonb
|
|
20
|
+
<% else -%>
|
|
21
|
+
add_column :ruby_llm_tool_calls, :mcp_state, :json
|
|
22
|
+
<% end -%>
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
unless column_exists?(:ruby_llm_tool_calls, :mcp_result)
|
|
27
|
+
<% if postgresql? -%>
|
|
28
|
+
add_column :ruby_llm_tool_calls, :mcp_result, :jsonb
|
|
29
|
+
<% else -%>
|
|
30
|
+
add_column :ruby_llm_tool_calls, :mcp_result, :json
|
|
31
|
+
<% end -%>
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
unless column_exists?(:ruby_llm_usages, :server_tool_use)
|
|
35
|
+
<% if postgresql? -%>
|
|
36
|
+
add_column :ruby_llm_usages, :server_tool_use, :jsonb
|
|
37
|
+
<% else -%>
|
|
38
|
+
add_column :ruby_llm_usages, :server_tool_use, :json
|
|
39
|
+
<% end -%>
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if columns(:ruby_llm_usages).any? { |column| column.name == "chat_id" && !column.null }
|
|
43
|
+
change_column_null :ruby_llm_usages, :chat_type, true
|
|
44
|
+
change_column_null :ruby_llm_usages, :chat_id, true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
unless column_exists?(:ruby_llm_usages, :owner_id)
|
|
48
|
+
add_reference :ruby_llm_usages, :owner, polymorphic: true, type: :<%= reference_type %>, index: true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
reversible do |direction|
|
|
52
|
+
direction.up do
|
|
53
|
+
operations = check_constraints(:ruby_llm_usages).find { |constraint| constraint.expression.include?("operation") }
|
|
54
|
+
if operations && [<%= usage_operations_sql %>].any? { |operation| !operations.expression.include?("'#{operation}'") }
|
|
55
|
+
remove_check_constraint :ruby_llm_usages, name: operations.name
|
|
56
|
+
add_check_constraint :ruby_llm_usages, "operation<%= '::text' if postgresql? %> IN (<%= usage_operations_sql %>)"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
unless column_exists?(:<%= message_table_name %>, :cache_ttl)
|
|
62
|
+
add_column :<%= message_table_name %>, :cache_ttl, :string
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
unless table_exists?(:ruby_llm_provider_files)
|
|
66
|
+
create_table :ruby_llm_provider_files, id: :<%= reference_type %> do |t|
|
|
67
|
+
t.string :blob_key, null: false
|
|
68
|
+
t.string :provider, null: false
|
|
69
|
+
t.string :account, null: false
|
|
70
|
+
t.text :file_id, null: false
|
|
71
|
+
t.datetime :expires_at
|
|
72
|
+
t.timestamps
|
|
73
|
+
|
|
74
|
+
t.index [ :blob_key, :provider, :account ], unique: true, name: "index_ruby_llm_provider_files_uniqueness"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|