ruby_llm 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +65 -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 +136 -40
- 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 +488 -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 +213 -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 +192 -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/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.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/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 +95 -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: 2f0478372c3c7c58cd1452546f21068161f553949b064377738c28e74b67da9d
|
|
4
|
+
data.tar.gz: 2f8f63b90a885326904724a95074934877251b589b41ba00d9366dd72d5b3d69
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4028eea4e3515ffa779ada1d1464ab5ed4fa4c21418c1ebaa8fe09bb0da11fd6998f6b0132dde1cd59f876b54a43c03f89507be73e7d3e39b3b3794fbb33f3c5
|
|
7
|
+
data.tar.gz: e57218438a1479c2d4974926d4afdf509fa18f2c57ffe0524e116feea919d1fb337d80f883dca30205a442ed754faafafb076bcc780eceb2f86b4c6372a47321
|
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
|
|
|
@@ -26,7 +26,7 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## 19 providers. One Ruby API.
|
|
30
30
|
|
|
31
31
|
Build with the models you want. Move between hosted and local providers without rewriting your application, or connect an OpenAI-compatible endpoint.
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ Build with the models you want. Move between hosted and local providers without
|
|
|
34
34
|
|
|
35
35
|
## Start with one line. Add files, tools, and agents
|
|
36
36
|
|
|
37
|
-
These examples use **2.
|
|
37
|
+
These examples use **2.1.0**. 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
38
|
|
|
39
39
|
```ruby
|
|
40
40
|
# Just ask
|
|
@@ -111,6 +111,15 @@ puts document.markdown
|
|
|
111
111
|
RubyLLM.moderate("Some user-generated content").flagged?
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
```ruby
|
|
115
|
+
# Ask a typed question about your data
|
|
116
|
+
class Urgency < RubyLLM::Judge
|
|
117
|
+
probability :urgent, "Does this need attention today?"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
Urgency.judge("Please refund my duplicate charge today.").urgent.probability
|
|
121
|
+
```
|
|
122
|
+
|
|
114
123
|
```ruby
|
|
115
124
|
# Let AI use your code
|
|
116
125
|
class Weather < RubyLLM::Tool
|
|
@@ -150,9 +159,55 @@ response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "pr
|
|
|
150
159
|
response.parsed
|
|
151
160
|
```
|
|
152
161
|
|
|
162
|
+
## Evaluate your agents
|
|
163
|
+
|
|
164
|
+
Keep questions and expected answers in a dataset. RubyLLM checks correctness by default. Run the same evaluation from Ruby, RSpec, Minitest, or Rake.
|
|
165
|
+
|
|
166
|
+
Evaluations are available on `main`. Follow the [development installation guide](https://rubyllm.com/next/getting-started/) to try them.
|
|
167
|
+
|
|
168
|
+
`app/agents/support_agent.rb`
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
class SupportAgent < RubyLLM::Agent
|
|
172
|
+
instructions "Accept returns of unopened items within 30 days."
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`app/evals/support_evaluation.rb`
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
class SupportEvaluation < RubyLLM::Evaluation
|
|
180
|
+
def perform(input)
|
|
181
|
+
SupportAgent.new.ask(input)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`app/evals/support_evaluation.yml`
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
cases:
|
|
190
|
+
- name: within_return_window
|
|
191
|
+
inputs: Can I return an unopened item after 14 days?
|
|
192
|
+
expected_output: Yes, unopened items can be returned within 30 days.
|
|
193
|
+
- name: past_return_window
|
|
194
|
+
inputs: Can I return an unopened item after 45 days?
|
|
195
|
+
expected_output: No, the 30-day return window has passed.
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Run the evaluation:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
report = SupportEvaluation.run
|
|
202
|
+
report.passed?
|
|
203
|
+
report.cost.total
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
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/).
|
|
207
|
+
|
|
153
208
|
## A complete AI framework for Ruby
|
|
154
209
|
|
|
155
|
-
Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking and Rails integration to bring them into your app.
|
|
210
|
+
Agents, evaluations, workflows, RAG, images, audio, and video. Built in, with usage tracking and Rails integration to bring them into your app.
|
|
156
211
|
|
|
157
212
|
* **Chat:** Conversational AI with `RubyLLM.chat`
|
|
158
213
|
* **Vision:** Analyze images and videos
|
|
@@ -164,11 +219,13 @@ Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking
|
|
|
164
219
|
* **Embeddings:** Generate embeddings with `RubyLLM.embed`
|
|
165
220
|
* **Reranking:** Order retrieval candidates by relevance with `RubyLLM.rerank`
|
|
166
221
|
* **Moderation:** Content flags, categories, and scores with `RubyLLM.moderate`
|
|
222
|
+
* **Judgments:** Probabilities, choices, and scores with `RubyLLM::Judge` and `RubyLLM.judge`
|
|
167
223
|
* **Tools:** Let AI call your Ruby methods
|
|
168
224
|
* **Tool approval:** Park a run until a human approves with `requires_approval`
|
|
169
225
|
* **The agentic loop:** Drive it yourself with `ask_later`, `step`, and `complete?`
|
|
170
226
|
* **Provider tools:** Web search, code execution, and MCP connectors with `with_provider_tools`
|
|
171
227
|
* **Agents:** Reusable assistants with `RubyLLM::Agent`
|
|
228
|
+
* **Evaluations (main):** Reusable datasets, model criteria, and Ruby assertions with [`RubyLLM::Evaluation`](https://rubyllm.com/next/evaluations/)
|
|
172
229
|
* **Prompt templates:** ERB prompts in `app/prompts`, rendered with `RubyLLM.render_prompt`
|
|
173
230
|
* **Workflows:** Correlate multi-agent runs in your telemetry with `RubyLLM.workflow`
|
|
174
231
|
* **Structured output:** Define a Ruby schema and read the result with `response.parsed`
|
|
@@ -185,14 +242,14 @@ Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking
|
|
|
185
242
|
* **Batches:** Provider-side batch processing with provider-specific discounts via `RubyLLM.batch`
|
|
186
243
|
* **Compaction:** Let providers condense long conversations with `with_compaction`
|
|
187
244
|
* **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
|
|
245
|
+
* **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
246
|
|
|
190
247
|
## Installation
|
|
191
248
|
|
|
192
|
-
Install RubyLLM 2.
|
|
249
|
+
Install RubyLLM 2.1:
|
|
193
250
|
|
|
194
251
|
```bash
|
|
195
|
-
bundle add ruby_llm --version 2.
|
|
252
|
+
bundle add ruby_llm --version 2.1.0
|
|
196
253
|
```
|
|
197
254
|
|
|
198
255
|
Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
|
|
@@ -205,7 +262,7 @@ RubyLLM.configure do |config|
|
|
|
205
262
|
end
|
|
206
263
|
```
|
|
207
264
|
|
|
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.
|
|
265
|
+
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
266
|
|
|
210
267
|
## Feels at home in Rails
|
|
211
268
|
|
|
@@ -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
|