ruby_llm 1.16.0 → 2.0.0.rc2
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/.rdoc_options +25 -0
- data/README.md +85 -32
- data/exe/ruby_llm +8 -0
- data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +0 -1
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +3 -43
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +11 -3
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +4 -4
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +6 -4
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +5 -6
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +5 -5
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +6 -4
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +4 -6
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +5 -6
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -3
- data/lib/generators/ruby_llm/generator_helpers.rb +106 -62
- data/lib/generators/ruby_llm/install/install_generator.rb +3 -11
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +2 -0
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +14 -8
- data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +117 -0
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +0 -8
- data/lib/generators/ruby_llm/provider/cli.rb +175 -0
- data/lib/generators/ruby_llm/provider/scaffold.rb +323 -0
- data/lib/generators/ruby_llm/provider/templates/core/provider.rb.erb +37 -0
- data/lib/generators/ruby_llm/provider/templates/core/provider_spec.rb.erb +34 -0
- data/lib/generators/ruby_llm/provider/templates/gem/archspec.rb.erb +14 -0
- data/lib/generators/ruby_llm/provider/templates/gem/bin/console.erb +15 -0
- data/lib/generators/ruby_llm/provider/templates/gem/bin/setup.erb +5 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_schema_spec.rb.erb +30 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_spec.rb.erb +35 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_streaming_spec.rb.erb +22 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_tools_spec.rb.erb +30 -0
- data/lib/generators/ruby_llm/provider/templates/gem/ci.yml.erb +32 -0
- data/lib/generators/ruby_llm/provider/templates/gem/embedding_spec.rb.erb +49 -0
- data/lib/generators/ruby_llm/provider/templates/gem/env.erb +2 -0
- data/lib/generators/ruby_llm/provider/templates/gem/fixtures_gitkeep.erb +1 -0
- data/lib/generators/ruby_llm/provider/templates/gem/flayignore.erb +1 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gemfile.erb +23 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gemspec.erb +28 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gitignore.erb +7 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gitleaks.yml.erb +22 -0
- data/lib/generators/ruby_llm/provider/templates/gem/image_spec.rb.erb +23 -0
- data/lib/generators/ruby_llm/provider/templates/gem/license.erb +21 -0
- data/lib/generators/ruby_llm/provider/templates/gem/models.rb.erb +19 -0
- data/lib/generators/ruby_llm/provider/templates/gem/models_spec.rb.erb +17 -0
- data/lib/generators/ruby_llm/provider/templates/gem/moderation_spec.rb.erb +22 -0
- data/lib/generators/ruby_llm/provider/templates/gem/overcommit.yml.erb +31 -0
- data/lib/generators/ruby_llm/provider/templates/gem/provider.rb.erb +52 -0
- data/lib/generators/ruby_llm/provider/templates/gem/provider_spec.rb.erb +38 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rakefile.erb +38 -0
- data/lib/generators/ruby_llm/provider/templates/gem/readme.md.erb +50 -0
- data/lib/generators/ruby_llm/provider/templates/gem/release.yml.erb +36 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rerank_spec.rb.erb +23 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rspec.erb +2 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rubocop.yml.erb +29 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rubyllm_configuration.rb.erb +14 -0
- data/lib/generators/ruby_llm/provider/templates/gem/spec_helper.rb.erb +26 -0
- data/lib/generators/ruby_llm/provider/templates/gem/speech_spec.rb.erb +25 -0
- data/lib/generators/ruby_llm/provider/templates/gem/vcr_configuration.rb.erb +16 -0
- data/lib/generators/ruby_llm/provider/templates/gem/video_spec.rb.erb +27 -0
- data/lib/generators/ruby_llm/schema/schema_generator.rb +5 -1
- data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_call.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_result.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +3 -3
- data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +7 -12
- data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +5 -2
- data/lib/generators/ruby_llm/tool/tool_generator.rb +25 -59
- data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +461 -0
- data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +101 -0
- data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +215 -0
- data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +660 -0
- data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +222 -0
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +13 -0
- data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +167 -0
- data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +344 -0
- data/lib/ruby_llm/accounting/usage.rb +245 -0
- data/lib/ruby_llm/active_record/acts_as.rb +94 -111
- data/lib/ruby_llm/active_record/attachment_helpers.rb +186 -0
- data/lib/ruby_llm/active_record/batch.rb +97 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +828 -305
- data/lib/ruby_llm/active_record/message_methods.rb +113 -136
- data/lib/ruby_llm/active_record/model.rb +135 -0
- data/lib/ruby_llm/active_record/payload_helpers.rb +1 -2
- data/lib/ruby_llm/active_record/tool_call.rb +33 -0
- data/lib/ruby_llm/active_record/usage.rb +61 -0
- data/lib/ruby_llm/agent.rb +1065 -151
- data/lib/ruby_llm/aliases.json +268 -100
- data/lib/ruby_llm/attachment.rb +187 -48
- data/lib/ruby_llm/batch.rb +432 -0
- data/lib/ruby_llm/cached_content.rb +112 -0
- data/lib/ruby_llm/chat/tool_concurrency.rb +111 -0
- data/lib/ruby_llm/chat.rb +1127 -198
- data/lib/ruby_llm/chunk.rb +10 -0
- data/lib/ruby_llm/citation.rb +105 -0
- data/lib/ruby_llm/configuration.rb +261 -24
- data/lib/ruby_llm/context.rb +128 -6
- data/lib/ruby_llm/cost.rb +217 -80
- data/lib/ruby_llm/downloaded_file.rb +33 -0
- data/lib/ruby_llm/embedding.rb +121 -17
- data/lib/ruby_llm/embedding_request.rb +53 -0
- data/lib/ruby_llm/error.rb +159 -23
- data/lib/ruby_llm/fallback.rb +133 -0
- data/lib/ruby_llm/files/mime_type.rb +97 -0
- data/lib/ruby_llm/image.rb +153 -32
- data/lib/ruby_llm/message.rb +233 -54
- data/lib/ruby_llm/model/modalities.rb +17 -4
- data/lib/ruby_llm/model/pricing.rb +24 -5
- data/lib/ruby_llm/model/pricing_category.rb +103 -14
- data/lib/ruby_llm/model/pricing_tier.rb +55 -15
- data/lib/ruby_llm/model.rb +244 -2
- data/lib/ruby_llm/models/aliases.rb +41 -0
- data/lib/ruby_llm/models/registry.rb +165 -0
- data/lib/ruby_llm/models/schema.rb +99 -0
- data/lib/ruby_llm/models.json +73261 -33733
- data/lib/ruby_llm/models.rb +477 -215
- data/lib/ruby_llm/moderation.rb +139 -26
- data/lib/ruby_llm/ocr.rb +112 -0
- data/lib/ruby_llm/prompt.rb +79 -0
- data/lib/ruby_llm/protocol/binary_streaming.rb +65 -0
- data/lib/ruby_llm/protocol/stream_accumulator.rb +214 -0
- data/lib/ruby_llm/protocol/streaming.rb +230 -0
- data/lib/ruby_llm/protocol.rb +662 -0
- data/lib/ruby_llm/protocols/anthropic/batches.rb +73 -0
- data/lib/ruby_llm/protocols/anthropic/chat.rb +548 -0
- data/lib/ruby_llm/protocols/anthropic/embeddings.rb +14 -0
- data/lib/ruby_llm/protocols/anthropic/files.rb +38 -0
- data/lib/ruby_llm/protocols/anthropic/media.rb +141 -0
- data/lib/ruby_llm/protocols/anthropic/models.rb +129 -0
- data/lib/ruby_llm/protocols/anthropic/streaming.rb +167 -0
- data/lib/ruby_llm/{providers → protocols}/anthropic/tools.rb +24 -41
- data/lib/ruby_llm/protocols/anthropic.rb +101 -0
- data/lib/ruby_llm/protocols/azure/files.rb +16 -0
- data/lib/ruby_llm/protocols/bedrock/async_videos.rb +109 -0
- data/lib/ruby_llm/protocols/bedrock/batches.rb +129 -0
- data/lib/ruby_llm/protocols/bedrock/files.rb +110 -0
- data/lib/ruby_llm/protocols/bedrock/guardrails.rb +122 -0
- data/lib/ruby_llm/protocols/bedrock/rerank.rb +95 -0
- data/lib/ruby_llm/protocols/chat_completions/batches.rb +32 -0
- data/lib/ruby_llm/protocols/chat_completions/chat.rb +493 -0
- data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +35 -0
- data/lib/ruby_llm/protocols/chat_completions/embeddings.rb +60 -0
- data/lib/ruby_llm/protocols/chat_completions/images.rb +127 -0
- data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/media.rb +30 -17
- data/lib/ruby_llm/protocols/chat_completions/models.rb +39 -0
- data/lib/ruby_llm/protocols/chat_completions/moderation.rb +52 -0
- data/lib/ruby_llm/protocols/chat_completions/rerank.rb +56 -0
- data/lib/ruby_llm/protocols/chat_completions/speech.rb +40 -0
- data/lib/ruby_llm/protocols/chat_completions/streaming.rb +69 -0
- data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/tools.rb +15 -17
- data/lib/ruby_llm/protocols/chat_completions/transcription.rb +150 -0
- data/lib/ruby_llm/protocols/chat_completions.rb +21 -0
- data/lib/ruby_llm/protocols/cohere/batch_requests.rb +75 -0
- data/lib/ruby_llm/protocols/cohere/batches.rb +98 -0
- data/lib/ruby_llm/protocols/cohere/chat.rb +227 -0
- data/lib/ruby_llm/protocols/cohere/datasets.rb +102 -0
- data/lib/ruby_llm/protocols/cohere/embeddings.rb +68 -0
- data/lib/ruby_llm/protocols/cohere/media.rb +77 -0
- data/lib/ruby_llm/protocols/cohere/models.rb +92 -0
- data/lib/ruby_llm/protocols/cohere/ocr.rb +63 -0
- data/lib/ruby_llm/protocols/cohere/rerank.rb +52 -0
- data/lib/ruby_llm/protocols/cohere/streaming.rb +105 -0
- data/lib/ruby_llm/protocols/cohere/tokenization.rb +22 -0
- data/lib/ruby_llm/protocols/cohere/tools.rb +132 -0
- data/lib/ruby_llm/protocols/cohere/transcription.rb +41 -0
- data/lib/ruby_llm/protocols/cohere.rb +21 -0
- data/lib/ruby_llm/protocols/converse/batches.rb +55 -0
- data/lib/ruby_llm/protocols/converse/chat.rb +695 -0
- data/lib/ruby_llm/protocols/converse/media.rb +178 -0
- data/lib/ruby_llm/protocols/converse/streaming.rb +432 -0
- data/lib/ruby_llm/protocols/converse/thinking_stream.rb +56 -0
- data/lib/ruby_llm/protocols/converse.rb +54 -0
- data/lib/ruby_llm/protocols/deepgram/models.rb +74 -0
- data/lib/ruby_llm/protocols/deepgram/speech.rb +93 -0
- data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +89 -0
- data/lib/ruby_llm/protocols/deepgram/transcription.rb +96 -0
- data/lib/ruby_llm/protocols/deepgram.rb +19 -0
- data/lib/ruby_llm/protocols/deepseek/files.rb +31 -0
- data/lib/ruby_llm/protocols/elevenlabs/assets.rb +36 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +74 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows/media.rb +42 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +93 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows.rb +14 -0
- data/lib/ruby_llm/protocols/elevenlabs/models.rb +64 -0
- data/lib/ruby_llm/protocols/elevenlabs/speech.rb +67 -0
- data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +127 -0
- data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +61 -0
- data/lib/ruby_llm/protocols/elevenlabs.rb +15 -0
- data/lib/ruby_llm/protocols/files.rb +119 -0
- data/lib/ruby_llm/protocols/gemini/batches.rb +162 -0
- data/lib/ruby_llm/protocols/gemini/caches.rb +59 -0
- data/lib/ruby_llm/protocols/gemini/chat.rb +453 -0
- data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +86 -0
- data/lib/ruby_llm/protocols/gemini/embeddings.rb +70 -0
- data/lib/ruby_llm/protocols/gemini/file_transcription.rb +32 -0
- data/lib/ruby_llm/protocols/gemini/files.rb +115 -0
- data/lib/ruby_llm/protocols/gemini/images.rb +183 -0
- data/lib/ruby_llm/protocols/gemini/live_transcription.rb +140 -0
- data/lib/ruby_llm/{providers → protocols}/gemini/media.rb +17 -11
- data/lib/ruby_llm/protocols/gemini/models.rb +71 -0
- data/lib/ruby_llm/protocols/gemini/speech.rb +56 -0
- data/lib/ruby_llm/protocols/gemini/streaming.rb +96 -0
- data/lib/ruby_llm/protocols/gemini/tools.rb +157 -0
- data/lib/ruby_llm/{providers → protocols}/gemini/transcription.rb +22 -22
- data/lib/ruby_llm/protocols/gemini/videos.rb +103 -0
- data/lib/ruby_llm/protocols/gemini.rb +35 -0
- data/lib/ruby_llm/protocols/gpustack/responses.rb +111 -0
- data/lib/ruby_llm/protocols/gpustack/tokenization.rb +22 -0
- data/lib/ruby_llm/protocols/gpustack/videos.rb +96 -0
- data/lib/ruby_llm/protocols/interactions/chat.rb +145 -0
- data/lib/ruby_llm/protocols/interactions/content.rb +90 -0
- data/lib/ruby_llm/protocols/interactions/streaming.rb +91 -0
- data/lib/ruby_llm/protocols/interactions/tools.rb +51 -0
- data/lib/ruby_llm/protocols/interactions/transcription.rb +58 -0
- data/lib/ruby_llm/protocols/interactions.rb +29 -0
- data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +51 -0
- data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +111 -0
- data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +50 -0
- data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +103 -0
- data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +33 -0
- data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +44 -0
- data/lib/ruby_llm/protocols/invoke_model.rb +57 -0
- data/lib/ruby_llm/protocols/mistral/content.rb +49 -0
- data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +160 -0
- data/lib/ruby_llm/protocols/mistral/conversations/images.rb +43 -0
- data/lib/ruby_llm/protocols/mistral/conversations/streaming.rb +83 -0
- data/lib/ruby_llm/protocols/mistral/conversations.rb +30 -0
- data/lib/ruby_llm/protocols/mistral/files.rb +36 -0
- data/lib/ruby_llm/protocols/mistral/multi_completion.rb +160 -0
- data/lib/ruby_llm/protocols/openai/batches.rb +126 -0
- data/lib/ruby_llm/protocols/openai/files.rb +42 -0
- data/lib/ruby_llm/protocols/openrouter/batches.rb +147 -0
- data/lib/ruby_llm/protocols/openrouter/files.rb +24 -0
- data/lib/ruby_llm/protocols/openrouter/responses.rb +53 -0
- data/lib/ruby_llm/protocols/openrouter/transcription.rb +51 -0
- data/lib/ruby_llm/protocols/perplexity/files.rb +48 -0
- data/lib/ruby_llm/protocols/perplexity/router.rb +59 -0
- data/lib/ruby_llm/protocols/responses/approvals.rb +32 -0
- data/lib/ruby_llm/protocols/responses/batches.rb +32 -0
- data/lib/ruby_llm/protocols/responses/chat.rb +476 -0
- data/lib/ruby_llm/protocols/responses/compaction.rb +29 -0
- data/lib/ruby_llm/protocols/responses/media.rb +61 -0
- data/lib/ruby_llm/protocols/responses/streaming.rb +117 -0
- data/lib/ruby_llm/protocols/responses/token_counting.rb +26 -0
- data/lib/ruby_llm/protocols/responses/tools.rb +39 -0
- data/lib/ruby_llm/protocols/responses.rb +35 -0
- data/lib/ruby_llm/protocols/vertexai/batch_prediction.rb +155 -0
- data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +85 -0
- data/lib/ruby_llm/protocols/vertexai/embedding_prediction/results.rb +74 -0
- data/lib/ruby_llm/protocols/vertexai/embedding_prediction.rb +56 -0
- data/lib/ruby_llm/protocols/vertexai/files.rb +101 -0
- data/lib/ruby_llm/protocols/vertexai/ranking.rb +69 -0
- data/lib/ruby_llm/protocols/vertexai/research.rb +193 -0
- data/lib/ruby_llm/protocols/xai/files.rb +30 -0
- data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +120 -0
- data/lib/ruby_llm/protocols/xai/tokenization.rb +23 -0
- data/lib/ruby_llm/provider.rb +560 -128
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +5 -7
- data/lib/ruby_llm/providers/anthropic.rb +4 -6
- data/lib/ruby_llm/providers/azure/audio.rb +18 -0
- data/lib/ruby_llm/providers/azure/capabilities.rb +16 -0
- data/lib/ruby_llm/providers/azure/chat.rb +2 -9
- data/lib/ruby_llm/providers/azure/chat_completions/batches.rb +29 -0
- data/lib/ruby_llm/providers/azure/chat_completions.rb +80 -0
- data/lib/ruby_llm/providers/azure/cohere.rb +33 -0
- data/lib/ruby_llm/providers/azure/embeddings.rb +3 -2
- data/lib/ruby_llm/providers/azure/images.rb +22 -0
- data/lib/ruby_llm/providers/azure/media.rb +5 -14
- data/lib/ruby_llm/providers/azure/models.rb +35 -0
- data/lib/ruby_llm/providers/azure/responses.rb +26 -0
- data/lib/ruby_llm/providers/azure/videos.rb +64 -0
- data/lib/ruby_llm/providers/azure.rb +77 -78
- data/lib/ruby_llm/providers/bedrock/auth.rb +60 -41
- data/lib/ruby_llm/providers/bedrock/capabilities.rb +18 -0
- data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +39 -0
- data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +23 -0
- data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +24 -0
- data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +96 -0
- data/lib/ruby_llm/providers/bedrock/mantle.rb +57 -0
- data/lib/ruby_llm/providers/bedrock/models.rb +193 -41
- data/lib/ruby_llm/providers/bedrock.rb +216 -45
- data/lib/ruby_llm/providers/cohere.rb +31 -0
- data/lib/ruby_llm/providers/deepgram.rb +37 -0
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +4 -51
- data/lib/ruby_llm/providers/deepseek/chat.rb +49 -2
- data/lib/ruby_llm/providers/deepseek/responses.rb +68 -0
- data/lib/ruby_llm/providers/deepseek.rb +9 -2
- data/lib/ruby_llm/providers/elevenlabs.rb +35 -0
- data/lib/ruby_llm/providers/gemini/capabilities.rb +8 -107
- data/lib/ruby_llm/providers/gemini.rb +15 -8
- data/lib/ruby_llm/providers/gpustack/chat.rb +2 -16
- data/lib/ruby_llm/providers/gpustack/embeddings.rb +28 -0
- data/lib/ruby_llm/providers/gpustack/media.rb +17 -16
- data/lib/ruby_llm/providers/gpustack/models.rb +72 -62
- data/lib/ruby_llm/providers/gpustack/speech.rb +15 -0
- data/lib/ruby_llm/providers/gpustack/transcription.rb +29 -0
- data/lib/ruby_llm/providers/gpustack.rb +35 -11
- data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -155
- data/lib/ruby_llm/providers/mistral/chat.rb +37 -61
- data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +120 -0
- data/lib/ruby_llm/providers/mistral/chat_completions.rb +21 -0
- data/lib/ruby_llm/providers/mistral/conversations.rb +12 -0
- data/lib/ruby_llm/providers/mistral/embeddings.rb +6 -4
- data/lib/ruby_llm/providers/mistral/media.rb +6 -18
- data/lib/ruby_llm/providers/mistral/models.rb +57 -23
- data/lib/ruby_llm/providers/mistral/ocr.rb +47 -0
- data/lib/ruby_llm/providers/mistral/speech.rb +51 -0
- data/lib/ruby_llm/providers/mistral/transcription.rb +62 -0
- data/lib/ruby_llm/providers/mistral.rb +16 -4
- data/lib/ruby_llm/providers/ollama/chat.rb +7 -13
- data/lib/ruby_llm/providers/ollama/media.rb +6 -15
- data/lib/ruby_llm/providers/ollama/models.rb +50 -9
- data/lib/ruby_llm/providers/ollama.rb +9 -8
- data/lib/ruby_llm/providers/ollama_cloud/models.rb +14 -0
- data/lib/ruby_llm/providers/ollama_cloud.rb +40 -0
- data/lib/ruby_llm/providers/openai/capabilities.rb +54 -259
- data/lib/ruby_llm/providers/openai/models.rb +23 -23
- data/lib/ruby_llm/providers/openai/responses.rb +13 -0
- data/lib/ruby_llm/providers/openai.rb +92 -11
- data/lib/ruby_llm/providers/openrouter/chat.rb +130 -108
- data/lib/ruby_llm/providers/openrouter/embeddings.rb +51 -0
- data/lib/ruby_llm/providers/openrouter/images.rb +44 -43
- data/lib/ruby_llm/providers/openrouter/media.rb +34 -0
- data/lib/ruby_llm/providers/openrouter/models.rb +50 -11
- data/lib/ruby_llm/providers/openrouter/speech.rb +32 -0
- data/lib/ruby_llm/providers/openrouter/streaming.rb +31 -38
- data/lib/ruby_llm/providers/openrouter/videos.rb +81 -0
- data/lib/ruby_llm/providers/openrouter.rb +78 -20
- data/lib/ruby_llm/providers/perplexity/chat.rb +2 -9
- data/lib/ruby_llm/providers/perplexity/embeddings.rb +32 -0
- data/lib/ruby_llm/providers/perplexity/media.rb +5 -21
- data/lib/ruby_llm/providers/perplexity/models.rb +80 -13
- data/lib/ruby_llm/providers/perplexity.rb +28 -20
- data/lib/ruby_llm/providers/vertexai/anthropic/batches.rb +52 -0
- data/lib/ruby_llm/providers/vertexai/anthropic.rb +34 -0
- data/lib/ruby_llm/providers/vertexai/capabilities.rb +19 -0
- data/lib/ruby_llm/providers/vertexai/chat_completions/batches.rb +54 -0
- data/lib/ruby_llm/providers/vertexai/chat_completions.rb +15 -0
- data/lib/ruby_llm/providers/vertexai/embed_content.rb +42 -0
- data/lib/ruby_llm/providers/vertexai/embeddings.rb +22 -7
- data/lib/ruby_llm/providers/vertexai/gemini/batches.rb +42 -0
- data/lib/ruby_llm/providers/vertexai/gemini.rb +69 -0
- data/lib/ruby_llm/providers/vertexai/live_transcription.rb +24 -0
- data/lib/ruby_llm/providers/vertexai/mistral.rb +28 -0
- data/lib/ruby_llm/providers/vertexai/models.rb +145 -43
- data/lib/ruby_llm/providers/vertexai/transcription.rb +49 -4
- data/lib/ruby_llm/providers/vertexai/videos.rb +61 -0
- data/lib/ruby_llm/providers/vertexai.rb +160 -17
- data/lib/ruby_llm/providers/xai/capabilities.rb +18 -0
- data/lib/ruby_llm/providers/xai/chat.rb +3 -2
- data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +108 -0
- data/lib/ruby_llm/providers/xai/chat_completions.rb +19 -0
- data/lib/ruby_llm/providers/xai/images.rb +91 -0
- data/lib/ruby_llm/providers/xai/models.rb +32 -36
- data/lib/ruby_llm/providers/xai/reported_cost.rb +18 -0
- data/lib/ruby_llm/providers/xai/responses.rb +52 -0
- data/lib/ruby_llm/providers/xai/speech.rb +45 -0
- data/lib/ruby_llm/providers/xai/transcription.rb +48 -0
- data/lib/ruby_llm/providers/xai/videos.rb +87 -0
- data/lib/ruby_llm/providers/xai.rb +15 -5
- data/lib/ruby_llm/railtie.rb +7 -16
- data/lib/ruby_llm/rerank.rb +105 -0
- data/lib/ruby_llm/research_job.rb +241 -0
- data/lib/ruby_llm/search_results.rb +68 -0
- data/lib/ruby_llm/server_tool_call.rb +73 -0
- data/lib/ruby_llm/speech.rb +159 -0
- data/lib/ruby_llm/speech_chunk.rb +33 -0
- data/lib/ruby_llm/support/deprecator.rb +22 -0
- data/lib/ruby_llm/support/inspectable.rb +49 -0
- data/lib/ruby_llm/support/instrumentation.rb +41 -0
- data/lib/ruby_llm/support/utils.rb +147 -0
- data/lib/ruby_llm/thinking.rb +127 -20
- data/lib/ruby_llm/tokenization.rb +59 -0
- data/lib/ruby_llm/tokens.rb +103 -33
- data/lib/ruby_llm/tool.rb +266 -91
- data/lib/ruby_llm/tool_call.rb +36 -3
- data/lib/ruby_llm/tools/server_tools.rb +109 -0
- data/lib/ruby_llm/transcription/wav_audio.rb +62 -0
- data/lib/ruby_llm/transcription.rb +138 -13
- data/lib/ruby_llm/transcription_chunk.rb +68 -0
- data/lib/ruby_llm/transport/connection.rb +193 -0
- data/lib/ruby_llm/transport/error_middleware.rb +131 -0
- data/lib/ruby_llm/transport/usage_middleware.rb +28 -0
- data/lib/ruby_llm/transport/websocket_connection.rb +220 -0
- data/lib/ruby_llm/uploaded_file.rb +144 -0
- data/lib/ruby_llm/version.rb +2 -1
- data/lib/ruby_llm/video.rb +136 -0
- data/lib/ruby_llm/video_job.rb +150 -0
- data/lib/ruby_llm/workflow.rb +91 -0
- data/lib/ruby_llm.rb +380 -6
- data/lib/tasks/ruby_llm.rake +21 -16
- data/skills/rubyllm/SKILL.md +81 -0
- data/skills/rubyllm/agents/openai.yaml +4 -0
- metadata +339 -97
- data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +0 -9
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +0 -39
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -21
- data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +0 -3
- data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +0 -3
- data/lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt +0 -19
- data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +0 -50
- data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +0 -7
- data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +0 -49
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +0 -145
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +0 -122
- data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +0 -15
- data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +0 -49
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +0 -597
- data/lib/ruby_llm/active_record/model_methods.rb +0 -82
- data/lib/ruby_llm/active_record/tool_call_methods.rb +0 -18
- data/lib/ruby_llm/aliases.rb +0 -41
- data/lib/ruby_llm/connection.rb +0 -159
- data/lib/ruby_llm/content.rb +0 -91
- data/lib/ruby_llm/deprecator.rb +0 -24
- data/lib/ruby_llm/error_middleware.rb +0 -81
- data/lib/ruby_llm/instrumentation.rb +0 -36
- data/lib/ruby_llm/mime_type.rb +0 -96
- data/lib/ruby_llm/model/info.rb +0 -164
- data/lib/ruby_llm/model_registry.rb +0 -39
- data/lib/ruby_llm/models_schema.json +0 -171
- data/lib/ruby_llm/providers/anthropic/chat.rb +0 -291
- data/lib/ruby_llm/providers/anthropic/content.rb +0 -44
- data/lib/ruby_llm/providers/anthropic/embeddings.rb +0 -20
- data/lib/ruby_llm/providers/anthropic/media.rb +0 -92
- data/lib/ruby_llm/providers/anthropic/models.rb +0 -59
- data/lib/ruby_llm/providers/anthropic/streaming.rb +0 -71
- data/lib/ruby_llm/providers/bedrock/chat.rb +0 -405
- data/lib/ruby_llm/providers/bedrock/media.rb +0 -108
- data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -328
- data/lib/ruby_llm/providers/gemini/chat.rb +0 -542
- data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -37
- data/lib/ruby_llm/providers/gemini/images.rb +0 -47
- data/lib/ruby_llm/providers/gemini/models.rb +0 -38
- data/lib/ruby_llm/providers/gemini/streaming.rb +0 -98
- data/lib/ruby_llm/providers/gemini/tools.rb +0 -234
- data/lib/ruby_llm/providers/gpustack/capabilities.rb +0 -20
- data/lib/ruby_llm/providers/ollama/capabilities.rb +0 -20
- data/lib/ruby_llm/providers/openai/chat.rb +0 -236
- data/lib/ruby_llm/providers/openai/embeddings.rb +0 -33
- data/lib/ruby_llm/providers/openai/images.rb +0 -90
- data/lib/ruby_llm/providers/openai/moderation.rb +0 -34
- data/lib/ruby_llm/providers/openai/streaming.rb +0 -55
- data/lib/ruby_llm/providers/openai/temperature.rb +0 -28
- data/lib/ruby_llm/providers/openai/transcription.rb +0 -71
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -72
- data/lib/ruby_llm/providers/vertexai/chat.rb +0 -14
- data/lib/ruby_llm/providers/vertexai/streaming.rb +0 -14
- data/lib/ruby_llm/stream_accumulator.rb +0 -218
- data/lib/ruby_llm/streaming.rb +0 -179
- data/lib/ruby_llm/tool_concurrency.rb +0 -105
- data/lib/ruby_llm/utils.rb +0 -130
- data/lib/tasks/models.rake +0 -593
- data/lib/tasks/release.rake +0 -94
- data/lib/tasks/vcr.rake +0 -124
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
# Adds workflow and step context to RubyLLM instrumentation while leaving
|
|
5
|
+
# orchestration in ordinary Ruby code.
|
|
6
|
+
#
|
|
7
|
+
# Create workflows through RubyLLM.workflow rather than constructing this
|
|
8
|
+
# class directly:
|
|
9
|
+
#
|
|
10
|
+
# RubyLLM.workflow("Write article", id: "article-42") do |workflow|
|
|
11
|
+
# notes = workflow.step("Research") { ResearchAgent.new.ask(topic).content }
|
|
12
|
+
# workflow.step("Draft") { WriterAgent.new.ask(notes).content }
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
class Workflow
|
|
16
|
+
# Returns the workflow identifier shared by its instrumentation events.
|
|
17
|
+
attr_reader :id
|
|
18
|
+
|
|
19
|
+
# Returns the workflow name.
|
|
20
|
+
attr_reader :name
|
|
21
|
+
|
|
22
|
+
def initialize(name, id: nil, metadata: nil, config: RubyLLM.config) # :nodoc:
|
|
23
|
+
@name = normalize(name, 'name')
|
|
24
|
+
@id = normalize(id || SecureRandom.uuid, 'id')
|
|
25
|
+
@metadata = metadata
|
|
26
|
+
@config = config
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def run # :nodoc:
|
|
30
|
+
raise ArgumentError, 'a workflow block is required' unless block_given?
|
|
31
|
+
|
|
32
|
+
link_parent
|
|
33
|
+
Support::Instrumentation.with_workflow(workflow_context) do
|
|
34
|
+
RubyLLM.instrument('workflow.ruby_llm', config: @config) { yield self }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Runs a named section of Ruby code and adds its identity to every RubyLLM
|
|
39
|
+
# event emitted by the block. An ID is generated when one is not supplied.
|
|
40
|
+
# Steps can contain regular Ruby control flow and may be nested.
|
|
41
|
+
# Returns the block's result.
|
|
42
|
+
def step(name, id: nil, &block)
|
|
43
|
+
raise ArgumentError, 'a workflow step block is required' unless block
|
|
44
|
+
|
|
45
|
+
step_context = workflow_context.merge(
|
|
46
|
+
workflow_step_id: normalize(id || SecureRandom.uuid, 'step id'),
|
|
47
|
+
workflow_step_name: normalize(name, 'step name')
|
|
48
|
+
)
|
|
49
|
+
parent_id = current_step_id
|
|
50
|
+
step_context[:workflow_step_parent_id] = parent_id if parent_id
|
|
51
|
+
|
|
52
|
+
Support::Instrumentation.with_workflow(step_context.freeze) do
|
|
53
|
+
RubyLLM.instrument('workflow_step.ruby_llm', config: @config) { block.call }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def workflow_context
|
|
60
|
+
@workflow_context ||= begin
|
|
61
|
+
context = { workflow_id: id, workflow_name: name }
|
|
62
|
+
context[:workflow_metadata] = @metadata unless @metadata.nil?
|
|
63
|
+
context.freeze
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def link_parent
|
|
68
|
+
current = Support::Instrumentation.current_workflow
|
|
69
|
+
context = workflow_context.dup
|
|
70
|
+
context.delete(:workflow_parent_id)
|
|
71
|
+
context.delete(:workflow_parent_step_id)
|
|
72
|
+
if current && current[:workflow_id] != id
|
|
73
|
+
context[:workflow_parent_id] = current[:workflow_id]
|
|
74
|
+
context[:workflow_parent_step_id] = current[:workflow_step_id] if current[:workflow_step_id]
|
|
75
|
+
end
|
|
76
|
+
@workflow_context = context.freeze
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def current_step_id
|
|
80
|
+
current = Support::Instrumentation.current_workflow
|
|
81
|
+
current[:workflow_step_id] if current && current[:workflow_id] == id
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def normalize(value, attribute)
|
|
85
|
+
value = value.to_s
|
|
86
|
+
raise ArgumentError, "workflow #{attribute} cannot be empty" if value.empty?
|
|
87
|
+
|
|
88
|
+
value
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/ruby_llm.rb
CHANGED
|
@@ -8,6 +8,7 @@ require 'faraday/retry'
|
|
|
8
8
|
require 'json'
|
|
9
9
|
require 'logger'
|
|
10
10
|
require 'marcel'
|
|
11
|
+
require 'schematist'
|
|
11
12
|
require 'securerandom'
|
|
12
13
|
require 'date'
|
|
13
14
|
require 'time'
|
|
@@ -20,10 +21,13 @@ loader.inflector.inflect(
|
|
|
20
21
|
'UI' => 'UI',
|
|
21
22
|
'api' => 'API',
|
|
22
23
|
'bedrock' => 'Bedrock',
|
|
24
|
+
'cli' => 'CLI',
|
|
23
25
|
'deepseek' => 'DeepSeek',
|
|
26
|
+
'elevenlabs' => 'ElevenLabs',
|
|
24
27
|
'gpustack' => 'GPUStack',
|
|
25
28
|
'llm' => 'LLM',
|
|
26
29
|
'mistral' => 'Mistral',
|
|
30
|
+
'ocr' => 'OCR',
|
|
27
31
|
'openai' => 'OpenAI',
|
|
28
32
|
'openrouter' => 'OpenRouter',
|
|
29
33
|
'pdf' => 'PDF',
|
|
@@ -38,60 +42,426 @@ loader.ignore("#{__dir__}/ruby_llm/active_record")
|
|
|
38
42
|
loader.ignore("#{__dir__}/ruby_llm/railtie.rb")
|
|
39
43
|
loader.setup
|
|
40
44
|
|
|
41
|
-
#
|
|
45
|
+
# RubyLLM is an AI framework for Ruby and Rails. Build conversations and
|
|
46
|
+
# agents, generate media, process documents, and work with model providers
|
|
47
|
+
# through one Ruby API. The guides at https://rubyllm.com/next/ introduce
|
|
48
|
+
# each feature; this reference documents its classes, arguments, and results.
|
|
49
|
+
#
|
|
50
|
+
# RubyLLM.configure do |config|
|
|
51
|
+
# config.openai_api_key = ENV['OPENAI_API_KEY']
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
# chat = RubyLLM.chat
|
|
55
|
+
# chat.ask "What is the capital of France?"
|
|
56
|
+
#
|
|
57
|
+
# == Conversations, tools, and agents
|
|
58
|
+
#
|
|
59
|
+
# RubyLLM.chat returns a Chat that holds the conversation. Chat#ask accepts
|
|
60
|
+
# text and attachments, runs tools as needed, and returns a Message. Give
|
|
61
|
+
# it a block to receive Chunk objects as the response streams:
|
|
62
|
+
#
|
|
63
|
+
# chat.ask("Summarize this report", with: "report.pdf") do |chunk|
|
|
64
|
+
# print chunk.content
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# Configure the request with chainable methods:
|
|
68
|
+
#
|
|
69
|
+
# - Chat#with_schema requests structured output; Message#parsed reads it.
|
|
70
|
+
# - Chat#with_thinking sets thinking effort, budget, or display preferences.
|
|
71
|
+
# - Chat#with_citations requests source citations, read through Message#citations.
|
|
72
|
+
# - Chat#with_fallbacks tries other models when a request fails.
|
|
73
|
+
# - Chat#with_caching enables prompt caching; Chat#cache_until_here marks
|
|
74
|
+
# a reusable prefix. Chat#with_compaction manages long conversations.
|
|
75
|
+
#
|
|
76
|
+
# Subclass Tool and implement +execute+ to give the model an application
|
|
77
|
+
# action. Tool.requires_approval pauses execution for a human decision;
|
|
78
|
+
# Chat#approve and Chat#deny record it. Chat#with_server_tools enables
|
|
79
|
+
# provider-executed tools such as web search, code execution, and remote MCP.
|
|
80
|
+
# Their calls appear as ServerToolCall values, with Citation values for sources.
|
|
81
|
+
#
|
|
82
|
+
# Agent defines a reusable configuration with model, instructions, tools,
|
|
83
|
+
# schema, and runtime inputs. Chat#ask_later, Chat#generate, Chat#run_tools,
|
|
84
|
+
# and Chat#step expose the conversation loop for jobs and application logic.
|
|
85
|
+
#
|
|
86
|
+
# == Images, video, and speech
|
|
87
|
+
#
|
|
88
|
+
# Individual operations do not require a chat. Image, Video, and Speech
|
|
89
|
+
# results share +save(path)+ and +to_blob+:
|
|
90
|
+
#
|
|
91
|
+
# RubyLLM.paint("A red panda coding Ruby, watercolor").save("panda.png")
|
|
92
|
+
# RubyLLM.animate("A paper boat sailing down a gutter").save("boat.mp4")
|
|
93
|
+
# RubyLLM.speak("Welcome to RubyLLM.").save("welcome.mp3")
|
|
94
|
+
#
|
|
95
|
+
# Image.paint accepts source images and masks for editing. Video.animate
|
|
96
|
+
# accepts reference media, video edits, and extensions on supported models.
|
|
97
|
+
# RubyLLM.animate waits for the clip; RubyLLM.animate_later returns a
|
|
98
|
+
# VideoJob that you can poll. Speech.speak also streams SpeechChunk objects
|
|
99
|
+
# while retaining the complete audio result.
|
|
100
|
+
#
|
|
101
|
+
# == Documents, audio, and retrieval
|
|
102
|
+
#
|
|
103
|
+
# transcript = RubyLLM.transcribe("meeting.wav")
|
|
104
|
+
# document = RubyLLM.ocr("report.pdf", pages: [0, 1])
|
|
105
|
+
# embedding = RubyLLM.embed("Ruby is a programmer's best friend")
|
|
106
|
+
#
|
|
107
|
+
# Transcription provides text, timestamps, and speaker information when
|
|
108
|
+
# the model reports them; streaming yields TranscriptionChunk objects.
|
|
109
|
+
# OCR returns document pages and combined markdown. Embedding returns
|
|
110
|
+
# vectors for text or supported media, and RubyLLM.rerank returns a Rerank
|
|
111
|
+
# whose results order documents by relevance. SearchResults lets a Tool
|
|
112
|
+
# return source documents that the model can cite.
|
|
113
|
+
#
|
|
114
|
+
# RubyLLM.upload returns an UploadedFile for reuse across requests.
|
|
115
|
+
# RubyLLM.download returns a DownloadedFile with the same saving interface:
|
|
116
|
+
#
|
|
117
|
+
# RubyLLM.download(file.id, provider: file.provider).save("report.pdf")
|
|
118
|
+
#
|
|
119
|
+
# == Tokenization, moderation, and research
|
|
120
|
+
#
|
|
121
|
+
# RubyLLM.count_tokens and Chat#count_tokens count a model request without
|
|
122
|
+
# generating a response. RubyLLM.tokenize returns plain-text token IDs and
|
|
123
|
+
# a count as a Tokenization, excluding chat formatting and attachments.
|
|
124
|
+
#
|
|
125
|
+
# result = RubyLLM.tokenize("Hello Ruby", model: "grok-4.3", provider: :xai)
|
|
126
|
+
# result.ids
|
|
127
|
+
# result.count
|
|
128
|
+
#
|
|
129
|
+
# RubyLLM.moderate screens text and images, returning Moderation results
|
|
130
|
+
# with categories, scores, and +flagged?+. RubyLLM.research runs a hosted
|
|
131
|
+
# research task and returns its report as a Message; RubyLLM.research_later
|
|
132
|
+
# returns a ResearchJob for polling and cancellation. Hosted agent identities
|
|
133
|
+
# are selected separately from model IDs.
|
|
134
|
+
#
|
|
135
|
+
# == Batches, usage, and configuration
|
|
136
|
+
#
|
|
137
|
+
# RubyLLM.batch submits staged chats or EmbeddingRequest objects for
|
|
138
|
+
# provider-side processing. Batch exposes progress, results, token usage,
|
|
139
|
+
# and cost. RubyLLM.cache creates a managed CachedContent resource for
|
|
140
|
+
# reuse with Chat#with_caching.
|
|
141
|
+
#
|
|
142
|
+
# Tokens and Cost report usage and pricing. Chat totals include retries
|
|
143
|
+
# and attempts that produced no message. Provider-reported costs take
|
|
144
|
+
# precedence over estimates; unknown usage and prices remain +nil+.
|
|
145
|
+
# RubyLLM.workflow groups instrumentation from ordinary Ruby code into
|
|
146
|
+
# named Workflow steps.
|
|
147
|
+
#
|
|
148
|
+
# RubyLLM.configure sets global Configuration; RubyLLM.context creates
|
|
149
|
+
# isolated settings for a request or tenant. Models finds, filters, and
|
|
150
|
+
# describes the model catalog. Provider supplies endpoints, authentication,
|
|
151
|
+
# and protocol selection; Protocol implements request and response formats.
|
|
152
|
+
# Error subclasses normalize provider failures.
|
|
153
|
+
#
|
|
154
|
+
# == Rails integration
|
|
155
|
+
#
|
|
156
|
+
# ActiveRecord::ActsAs adds +acts_as_chat+ and +acts_as_message+ to your
|
|
157
|
+
# application's models. ActiveRecord::ChatMethods and
|
|
158
|
+
# ActiveRecord::MessageMethods provide the conversation API with persistence,
|
|
159
|
+
# Active Storage attachments, and support for Hotwire streaming and jobs.
|
|
160
|
+
# Approvals and cancellation survive requests and processes.
|
|
161
|
+
#
|
|
162
|
+
# Your application owns chats and messages; RubyLLM owns usage, tool calls,
|
|
163
|
+
# models, and batches. Agent can create and reload your chat records through
|
|
164
|
+
# Agent.chat_model. Individual operations also work directly in Rails
|
|
165
|
+
# services and jobs.
|
|
42
166
|
module RubyLLM
|
|
43
167
|
class << self
|
|
44
|
-
def deprecator
|
|
45
|
-
@deprecator ||= Deprecator.new
|
|
168
|
+
def deprecator # :nodoc:
|
|
169
|
+
@deprecator ||= Support::Deprecator.new
|
|
46
170
|
end
|
|
47
171
|
|
|
48
|
-
def instrument(...)
|
|
49
|
-
Instrumentation.instrument(...)
|
|
172
|
+
def instrument(...) # :nodoc:
|
|
173
|
+
Support::Instrumentation.instrument(...)
|
|
50
174
|
end
|
|
51
175
|
|
|
176
|
+
# Returns a Context, an isolated set of configuration overrides.
|
|
177
|
+
# Duplicates the global configuration and yields the copy if a block is
|
|
178
|
+
# given. The context offers the same entry points as the top-level
|
|
179
|
+
# RubyLLM module (Context#chat, Context#embed, and so on) using its
|
|
180
|
+
# own configuration.
|
|
181
|
+
#
|
|
182
|
+
# context = RubyLLM.context do |config|
|
|
183
|
+
# config.openai_api_key = 'sk-customer-specific-key'
|
|
184
|
+
# end
|
|
185
|
+
# context.chat.ask "Hello"
|
|
186
|
+
#
|
|
52
187
|
def context
|
|
53
188
|
context_config = config.dup
|
|
54
189
|
yield context_config if block_given?
|
|
55
190
|
Context.new(context_config)
|
|
56
191
|
end
|
|
57
192
|
|
|
193
|
+
# Runs ordinary Ruby code as a named, instrumented workflow. Every RubyLLM
|
|
194
|
+
# event emitted inside the block includes the workflow ID and name. Wrap
|
|
195
|
+
# meaningful regions with Workflow#step to add step correlation.
|
|
196
|
+
#
|
|
197
|
+
# RubyLLM.workflow("Write article", id: "article-42") do |workflow|
|
|
198
|
+
# notes = workflow.step("Research") { researcher.ask(topic).content }
|
|
199
|
+
# workflow.step("Draft") { writer.ask(notes).content }
|
|
200
|
+
# end
|
|
201
|
+
#
|
|
202
|
+
# If +id:+ is omitted, RubyLLM generates one. Pass +metadata:+ to attach
|
|
203
|
+
# application data to every nested event as +workflow_metadata+. Workflows
|
|
204
|
+
# may nest; an inner workflow keeps its own identity and records its
|
|
205
|
+
# parent as +workflow_parent_id+. The block's return value is returned
|
|
206
|
+
# unchanged.
|
|
207
|
+
def workflow(name, id: nil, metadata: nil, &)
|
|
208
|
+
Workflow.new(name, id:, metadata:, config: config).run(&)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Creates a Chat conversation. Arguments are forwarded to Chat.new:
|
|
212
|
+
# +model:+, +provider:+, +protocol:+, +assume_model_exists:+, and
|
|
213
|
+
# +context:+. With no arguments, uses the configured default model.
|
|
214
|
+
#
|
|
215
|
+
# chat = RubyLLM.chat
|
|
216
|
+
# chat.ask "What is the capital of France?"
|
|
217
|
+
#
|
|
218
|
+
# chat = RubyLLM.chat(model: 'claude-sonnet-5')
|
|
219
|
+
#
|
|
58
220
|
def chat(...)
|
|
59
221
|
Chat.new(...)
|
|
60
222
|
end
|
|
61
223
|
|
|
224
|
+
# Counts the tokens +text+ would consume as a single user message,
|
|
225
|
+
# without requesting a completion. Builds a minimal Chat and delegates
|
|
226
|
+
# to Chat#count_tokens. Returns an Integer.
|
|
227
|
+
#
|
|
228
|
+
# RubyLLM.count_tokens("What is the capital of France?", model: 'claude-haiku-4-5')
|
|
229
|
+
#
|
|
230
|
+
def count_tokens(text, model: nil, provider: nil)
|
|
231
|
+
chat(model: model, provider: provider).count_tokens(text)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Tokenizes plain text and returns a Tokenization with its token IDs
|
|
235
|
+
# and count. Excludes chat formatting and billable generation usage.
|
|
236
|
+
# See Tokenization.tokenize for options.
|
|
237
|
+
def tokenize(...)
|
|
238
|
+
Tokenization.tokenize(...)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Submits requests staged with Chat#ask_later or ::embed_later as a
|
|
242
|
+
# provider-side batch and returns a Batch. A batch takes chats or
|
|
243
|
+
# embedding requests, not both. Look up an existing batch with
|
|
244
|
+
# Batch.find.
|
|
245
|
+
#
|
|
246
|
+
# chats = documents.map do |doc|
|
|
247
|
+
# RubyLLM.chat(model: 'claude-haiku-4-5').ask_later(doc.text)
|
|
248
|
+
# end
|
|
249
|
+
# batch = RubyLLM.batch(chats)
|
|
250
|
+
#
|
|
251
|
+
def batch(chats)
|
|
252
|
+
Batch.submit(chats)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Generates a vector embedding for a text, or one embedding per element
|
|
256
|
+
# when given an array of strings. Returns an Embedding. Arguments are
|
|
257
|
+
# forwarded to Embedding.embed.
|
|
258
|
+
#
|
|
259
|
+
# embedding = RubyLLM.embed("Ruby is a programmer's best friend")
|
|
260
|
+
# embedding.vectors # => [0.018, -0.027, ...]
|
|
261
|
+
#
|
|
62
262
|
def embed(...)
|
|
63
263
|
Embedding.embed(...)
|
|
64
264
|
end
|
|
65
265
|
|
|
266
|
+
# Stages a text for embedding without contacting the provider, and
|
|
267
|
+
# returns an EmbeddingRequest. Submit an array of staged requests as a
|
|
268
|
+
# provider-side batch with ::batch; once the batch completes, each
|
|
269
|
+
# request's EmbeddingRequest#result holds its Embedding.
|
|
270
|
+
#
|
|
271
|
+
# requests = texts.map { |text| RubyLLM.embed_later(text) }
|
|
272
|
+
# batch = RubyLLM.batch(requests)
|
|
273
|
+
#
|
|
274
|
+
def embed_later(text, model: nil, provider: nil, dimensions: nil)
|
|
275
|
+
EmbeddingRequest.new(text, model:, provider:, dimensions:)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Checks text or image attachments against the provider's moderation model and returns a
|
|
279
|
+
# Moderation result. Arguments are forwarded to Moderation.moderate.
|
|
280
|
+
# An explicitly selected provider can use a configured moderation
|
|
281
|
+
# resource without a model.
|
|
282
|
+
#
|
|
283
|
+
# result = RubyLLM.moderate("Some user input text")
|
|
284
|
+
# result.flagged? # => false
|
|
285
|
+
#
|
|
66
286
|
def moderate(...)
|
|
67
287
|
Moderation.moderate(...)
|
|
68
288
|
end
|
|
69
289
|
|
|
290
|
+
# Runs a hosted research task and returns its report as a Message.
|
|
291
|
+
# Requires explicit +provider:+ and +agent:+. See ResearchJob.research.
|
|
292
|
+
def research(...)
|
|
293
|
+
ResearchJob.research(...)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Submits a hosted research task and returns a ResearchJob immediately.
|
|
297
|
+
# Requires explicit +provider:+ and +agent:+. See ResearchJob.research_later.
|
|
298
|
+
def research_later(...)
|
|
299
|
+
ResearchJob.research_later(...)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Generates or edits an image and returns an Image, or an array when
|
|
303
|
+
# the provider returns several images. Pass +with:+ for source images,
|
|
304
|
+
# +mask:+ for a mask, and +count:+ for multiple results. See Image.paint.
|
|
305
|
+
#
|
|
306
|
+
# image = RubyLLM.paint("a sunset over mountains in watercolor style")
|
|
307
|
+
# image.save("sunset.png")
|
|
308
|
+
#
|
|
70
309
|
def paint(...)
|
|
71
310
|
Image.paint(...)
|
|
72
311
|
end
|
|
73
312
|
|
|
313
|
+
# Generates a video from a text prompt, blocks until the provider
|
|
314
|
+
# finishes rendering it, and returns a Video. Arguments are forwarded
|
|
315
|
+
# to Video.animate.
|
|
316
|
+
#
|
|
317
|
+
# video = RubyLLM.animate("a paper boat sailing down a rainy gutter")
|
|
318
|
+
# video.save("boat.mp4")
|
|
319
|
+
#
|
|
320
|
+
def animate(...)
|
|
321
|
+
Video.animate(...)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Submits a video generation job and returns a VideoJob immediately,
|
|
325
|
+
# without waiting for the result. Arguments are forwarded to
|
|
326
|
+
# VideoJob.animate_later.
|
|
327
|
+
#
|
|
328
|
+
# job = RubyLLM.animate_later("a paper boat sailing down a gutter")
|
|
329
|
+
# job.wait
|
|
330
|
+
# job.video.save("boat.mp4")
|
|
331
|
+
#
|
|
332
|
+
def animate_later(...)
|
|
333
|
+
VideoJob.animate_later(...)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Synthesizes speech from text and returns a Speech. Given a block,
|
|
337
|
+
# yields SpeechChunk objects as audio arrives. Arguments are
|
|
338
|
+
# forwarded to Speech.speak.
|
|
339
|
+
#
|
|
340
|
+
# speech = RubyLLM.speak "Hello, welcome to RubyLLM!"
|
|
341
|
+
# speech.save("welcome.mp3")
|
|
342
|
+
#
|
|
343
|
+
def speak(...)
|
|
344
|
+
Speech.speak(...)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Transcribes an audio file and returns a Transcription. Arguments are
|
|
348
|
+
# forwarded to Transcription.transcribe. Given a block, the transcript
|
|
349
|
+
# streams as TranscriptionChunk objects.
|
|
350
|
+
#
|
|
351
|
+
# transcription = RubyLLM.transcribe("meeting.wav")
|
|
352
|
+
# transcription.text
|
|
353
|
+
#
|
|
354
|
+
# RubyLLM.transcribe("meeting.wav", model: "gpt-4o-transcribe") do |chunk|
|
|
355
|
+
# print chunk.delta
|
|
356
|
+
# end
|
|
357
|
+
#
|
|
74
358
|
def transcribe(...)
|
|
75
359
|
Transcription.transcribe(...)
|
|
76
360
|
end
|
|
77
361
|
|
|
362
|
+
# Extracts the text of a document or image and returns an OCR result.
|
|
363
|
+
# Arguments are forwarded to OCR.ocr.
|
|
364
|
+
#
|
|
365
|
+
# ocr = RubyLLM.ocr("contract.pdf")
|
|
366
|
+
# ocr.markdown
|
|
367
|
+
#
|
|
368
|
+
def ocr(...)
|
|
369
|
+
OCR.ocr(...)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Ranks documents by relevance to a query on providers with a rerank
|
|
373
|
+
# endpoint. Arguments are forwarded to Rerank.rerank.
|
|
374
|
+
#
|
|
375
|
+
# rerank = RubyLLM.rerank("what is ruby", docs,
|
|
376
|
+
# model: "voyageai/rerank-2.5-lite", provider: :openrouter)
|
|
377
|
+
# rerank.results.first.document
|
|
378
|
+
#
|
|
379
|
+
def rerank(...)
|
|
380
|
+
Rerank.rerank(...)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Uploads a file to a provider and returns an UploadedFile that can be
|
|
384
|
+
# reused across chats. Arguments are forwarded to UploadedFile.upload.
|
|
385
|
+
#
|
|
386
|
+
# file = RubyLLM.upload("document.pdf", provider: :anthropic)
|
|
387
|
+
# chat.ask "Summarize this document", with: file
|
|
388
|
+
#
|
|
389
|
+
def upload(...)
|
|
390
|
+
UploadedFile.upload(...)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Downloads a provider file and returns a DownloadedFile. Save it with
|
|
394
|
+
# DownloadedFile#save or read its bytes with DownloadedFile#to_blob.
|
|
395
|
+
# Arguments are forwarded to UploadedFile.download.
|
|
396
|
+
#
|
|
397
|
+
# RubyLLM.download(file.id, provider: :openai).save("report.pdf")
|
|
398
|
+
#
|
|
399
|
+
def download(...)
|
|
400
|
+
UploadedFile.download(...)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Creates a provider-side prompt cache and returns a CachedContent
|
|
404
|
+
# that chats can attach with Chat#with_caching. Arguments are
|
|
405
|
+
# forwarded to CachedContent.create.
|
|
406
|
+
#
|
|
407
|
+
# cache = RubyLLM.cache(big_document, model: 'gemini-3.7-flash', ttl: 3600)
|
|
408
|
+
# chat = RubyLLM.chat(model: 'gemini-3.7-flash').with_caching(id: cache)
|
|
409
|
+
#
|
|
410
|
+
def cache(...)
|
|
411
|
+
CachedContent.create(...)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Renders the ERB prompt template +name+ and returns the result as a
|
|
415
|
+
# String. The name resolves to a <tt>.txt.erb</tt> file under
|
|
416
|
+
# app/prompts. Keyword arguments become locals in the template.
|
|
417
|
+
#
|
|
418
|
+
# instructions = RubyLLM.render_prompt(
|
|
419
|
+
# "support/instructions",
|
|
420
|
+
# product_name: "BillingHub"
|
|
421
|
+
# )
|
|
422
|
+
# chat.with_instructions(instructions)
|
|
423
|
+
#
|
|
424
|
+
# Raises PromptNotFoundError if the template file does not exist.
|
|
425
|
+
def render_prompt(name, **locals)
|
|
426
|
+
Prompt.render(name, **locals)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Returns the Models registry, used to browse, find, and refresh model
|
|
430
|
+
# metadata.
|
|
431
|
+
#
|
|
432
|
+
# RubyLLM.models.find("claude-haiku-4-5")
|
|
433
|
+
# RubyLLM.models.refresh
|
|
434
|
+
#
|
|
78
435
|
def models
|
|
79
436
|
Models.instance
|
|
80
437
|
end
|
|
81
438
|
|
|
439
|
+
# Returns the registered provider classes.
|
|
440
|
+
#
|
|
441
|
+
# RubyLLM.providers.map(&:slug)
|
|
442
|
+
# # => ["anthropic", "azure", "bedrock", ...]
|
|
443
|
+
#
|
|
82
444
|
def providers
|
|
83
445
|
Provider.providers.values
|
|
84
446
|
end
|
|
85
447
|
|
|
448
|
+
# Yields the global configuration for block-style setup. Call this once
|
|
449
|
+
# at startup to set API keys and defaults.
|
|
450
|
+
#
|
|
451
|
+
# RubyLLM.configure do |config|
|
|
452
|
+
# config.openai_api_key = ENV['OPENAI_API_KEY']
|
|
453
|
+
# end
|
|
454
|
+
#
|
|
86
455
|
def configure
|
|
87
456
|
yield config
|
|
88
457
|
end
|
|
89
458
|
|
|
459
|
+
# Returns the global Configuration instance.
|
|
90
460
|
def config
|
|
91
461
|
@config ||= Configuration.new
|
|
92
462
|
end
|
|
93
463
|
|
|
94
|
-
def logger
|
|
464
|
+
def logger # :nodoc:
|
|
95
465
|
@logger ||= config.logger || Logger.new(
|
|
96
466
|
config.log_file,
|
|
97
467
|
progname: 'RubyLLM',
|
|
@@ -104,11 +474,15 @@ end
|
|
|
104
474
|
RubyLLM::Provider.register :anthropic, RubyLLM::Providers::Anthropic
|
|
105
475
|
RubyLLM::Provider.register :azure, RubyLLM::Providers::Azure
|
|
106
476
|
RubyLLM::Provider.register :bedrock, RubyLLM::Providers::Bedrock
|
|
477
|
+
RubyLLM::Provider.register :cohere, RubyLLM::Providers::Cohere
|
|
478
|
+
RubyLLM::Provider.register :deepgram, RubyLLM::Providers::Deepgram
|
|
107
479
|
RubyLLM::Provider.register :deepseek, RubyLLM::Providers::DeepSeek
|
|
480
|
+
RubyLLM::Provider.register :elevenlabs, RubyLLM::Providers::ElevenLabs
|
|
108
481
|
RubyLLM::Provider.register :gemini, RubyLLM::Providers::Gemini
|
|
109
482
|
RubyLLM::Provider.register :gpustack, RubyLLM::Providers::GPUStack
|
|
110
483
|
RubyLLM::Provider.register :mistral, RubyLLM::Providers::Mistral
|
|
111
484
|
RubyLLM::Provider.register :ollama, RubyLLM::Providers::Ollama
|
|
485
|
+
RubyLLM::Provider.register :ollama_cloud, RubyLLM::Providers::OllamaCloud
|
|
112
486
|
RubyLLM::Provider.register :openai, RubyLLM::Providers::OpenAI
|
|
113
487
|
RubyLLM::Provider.register :openrouter, RubyLLM::Providers::OpenRouter
|
|
114
488
|
RubyLLM::Provider.register :perplexity, RubyLLM::Providers::Perplexity
|
data/lib/tasks/ruby_llm.rake
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
namespace :ruby_llm do
|
|
4
|
+
namespace :upgrade do
|
|
5
|
+
%i[rollback resume finalize].each do |action|
|
|
6
|
+
desc "#{action.to_s.capitalize} a copy-mode RubyLLM upgrade with affected processes stopped"
|
|
7
|
+
task action => :environment do
|
|
8
|
+
require 'generators/ruby_llm/upgrade/upgrade_migration'
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
RubyLLM::Generators::UpgradeMigration.new.public_send(action)
|
|
11
|
+
puts "RubyLLM copy upgrade: #{action} completed"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
desc 'Load models from models.json into the database'
|
|
16
|
+
desc 'Load the selected model registry into the database'
|
|
13
17
|
task load_models: :environment do
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
# Rails 8.1 loads ActiveRecord::Base lazily, so outside a runner or console
|
|
19
|
+
# the :active_record load hook that defines RubyLLM::ActiveRecord::Model
|
|
20
|
+
# never fires. Requiring it here runs the hook; it is a no-op if it already did.
|
|
21
|
+
require 'active_record/base'
|
|
22
|
+
|
|
23
|
+
RubyLLM.models.load_from_json
|
|
24
|
+
model_class = RubyLLM::ActiveRecord::Model
|
|
25
|
+
model_class.save_to_database
|
|
26
|
+
puts "✅ Loaded #{model_class.count} models into database"
|
|
22
27
|
end
|
|
23
28
|
end
|